文件描述符与进程间通信之关联【转】

最近在想一个问题,关于进程间通信的问题,我们都知道进程间可以通过管道通信,但是为什么进程间可以通过管道通信呢?管道通信的机制又是什么呢?我想很多人应该没有去思考过,下面我来讲讲我对管道通信机制的理解。

如果两个不相关的进程打开同一个文件,一个只读方式开打,一个只写方式打开,不就相当于创建了一个管道了,进程A往里面写,进程B读,同样实现进程间通信。管道其实也一样。

一般的管道,只能在父子进程间进行通信,为什么?因为管道实现的进程间通信是在父进程fork()出子进程时,子进程会继承父进程的文件描述符表,而这个文件描述符表里记录了所有父进程打开的文件,所以子进程也继承了父进程打开的文件,所以父子进程可以通过同一个文件描述符去访问同一个文件(但是不相干的进程文件描述符相同也不代表同一个文件),而管道就是建立在文件描述符上的,建立一个管道必须和两个文件描述符相关,一个可以赋予可读权限,一个可以赋予可写权限,但是这两个文件描述符其实都和同一个文件关联,所以你通过不同的文件描述符去访问这个文件时,可以获得不同的访问权限,如果在父子进程中,在父进程将fd[0]为可读,fd[1]可写,而子进程fd[0]可读,fd[1]可写,那么这个管道就将父子进程连起来,如下图所示:

    

创建文件描述符的方法有:

进程获取文件描述符最常见的方法是通过本机子例程open或create获取或者通过从父进程继承。后一种方法允许子进程同样能够访问由父进程使用的文件。文件描述符对于每个进程一般是唯一的。当用fork子例程创建某个子进程时,该子进程会获得其父进程所有文件描述符的副本,这些文件描述符在执行fork时打开。在由fcntl、dup和dup2子例程复制或拷贝某个进程时,会发生同样的复制过程。

下面有两个衍生的问题:

问题1:实现两个进程都能向同一个socket中写数据, 也就是服务器端只accept一次就可以把两个进程写的数据全部接收到, 传输过来的数据的先后顺序不用管.

问题2: 我在一个程序里打开了一个文件。将文件描述符传给了另外一个程序。在另一个文件中能否对这个文件描述符进行操作。两个程序不是fork出来的。是完全2个独立的程序。

解决命名管道。

关于文件描述符的详述如下:

在Linux中,进程是通过文件描述符(file descriptors,简称fd)而不是文件名来访问文件的,文件描述符实际上是一个整数。Linux中规定每个进程能最多能同时使用NR_OPEN个文件描述符,这个值在fs.h中定义,为1024*1024(2.0版中仅定义为256)。

每个文件都有一个32位的数字来表示下一个读写的字节位置,这个数字叫做文件位置。每次打开一个文件,除非明确要求,否则文件位置都被置为0,即文件的开始处,此后的读或写操作都将从文件的开始处执行,但你可以通过执行系统调用LSEEK(随机存储)对这个文件位置进行修改。Linux中专门用了一个数据结构file来保存打开文件的文件位置,这个结构称为打开的文件描述(open file description)。这个数据结构的设置是煞费苦心的,因为它与进程的联系非常紧密,可以说这是VFS中一个比较难于理解的数据结构。

file结构中主要保存了文件位置,此外,还把指向该文件索引节点的指针也放在其中。file结构形成一个双链表,称为系统打开文件表,其最大长度是NR_FILE,在fs.h中定义为8192。

file结构在includelinuxfs.h中定义如下:

struct file 

{

 struct list_head        f_list;    /*所有打开的文件形成一个链表*/

 struct dentry           *f_dentry; /*指向相关目录项的指针*/

 struct vfsmount         *f_vfsmnt; /*指向VFS安装点的指针*/

 struct file_operations  *f_op;     /*指向文件操作表的指针*/

 mode_t f_mode;                                  /*文件的打开模式*/

 loff_t f_pos;                                   /*文件的当前位置*/

 unsigned short f_flags;                         /*打开文件时所指定的标志*/

 unsigned short f_count;                           /*使用该结构的进程数*/

 unsigned long f_reada, f_ramax, f_raend, f_ralen, f_rawin;

 /*预读标志、要预读的最多页面数、上次预读后的文件指针、预读的字节数以及预读的页面数*/

 int f_owner;                  /* 通过信号进行异步I/O数据的传送*/

 unsigned int         f_uid, f_gid;  /*用户的UID和GID*/

 int                 f_error;       /*网络写操作的错误码*/

 

 unsigned long f_version;           /*版本号*/

 void *private_data;                      /* tty驱动程序所需 */

};

内核中,对应于每个进程都有一个文件描述符表,表示这个进程打开的所有文件。文件描述表中每一项都是一个指针,指向一个用于描述打开的文件的数据块———file对象,file对象中描述了文件的打开模式,读写位置等重要信息,当进程打开一个文件时,内核就会创建一个新的file对象。需要注意的是,file对象不是专属于某个进程的,不同进程的文件描述符表中的指针可以指向相同的file对象,从而共享这个打开的文件。file对象有引用计数,记录了引用这个对象的文件描述符个数,只有当引用计数为0时,内核才销毁file对象,因此某个进程关闭文件,不影响与之共享同一个file对象的进程.

file对象中包含一个指针,指向dentry对象。dentry对象代表一个独立的文件路径,如果一个文件路径被打开多次,那么会建立多个file对象,但它们都指向同一个dentry对象。
 
dentry对象中又包含一个指向inode对象的指针。inode对象代表一个独立文件。因为存在硬链接与符号链接,因此不同的dentry对象可以指向相同的inode对象.inode 对象包含了最终对文件进行操作所需的所有信息,如文件系统类型、文件的操作方法、文件的权限、访问日期等。
 
打开文件后,进程得到的文件描述符实质上就是文件描述符表的下标,内核根据这个下标值去访问相应的文件对象,从而实现对文件的操作。


注意,同一个进程多次打开同一个文件时,内核会创建多个file对象。
当进程使用fork系统调用创建一个子进程后,子进程将继承父进程的文件描述符表,因此在父进程中打开的文件可以在子进程中用同一个描述符访问。
 
参考:
1、http://www.cnblogs.com/GODYCA/archive/2013/01/05/2845618.html
2、http://blog.csdn.net/liuzhanchen1987/article/details/7455208
3、http://blog.csdn.net/daniel_ustc/article/details/8642375
原文地址:https://www.cnblogs.com/duhuo/p/5787839.html