UNIX环境高级编程---文件描述符浅析

时间:2023-02-12 22:00:05

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