博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
进程间通信-创建管道实现机制
阅读量:4212 次
发布时间:2019-05-26

本文共 5127 字,大约阅读时间需要 17 分钟。

1.图解

对于每个管道来说,内核都要创建一个索引节点对象和两个文件对象,一个文件对象用于读,一个文件对象用于写。当进程希望从管道中读取数据或向管道中写入数据时,必须使用适当的文件描述符。在linux 中,管道的实现并没有使用专门的数据结构,而是借助了文件系统的file结构和VFS的索引节点inode。通过将两个 file 结构指向同一个临时的 VFS 索引节点,而这个 VFS 索引节点又指向一个物理页面而实现。

2.pipe_inode_info结构

当索引节点指的是管道时,其i_pipe字段指向一个pipe_inode_info结构

/** *	struct pipe_inode_info - a linux kernel pipe *	@wait: reader/writer wait point in case of empty/full pipe *	@nrbufs: the number of non-empty pipe buffers in this pipe *	@curbuf: the current pipe buffer entry *	@tmp_page: cached released page *	@readers: number of current readers of this pipe *	@writers: number of current writers of this pipe *	@waiting_writers: number of writers blocked waiting for room *	@r_counter: reader counter *	@w_counter: writer counter *	@fasync_readers: reader side fasync *	@fasync_writers: writer side fasync *	@inode: inode this pipe is attached to *	@bufs: the circular array of pipe buffers **/struct pipe_inode_info {	wait_queue_head_t wait;        //管道/FIFO等待队列       unsigned int nrbufs, curbuf;  //待读数据的缓冲区数   待读数据的第一个缓冲区索引	struct page *tmp_page;  	unsigned int readers;	unsigned int writers;  	unsigned int waiting_writers;  //在等待队列中睡眠的写进程的个数	unsigned int r_counter;	unsigned int w_counter;	struct fasync_struct *fasync_readers;	struct fasync_struct *fasync_writers;	struct inode *inode;	struct pipe_buffer bufs[PIPE_BUFFERS];    //管道缓冲区数据};
利用inode里面的i_
pipe指向的pipe_inode_info结构来表示管道的特殊信息
缓冲区,等待队列,当前待读数据信息

每个管道都有自己的管道缓冲区(pipe buffer),其中包含了已写人管道等待读出的数据。现在每个管道可以使用多个管道缓冲区(2.6.11以后有16个)。这个改变大大增强了向管道写大量数据的用户态应用的性能。

16个缓冲区可以被看做一个整体环形缓冲区:写进程不断向这个大缓冲区追加数据,而读进程则不断移出数据。为提高效率,仍然要读的数据可以分散在几个未填充满的管道缓冲区内:事实上,在上一个管道缓冲区没有足够空间存放新数据时,每个写操作都可能把数据拷贝到一个新的空管道缓冲区。

故,内核必须记录:

  • 下一个待读字节所在的管道缓冲区、页框中的对应偏移量。该管道缓冲区的索引存放在pipe_inode_info数据接口的curbuf字段,而偏移量在相应pipe_buffer中的offset字段中
  • 第一个空管道缓冲区。

为了避免对管道数据结构的竞争条件,内核使用包含在索引节点对象的i_sem信号量

3.创建管道源码详解

pipe()系统调用由sys_pipe()函数处理,后者又会调用do_pipe()函数。为了创建新的管道,do_pipe主要执行以下操作:

为内核管道创建两个file结构体对象并分配两个空的文件描述符fd指向file结构体对象,两个file结构体对象中的f_inode指向inode索引节点

(在linux2.6.34源码中,最终调用的是do_pipe_flags

int do_pipe_flags(int *fd, int flags){	struct file *fw, *fr;  	int error;	int fdw, fdr;	if (flags & ~(O_CLOEXEC | O_NONBLOCK))		return -EINVAL;	fw = create_write_pipe(flags);	if (IS_ERR(fw))		return PTR_ERR(fw);	fr = create_read_pipe(fw, flags);	error = PTR_ERR(fr);	if (IS_ERR(fr))		goto err_write_pipe;	error = get_unused_fd_flags(flags);	if (error < 0)		goto err_read_pipe;	fdr = error;	error = get_unused_fd_flags(flags);	if (error < 0)		goto err_fdr;	fdw = error;	audit_fd_pair(fdr, fdw);	fd_install(fdr, fr);	fd_install(fdw, fw);	fd[0] = fdr;	fd[1] = fdw;	return 0; err_fdr:	put_unused_fd(fdr); err_read_pipe:	path_put(&fr->f_path);	put_filp(fr); err_write_pipe:	free_write_pipe(fw);	return error;}
struct file *create_write_pipe(int flags){	int err;	struct inode *inode;	struct file *f;	struct path path;	struct qstr name = { .name = "" };	err = -ENFILE;	inode = get_pipe_inode();	if (!inode)		goto err;	err = -ENOMEM;	path.dentry = d_alloc(pipe_mnt->mnt_sb->s_root, &name);	if (!path.dentry)		goto err_inode;	path.mnt = mntget(pipe_mnt);	path.dentry->d_op = &pipefs_dentry_operations;	d_instantiate(path.dentry, inode);	err = -ENFILE;	f = alloc_file(&path, FMODE_WRITE, &write_pipefifo_fops);	if (!f)		goto err_dentry;	f->f_mapping = inode->i_mapping;	f->f_flags = O_WRONLY | (flags & O_NONBLOCK);	f->f_version = 0;	return f; err_dentry:	free_pipe_info(inode);	path_put(&path);	return ERR_PTR(err); err_inode:	free_pipe_info(inode);	iput(inode); err:	return ERR_PTR(err);}
struct file *create_read_pipe(struct file *wrf, int flags){	/* Grab pipe from the writer */	struct file *f = alloc_file(&wrf->f_path, FMODE_READ,				    &read_pipefifo_fops);	if (!f)		return ERR_PTR(-ENFILE);	path_get(&wrf->f_path);	f->f_flags = O_RDONLY | (flags & O_NONBLOCK);	return f;}

1.do_pipe_flags调用create_write_pipecreate_read_pipe

2.create_write_pipe中,调用get_pipe_inode()。该函数为pipefs文件系统中的管道分配一个索引节点对象 、pipe_inode_info结构等并对其进行初始化。具体来说,该函数执行以下操作:

  (1)在pipefs文件系统中分配一个新的索引节点

  (2)分配pipe_inode_info数据结构,并把它的地址存放在索引节点的i_pipe字段

  (3)设置pipe_inode_info的curbuf和nrbufs字段为0,并把bufs数组中的管道缓冲区对象的所有字段都清为0

  (4)把r_counter、w_counter  、readers、writers字段初始化为1

3.为管道的写通道分配一个文件对象(struct file结构体),将文件操作f_op字段赋值为write_pipefifo_fops,设置文件对象标识f_flags字段为O_WRONLY,O_NONBLOCK,返回文件对象指针

4.调用的create_read_pipe中,为管道的读通道分配一个读文件对象(struct file结构体),将文件对象中的f_op字段设置为read_pipefifo_fops,设置文件对象标识f_flags字段为O_RDONLY,O_NONBLOCK,返回文件对象指针

5.从进程的打开文件描述符表中取出两个未使用的文件描述符,将管道读写端文件对象(struct file)与文件描述符关联起来
6.返回管道读写端文件描述符(赋值fd[0]和fd[1])

相关的VFS对象被组织为pipefs特殊文件系统以加速它们的处理。

pipe()创建管道,在pipefs中分配新的索引结点并初始化,初始化pipe_inode_info结构。分配一个只读文件对象和一个只写文件对象,    

它们的f_op字段分别初始化成read_pipe_fops和write_pipe_fops表的地址。分配一个目录项对象把两个文件对象和索引结点对象连接在一起。最后返回两个文件描述符。
read()从管道中读数据,调用的是read_pipe_fops表中的pipe_read(),获取索引结点i_sem信号量,检测管道写端是否关闭(判断pipe_inode_info中的writers是否为0),如果关闭则返回0。【
否则检测pipe_inode_info结构nrbufs字段表示的管道大小是否为0。检测O_NONBLOCK标志,是直接返回,否的话阻塞直到有数据可读。映射缓冲区页框读取数据到用户缓冲区中,
唤醒阻塞中的写进程。最后是一些释放i_sem信号量的工作。返回读取的字节数或错误信息。
write()类似read(),没有读进程时(判断pipe_inode_info中的writers是否为0),发送SIGPIPE信号并返回-EPIPE。同样是检查阻塞标志,等待缓冲区可写入全部数据,
唤醒读进程,最后是一些释放i_sem信号量的工作。返回写入的字节数或错误信息。(为什么规定多个进程并发写入一个管道时任何少于4096个字节的写操作必须是原子的?)

你可能感兴趣的文章
深入理解 OUI(Oracle Universal Installer)
查看>>
ORA-00600:[32695], [hash aggregation can't be done] 解决方法
查看>>
Oracle SQL中使用正则表达式 执行报ORA-07445 [_intel_fast_memcpy.A()+10] 错误
查看>>
Oracle TABLE ACCESS BY INDEX ROWID 说明
查看>>
ORA-00600 [kmgs_parameter_update_timeout_1], [27072] ORA-27072 解决方法
查看>>
Oracle 11g alert log 新增消息 opiodr aborting process unknown ospid (1951) as a result of ORA-28 说明
查看>>
Linux Context , Interrupts 和 Context Switching 说明
查看>>
《Oracle数据库问题解决方案和故障排除手册》终于发售了
查看>>
Oracle alert log ALTER SYSTEM SET service_names='','SYS$SYS.KUPC$C_...' SCOPE=MEMORY SID='' 说明
查看>>
Oracle latch:library cache 导致 数据库挂起 故障
查看>>
Openfiler 配置 NFS 示例
查看>>
Oracle 11.2.0.1 RAC GRID 无法启动 : Oracle High Availability Services startup failed
查看>>
Oracle 18c 单实例安装手册 详细截图版
查看>>
Oracle Linux 6.1 + Oracle 11.2.0.1 RAC + RAW 安装文档
查看>>
Oracle 11g 新特性 -- Online Patching (Hot Patching 热补丁)说明
查看>>
Oracle 11g 新特性 -- ASM 增强 说明
查看>>
Oracle 11g 新特性 -- Database Replay (重演) 说明
查看>>
Oracle 11g 新特性 -- 自动诊断资料档案库(ADR) 说明
查看>>
Oracle 11g 新特性 -- RMAN Data Recovery Advisor(DRA) 说明
查看>>
CSDN博客之星 投票说明
查看>>