Unix中file descriptor

很久都没有写博客了,但是每天9点回宿舍还是去看一点书的,最近就在看APUE(Unix高级环境编程)。

今天主要写一写Unix中的文件描述符,在Unix file IO中我们对文件读写等一系列操作,都是通过文件描述符来进行的,文件描述符通常是一个非负数int型,简称fd,不同于Unix标准IO库函数,Unix标准IO库函数是通过File Stream来操作,File Stream是一个缓冲区,为了就是避免通过缓冲技术来减少IO系统调用,因为每一次系统调用都是要陷入内核态,也称为管态,这状态的切换也伴随着栈的切换,当用户进程在进行系统调用的时候,会保存进程当前栈,切换到进程内核栈,进程内核栈也是一段内存区域,这个区域不是存在于内核内存区,而是在进程内存中单独划出来的进程内核栈,所以当进行系统调用时,也伴随着栈切换。所以减少系统调用就会提升程序运行效率。当然传统的IO操作也能通过BUF来一下写入一串内容,减少系统调用,File Steam的存在就是自动为我们分配缓冲区的大小(缓冲区大小用户也能自行设置,如果不设置,就会设置成默认值)。

说了这么多,就来说一说今天的主题,那就是Unix中的file descriptor,在说file descriptor之前,首先介绍process table entry(进程表项),file table entry(文件表项), V-node table entry(索引表项)。

#####这里写图片描述process table entry(进程表项)

存在于进程区域,每一个进程对应一个打开的文件描述符表,表中可以有很多表项,其中fd就是我们通过open函数打开一个特定路径返回的int型非负整数,表项有一个文件指针,这个指针指向文件表项。

file table entry(文件表项)

文件表项存在于操作系统内核区,表项的内容主要是文件状态标识,当前文件偏移量(这个很重要),V节点指针,指向V节点。

V-node table entry(索引节点)

V-node table entry 这是文件的索引指针,在操作系统中,当我们去通过open函数打开一个文件时,首先通过我们给出的文件路径在树形文件目录索引节点中查找对应的文件,索引节点包含了这个文件的一些信息,比如文件大小,这个文件物理块的位置(一般是物理索引块),文件全路径等等,但是现在文件系统中每一个物理块的大小是有限的,而一个文件所对应的信息实在太大,在检索时会降低速度,所以现在很多的操作系统在设计文件系统时会把文件索引节点中的相关信息分离开,在索引节点中保留文件路径和一个指向i节点指针,i节点存储着这个文件的其他信息比如文件的所有者、文件长度、文件所在设备、指向文件实际数据块在磁盘上所在位置的指针等,这样在文件检索时,会大大提高检索速度。

介绍完了进程文件表,内核文件表,V-node节点和i-node节点,下面就来说说常见的File IO操作函数,

open

这里不介绍函数的具体用法,open函数对应的是在树形文件目录中查找我们需要的文件,如果查找到就会在进程文件表中增加一个表项,同时内核文件表也会增加一个表项。

dup和dup2函数

重点讲一下这两个函数,因为稍微不注意这dup函数和dup2函数的用法,就会出现意想不到的错误,dup函数是duplicate的缩写,意思就是复制,那么这这个dup函数复制的是什么,当我们调用dup函数这个系统调用时,复制的只是进程文件表项中一条记录,返回的fd和原fd不一样,但是复制的进程文件表项中文件表项指针都是指向同一个内核文件表项,也就是说,两个不同的fd进程文件表项都指向同一个内核文件表项,当我们用其中一个fd区写入一段内容,写完文件偏移量会改变的,然后我们用dup函数返回的另一个fd进行文件写入,此时的偏移量已经修改过了,也就是说两个fd共享一个文件偏移量,dup2函数和dup函数差不多,是duplicate to的缩写,这个函数我们可以指定fd数值,当我们的指定的fd被占用时,先会关闭被占用的fd,然后返回我们指定的fd int数值。

close

close函数,我一开始以为是同时关闭进程文件表项和内核文件表项,然而我在写程序中发现当我close掉一个fd时,本来我以为我用另一个dup函数返回的fd进行读操作会报错,结果并没有报错,所以close函数只关闭了进程文件表项,或者在文件表项只有一个指向他的指针时,会关闭文件表项。

write

write函数可以只写入一个字符,也可以写入一段字符。