这里我们需要先说明一下,我们这里所说的文件共享并不是一般意义上的
两个进程可以对同一个文件进行读写。而是共享同一个文件表。就是说
这个文件的当前偏移量。文件状态标志等信息是共享的。下面我们就具体介
绍这种共享。
我们还要说明一下unix内核中关于打开文件的内核数据结构。
中给出了们的关系模型,如图所示。(linux中没有使用v节点而是使用一个独立于文件系统的i节点和一个依赖于文件系统的i节点。独立的i节点和v节点指向系统特有的i节点。)
这里的关系图只是一个忽略细节的简单模型。
从上图我们看到:
1每个进程在进程表中都有一个记录项。
记录项中包含有一张打开的文件描述符表。该描述符表中记录了每个打开文
件的文件描述符标志(当前只定义了一个文件描述符标志fd_cloexec,它指
明exec时是否关闭该文件描述符,默认时关闭该标志的。)和指向一个文件表
的指针。
2内核为每个打开文件维持一个文件表。其中包含文件状态
标志(如open函数中介绍的o_append o_sync 等标志),当前文件偏移量和v节点指针。
3每个打开的文件都有一个v节点。其中包含了i节点信息,文件类型等。
现在我们先举个"不共享"(指文件表项)的例子。
如果两个进程都打开了一个相同的文件。那么内核中关于该文件的数据结
构如图所示
也就是说打开该文件的每个进程都有自己的文件表。那么每个进程也就拥
有他们关于该文件的自己的文件状态标志。比如说a进程此时调用read读取
三个字节,那么a进程所关联的文件表中的当前文件偏移更新为三,但是因
为b进程也拥有它自己的关于该文件的文件表,那么对b来说当前文件偏移量
仍旧是在文件头处。与a进程无关,他们互不影响。这也是造成进程间数据覆
盖的原因。因为a进程写了数据到文件中只影响了自己的文件表而不影响b进程
的文件表。那么b进程写数据到文件时就会从头开始写造成数据被覆盖。
那么什么时候两个文件描述符能够指向同一个文件表项呢。
这就是dup和dup2函数的作用的
#include
int dup(int filedes);
int dup2(int filedes, int filedes2);
这两个函数都可以用来复制一个现存的文件描述符
dup函数返回的新文件描述符一定是当前可用文件描述符中的最小值。
dup2函数可以用filedes2参数指定新描述符的数值。如果filedes2已经打开,则
先将其关闭。如果filedes2等于filedes.则dup2返回filedes2而不关闭它(其实就是没做什么事)
dup后的内核数据结构如下
下面我们来用 dup函数做个简单的测试
文件test的内容为“abcdefghij”
10 fd=open("test",O_RDONLY);
11 nreads=read(fd,buf,3);
12 buf[nreads]='\0';
13 printf("fd=%d\n",fd);
14 printf("%s\n",buf);
15
16 int new_fd;
17 new_fd=dup(fd);
18 nreads=read(new_fd,buf,3);
19 buf[nreads]='\0';
20 printf("new_fd=%d\n",new_fd);
21 printf("%s\n",buf);
程序输出如下
fd=3
abc
new_fd=4
def
从输出我们不难看出,现在 文件描述符 3 和 4 是共享同一个文件表的。
到这里不知道大家有没有考虑过一个问题,这里的文件表共享是在同一个进程中
不中的文件描述符共享同一个文件表。那么不同的进程间怎么共享呢
我自己做了一个测试,只是让a进程简单的输出他打开的文件描述符,然后读取
一一些数据并输出,之后a进程休眠一段时间,这段时间b进程根据a的输出来得
到文件描述符,并在内部调用dup()函数复制.但是dup函数会出错返回。
那么如何能让两个不同间的进程传递文件描述符呢。那就需要用到一种IPC机制
并调用ioctl(fd,I_SENDFD,fd_to_send) (fd为某种IPC产生的描述符或id)函数
进行传递。这两个主题我们在这里就不讨论了。
最后需要注意的一点是 dup()函数复制的新描述符的标志 (当前只定义了一个
FD_CLOEXEC(执行时关闭))会被清除。即新描述符调用exec系列函数时不会被关闭
阅读(3046) | 评论(0) | 转发(0) |