UNIX环境高级编程---文件描述符浅析
最近看到一本书叫《UNIX环境高级编程》,网上称这本书被誉为UNIX编程“圣经”,对所有不同层次UNIX/LINUX程序员是一本不可缺少的参考书。自己最近恰好看到GLIB中IOChannel这部分,关于linux的IO这块很不清楚,先研究完这本书再说。
文件描述符是一个小的非负整数,内核用以标识一个特定进程正在存访的文件。当内核打开一个现存文件或创建一个新文件时,它就返回一个文件描述符。当读、写文件时,就可使用它。说白了就是我们通常使用的句柄一个意思。
一、文件描述符----文件表----v节点结构三者的联系
既然文件描述符标识特定进程正在访问的文件,那进程跟文件是怎么联系起来的呢?
1 首先我们得知道每运行一个进程,shell就会默认为其打开三个文件描述符(0,1,2),分别与标准输入(stdin),标准输出(stdout)和标准错误(stderr)对应。
2 接下来讲下内核所使用的三种数据结构,正是这三种数据结构才使进程最终跟文件联系起来。建议边看图一边看下面的文字描述
2.1 1) 每个进程在进程表中都有一个记录项,每个记录项中有一张打开文件描述符表,可将其视为一个矢量,每个描述符占用一项。与每个文件描述符相关联的是:(a) 文件描述符标志。(b) 指向一个文件表项的指针
2.2 2)内核为所有打开文件维持一张文件表。每个文件表项包含:(a) 文件状态标志(读、写、增写、同步、非阻塞等)。(b) 当前文件位移量。(c) 指向该文件v节点表项的指针。
2.3 3)每个打开文件(或设备)都有一个v节点结构。是文件的重要信息部分。
具体
如图一,下图表示以上三个数据结构的关系:
图一
总之图一是一定要弄明白的,否则后面会更模糊。
二、文件描述符的分配
文件描述符的分配:当用户调用相关函数比如open, creat等函数时,如果成功,会返回一个文件描述符,返回的文件描述符一定是当前进程最小没有用到的描述符。但是要特别注意文件描述符的最大值,可以通过命令$ulimit –n查看文件描述符的最大值,一般linux发行版都是1024,当然这个值可以修改的,具体可以参考关于文件描述符。
三、基于文件描述符的输入输出函数
接下来讲讲基于文件描述符的输入输出函数:
open:打开一个文件,并指定访问该文件的方式,调用成功后返回一个文件描述符。
creat:打开一个文件,如果该文件不存在,则创建它,调用成功后返回一个文件描述符。
close:关闭文件,进程对文件所加的锁全都被释放。
read:从文件描述符对应的文件中读取数据,调用成功后返回读出的字节数。
write:向文件描述符对应的文件中写入数据,调用成功后返回写入的字节数。
ftruncate:把文件描述符对应的文件缩短到指定的长度,调用成功后返回0。
lseek:在文件描述符对应的文件里把文件指针设定到指定的位置,调用成功后返回新指针的位置。
fsync:将所有已写入文件中的数据真正写到磁盘或其他下层设备上,调用成功后返回0。
fstat:返回文件描述符对应的文件的相关信息,把结果保存在struct stat中,调用成功后返回0。
fchown:改变与打开文件相关联的所有者和所有组,调用成功后返回0。
fchmod:把文件描述符对应的文件的权限位改为指定的八进制模式,调用成功后返回0。
flock:用于向文件描述符对应的文件施加建议性锁,调用成功后返回0。
fcntl:既能施加建议性锁也能施加强制性锁,能建立记录锁、读取锁和写入锁,调用成功后返回0。
dup:复制文件描述符,返回没使用的文件描述符中最小的编号。
dup2:由用户指定返回的文件描述符的值,用来重新打开或重定向一个文件描述符。
select:同时从多个文件描述符读取数据或向多个文件描述符写入数据。
这里就重点讲解下dup, dup2, fcntl函数,
1. dup:
函数原型:int dup(int filefd)
函数功能:复制一份现存的文件描述符filefd
2. dup2:
函数原型:int dup2(int Oldfilefd, int Newfield)
函数功能:复制一份现存的文件描述Oldfilefd,若Newfield等于Oldfilefd时,返回Newfield;若两者不相等时,若Newfield已经打开,先关闭Newfield,然后返回Newfield。
3. fcntl:
函数原型:int fcntl (int filefd, int cmd, …)
函数功能:fcntl有5种功能,通过第二个参数cmd来决定。这里只讲一种:复制一份现存的文件描述符,cmd为F_DUPFD。
由上面三个函数的功能都是复制文件描述符,记住是文件描述符,不是复制文件表,更不是复制v节点表,其实这三个函数有微妙的联系,如下:
dup(filefd)
相当于
fcntl(filefd, F_DUPFD, 0)
dup2(Oldfilefd, Newfilefd)
相当于
close(Newfilefd)
fcntl(Oldfilefd, F_DUPFD, 0)
主要区别是dup2操作是原子操作,执行过程中不会被打断,关于原子操作可以参考我的博文:Atomic Operation【原子操作符】简介【原】;。
四,dup, dup2, fcntl对文件描述符----文件表----v节点结构影响
《UNIX环境高级编程》一书第三章习题要求画出文件描述符----文件表----v节点结构三者的关系图。
如下题:
在假设一个进程执行下面的3个函数调用:
fd1 = open(pathname, oflags);
fd2 = dup(fd1);
fd3 = open(pathname, oflags);
画出结果图(类似于图一)。
解答:每次调用open就分配一个文件表项,如果两次打开的是相同的文件,则两个文件表项指向相同的v节点结构,所以文件描述符fd1跟fd2所指向的文件表项都指向相同的v节点结构。dup是复制文件描述符,复制的文件描述符所指向的文件表跟被复制的文件描述符一样。所以结果图就是图一。
五,参考资料
★ 《UNIX环境高级编程》
★ 关于文件描述符
★ 解惑dup/dup2(原创)
转自:http://hi.baidu.com/lammy/blog/item/804c9a2bc9f9a3f0e7cd4038.html