UNIX的read()/write()在向设备发送数据时的原子性

4
当在/dev设备上直接写入时,我会打开一个文件描述符并执行UNIX write(),然后再执行read()。如果多个线程同时进入write()函数,是否可以使用相同的文件描述符,以避免混乱的数据?
参考std文档将非常有帮助。我一直没有找到任何东西。有人提到这样的操作在内核中是原子性的,但我持怀疑态度。
此外,为了澄清,这是在/dev中的一个文件,因此,关于“文件指针”概念在这里适用的程度的任何见解都是有用的。

这个问题与你的相关。 - jxh
是的,我明白在常规文件上执行此操作会更改文件指针,可能会混淆事情。但这不是一个文件,而是位于/dev中的设备。 - wlformyd
3
如果由多个线程输入,设备驱动程序本身必须确保状态保持一致。这就是文件系统中“读/写”所做的事情。 - jxh
3个回答

1
文件指针(例如FILE *fp)是用户端代码中位于函数调用(如write())之上的一层。在线程环境中,对fp的访问由锁控制(不能同时有两个线程修改同一结构)。
在内核中,我期望会对文件描述符(和/或“打开文件描述符”)进行锁定,以防止其被两个线程同时使用。
您可以查阅POSIX规范read()getchar_unlocked(),了解更多关于锁定等方面的信息 - 至少对于符合POSIX标准的实现。
请注意,POSIX仍然使用C99。因此,它不认识C11线程设施。C11标准没有read()等(使用文件描述符的文件I/O),因此它对这些系统调用没有任何说明。它也没有提供getchar_unlocked()或任何相关函数。

1
注意:我已经有一段时间没有涉及内核了,但这是它过去的工作方式。
对于磁盘文件: 您能以追加模式打开文件,并编写块大小<= BLKSIZE吗?
在POSIX环境中,足够小的块大小保证原子写入(实际上,限制可能大于BLKSIZE...我懒得寻找替代符号)。
追加保证寻求到文件的末尾...对于支持寻求的设备。与原子写入结合使用,您应该很好。
每个缓冲区必须独立存在,假设后面可能会跟随某些“外来”信息。
对于ttys: 追加模式在这里没有意义。与之前一样,但是更加注意行结束很重要。这与读取非常不相关。ttys处理为控制序列的代码也可能会使您出错,即使启用序列的模式分割成块。
对于其他设备: 这里可能会变得棘手。这取决于设备。

1
我假设你指的是通用字符设备(例如tty),因为你没有具体说明。据我所知,每个fd类型操作(例如read()/write())直接映射到驱动程序的调用。
因此,驱动程序将作为整体接收每个write()的数据块,并且在完成之前不会看到下一个数据块(例如,数据排队等待传输)。
但是,如果驱动程序不能一次消耗整个数据块(即write()返回少于指定字节数),则不能保证线程能够在另一个线程执行不同的write()之前再次写入剩余部分。
此外,正如Johnathan Leffler所指出的那样,如果您使用具有进程级缓冲区的标准I / O,则所有赌注都关闭了。
最重要的是,如果您使用直接fd写入,则每次写入将直接映射到一个驱动程序函数调用。从那里开始,驱动程序决定write是否是原子性的。
编辑:wlformyd提出了多个处理器上多个线程之间的锁定问题。据我所知,在FD上没有锁定,实际上这将是无效的,因为可以使用多个FD来访问相同的设备。

我认为驱动程序本身需要进行锁定,以防止对内部队列和/或硬件的争用。在这种意义上,在多处理器系统上,内核不会阻止对驱动程序写例程的多个同时访问。但是,一个正确编写的驱动程序应该进行锁定,以防止两个写调用之间的输出混合。


那么,当sys_write调用驱动程序的write函数时,它会锁定文件描述符,因此操作的这一部分是原子性的。正确吗? - wlformyd

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接