Understanding Unix/Linux Programming-设备文件与磁盘连接的概念
我只是书本的搬运工,具体见《Unix/Linux编程实践教程》
Linux/Unix中,文件包含数据,具有属性,通过目录中的名字被标识,可以从文件中读写数据,而这种方法也被广泛应用于设备。
对于Unix而言,声卡、终端、鼠标和磁盘文件是同一种对象,每个设备都被当作是文件,都具有文件名、inode节点号,文件所有者、权限位集合以及最近修改时间等等。和磁盘文件相关的系统调用同样可以为其他设备服务。实际上,Unix没有其他的方法和设备通信。
不同Unix机器的"/dev"目录下的文件一般为设备文件。当移动鼠标并且按键时,鼠标将数据发送到系统,使得进程能够读取数据。向设备写入数据意味着什么呢?发送数据到鼠标,不会使鼠标移动,也不会使鼠标的键被按下。/dev/mouse(该文件未必存在,本人使用无线键鼠,在/dev目录下就没有mouse文件)文件不支持所有的write系统调用。当然,可以制造带发动机的鼠标,然后编写更高级的鼠标驱动程序,使系统能够接受并产生鼠标事件。
终端支持read和write,但是不支持lseek,考虑一下这是为什么呢?是否因为文件的读写指针是固定的?
- 设备文件和文件大小
常用的磁盘文件由字节组成,磁盘文件中的字节数就是文件的大小。设备文件是链接,而不是容器,设备文件的inode节点存储的是指向内核子程序的指针,而不是文件的大小和存储列表。内核中传输设备数据的子程序就是设备驱动程序。在“cp /dev/motd /dev/pts/0”这个例子中,从终端进行数据传输的代码是在设备-进程表中的编号为136的子程序。该子程序接受一个整型参数。在/dev/pts/0中,参数是0。136和0这两个数被称为设备的主设备号和从设备号。主设备号确定处理该设备的子程序,而从设备号被作为参数传输到该子程序。(书中例子与我自己实践的输出不同,毕竟实验物理机不一样)。 - 设备文件和权限位
每个文件都有相应的读、写和执行权限。当文件表示设备时,权限位表示什么?写入数据表示把数据发送到设备,因此权限写意味着向设备发送数据。因此读也就很好理解了,从文件获得数据。
在即时消息和聊天室出现之前,Unix用户通过使用命令“write”和其他终端上的用户聊天。具体的实现看书中write0.c的代码,可以看到,程序中将终端文件当做普通文件来处理。
设备文件和inode节点:
Unix文件系统的inode节点和数据块是如何支持设备文件这个概念的呢?目录是文件名和inode节点号的列表。目录并不能区分这些文件代表磁盘文件还是设备文件。文件类型的区别体现在inode节点号上。每个inode节点号指向inode节点表的中的一个结构体。inode节点可以是磁盘文件的,也可以是设备文件的。inode节点的类型被记录在结构体stat的成员变量st_mode的类型区域中。
磁盘文件的inode节点包含指向数据块的指针;而设备文件的inode节点指向内核子程序表的指针。主设备号用于告知从设备读取数据的那部分可执行代码的位置。
考虑一下read是如何工作的。内核首先找到文件描述符的inode节点(fd:file descriptor),该节点告诉内核文件的类型(st_mode)。如果是磁盘文件,那么内核通过访问块分配表来读取数据。而如果文件是设备文件,那么内核通过调用该设备的驱动程序的read部分来读取数据。其他操作,如close、write等等也是类似的哦。
设备与文件的不同之处
与磁盘文件的连接一般包含内核缓冲区。从进程到磁盘的字节先被缓冲,然后才从内核缓冲区被发送出去。与终端连接不同,进程需要尽快把终端的数据传送出去。
与终端或者调制解调器的连接也具有属性。这种连接拥有波特率、奇偶位、暂停位的个数。一般情况下,终端所输入的字符都会回显在屏幕上,但是有些时候,比如输入密码的时候,字符并不会回显。回显字符既不是键盘任务的一部分,也不是程序应该做的。回显是连接的属性,到磁盘问价的连接没有这种属性。
那么问题来了,什么是连接?
磁盘连接的属性
- 缓冲
文件描述符在形式上是一个非负整数。实际上,它是一个索引值,指向内核为每一个进程所维护的该进程打开文件的记录表。当程序打开一个现有文件或者创建一个新文件时,内核向进程返回一个文件描述符。我在看书的时候查了一下百度百科,这和缓冲没有关系,但是有助于了解整个工作过程啦。
书中的例子将了关闭文件访问缓冲属性的方法,说明连接并不是一个进程,而是类似于一个数据结构的东西,也许构成了所谓的文件记录表? - 自动添加模式
自动添加模式的用处之一:避免竞争。
考虑日志文件wtmp,如果同一时间,有A和B两人登录系统,需要在日志文件中写入登入记录。写入的过程为:- lseek(fd , 0 , SEEK_END)
- write(fd , &rec , len )
Unix是一个时间共享系统,会产生如下结果:
- B的登录进程定位到文件末尾
- B的时间片用完,A的登录进程定位到文件末尾
- A的时间片用完,B的写入进程写入文件
- B的时间片用完,A的写入进程写入文件
这样,B的写入记录就被A的覆盖啦,这种情况就被称为竞争。
如何避免竞争呢?在这个特定的情况下,内核提供一个简单的方法,自动添加模式,当文件描述符的O_APPEND位开启后,每个对write的调用都会自动调用lseek将内容添加到文件末尾。
竞争与原子操作(atomoc operation)密切相关。lseek和write是独立的系统调用,内核随时可以打断进程,从而使后面这两个操作被中断。而当O_APPEND被置位后,内核将lseek和write组合成一个原子操作,不可分割。
小结
内核在磁盘和进程之间传输数据时,内核中进行这些传输工作的可执行代码有很多选项,程序可使用open和fcntl系统调用来控制这些数据传输的内部运作。