即使write()
是完全线程安全的(禁止执行错误...),理论上这并不安全。 (强调我的): 。
The write()
function shall attempt to writenbyte
bytes from the buffer pointed to by buf
to the file associated with the open file descriptor, fildes
.
...
RETURN VALUE
Upon successful completion, these functions shall return the number of bytes actually written ...
谁也不能保证,你不会得到部分write()
,所以即使每个人write()
调用是原子,它不一定完整,所以你仍然可以得到交错的数据,因为它可能需要更多比拨打write()
要完全写入所有数据。在实践中,如果你只做相对较小的调用write()
,你可能永远不会看到局部的write()
,“小”和“可能”是取决于你的实现的不确定值。
我经常发表使用与O_APPEND
打开普通文件解锁单write()
调用,以提高记录的性能的代码 - 建立一个日志条目,然后write()
一次调用整个条目。我已经从来没有在Linux和Solaris系统上做了几乎几十年的部分或交错的write()
结果,即使许多进程写入相同的日志文件。但是再次,它是一个文本日志文件,如果发生部分或交错的write()
,则不会造成真正的损坏甚至数据丢失。
但是,在这种情况下,您正在将少量字节“写入”内核结构。您可以在Illumos.org上查看Solaris /dev/poll
内核驱动程序源代码,并查看部分write()
的可能性。我怀疑这实际上是不可能的 - 因为我回去看了十年前为我公司的软件库编写的多平台调查班。在Solaris上,它使用/dev/poll
和解锁来自多个线程的write()
调用。它已经工作正常了十年......
的Solaris的/ dev /泳池设备驱动程序源代码分析
的(开放)的Solaris源代码可以在这里找到:http://src.illumos.org/source/xref/illumos-gate/usr/src/uts/common/io/devpoll.c#628
的dpwrite()
函数是实际执行“写入”操作的/dev/poll
驱动程序中的代码。我使用引号是因为它根本不是一个写操作 - 数据不会像内核中的数据那样传输,而是代表被轮询的文件描述符集被更新。
将数据从用户空间复制到内核空间 - 到使用kmem_alloc()
获取的内存缓冲区。我没有看到任何可能的方式,可以是部分副本。分配成功或者不成功。在执行任何操作之前,代码可能会被中断,因为它等待对内核结构的独占访问权限write()
。
之后,最后返回调用是在结束 - 如果没有错误,则整个呼叫被标记为成功或任何错误的整个调用失败:
995 if (error == 0) {
996 /*
997 * The state of uio_resid is updated only after the pollcache
998 * is successfully modified.
999 */
1000 uioskip(uiop, copysize);
1001 }
1002 return (error);
1003}
如果你挖通的Solaris内核代码,你会看到uio_resid
是成功调用后返回值为write()
的结果。
因此,这个呼叫肯定看起来是全有或全无。尽管在传入多个描述符成功处理较早的描述符后,代码似乎可以在文件描述符上返回错误,但代码似乎没有返回任何部分成功指示。
如果您一次只处理一个文件描述符,我会说write()操作是完全线程安全的,并且它几乎可以确保线程安全,可以将多个文件描述符“写入”更新为驾驶员没有明显的方式返回部分write()
的结果。
我很肯定你需要做你自己的同步。对于使用多线程应用程序的I/O,我的经验是I/O不是线程安全的。 http://stackoverflow.com/questions/19974548/are-functions-in-the-c-standard-library-thread-safe有关于线程安全和C标准库的一些讨论,尽管它没有讨论'write()'特别是。然后有这篇文章,write(),线程安全和POSIX https://lwn.net/Articles/180387/ –
'write()'是线程安全的。但是,是否将从多个线程到同一个文件描述符的'write()'并发原子化是另一个问题。假设写入文件不是太大,用'pwrite()'来分隔文件的大部分部分,'writev()'是可以的。 – EOF
'write()'是POSIX的函数列表,如果“两个线程分别调用其中一个函数,每个调用将看到另一个调用的所有指定的效果,或者它们都不是。 http://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html#tag_15_09_07 –