我们知道,进程间的通信既为ipc常见的进程间的方法有: 管道、命名管道、信号、内存映射、消息队列、信号量、共享内存、套接字,现在来简单的写一些自己在实际的编程过程中遇到的问题,管道相关函数:
#include <unistd.h> int fd[2] // 存储文件描述符 int pipe(fd); // 成功返回0 失败返回 -1
|
管道在多进程中通信以及数据共享时,非常的方便,但自己一直对管道的理解有一些偏差,首先,管道是一个特殊的文件,定义int fd[2],就是为了存储管道两端的文件描述符,可以把管道历程成一段临时区域,通过两端的文件相连,由于管道对应一段内存区域,那么它不肯能同时进行读写的操作,所以认为管道是单工的,一定要明确的是管道是一段特殊的内存区域,而这段内存区域是由内核来控制的,如果要详细的理解管道的实现细节,就需要去阅读内核代码了,这里只是写一些感性认识,多是一些实践的来的经验了。
在编程实践中发现,多进程中,管道的使用不一定要关闭一些读写端口,比如开启管道后,子进程送数据给父进程,而在父进程中就不是必须把写即fd[2]关闭,同样在子进程就不是必须把读fd[0]关闭,管道就比如一个杯子插入多个吸管,而这些吸管在同一时刻只有一个可以工作,只要保证在读或者写不要重复就可以了。
管道另外一个特点是在子进程消亡后,管道中的信息不会消失,但前提是这个管道是由父进程创建并初始化的,这样的话,即便是在子进程中写入信息到管道,而父进程又创建了自己新的子进程,在原子进程消亡后,新的子进程还可以读取到原来子进程发送在管道中的数据,有一些细节的东西是需要注意的,在读管道的信息时,如果管道中没有数据,那么这时就会发生阻塞现象,直到管道中有新的数据写入,特别需要注意的是管道的中断,如果管道的读取端即fd[0]关闭,而这时还继续想管道中写入数据,那么就会发生管道中断,默认的系统操作就是进程结束。
感悟: linux中的文件概念非常的广泛,不仅是管道,很多的东西都可以理解为文件,而它实际的实现也是通过文件来是实现的。这就是linux的伟大。一个文件走遍天下
一起看看pipe的真实面貌:
int pipe(int pfd[2]) { int r; struct Fd *fd0, *fd1; //文件描述符相关数据结构 void *va;
// allocate the file descriptor table entries
if ((r = fd_alloc(&fd0)) < 0 //内核操作函数,不知道干什么的 || (r = sys_page_alloc(0, fd0, PTE_P|PTE_W|PTE_U|PTE_SHARE)) < 0) //估计是开辟空间 goto err; // 以后读内核代码了解细节
if ((r = fd_alloc(&fd1)) < 0 || (r = sys_page_alloc(0, fd1, PTE_P|PTE_W|PTE_U|PTE_SHARE)) < 0) goto err1; //c中忌讳的跳转在这里用的很多,高效的跳转
// allocate the pipe structure as first data page in both
va = fd2data(fd0); if ((r = sys_page_alloc(0, va, PTE_P|PTE_W|PTE_U|PTE_SHARE)) < 0) goto err2; if ((r = sys_page_map(0, va, 0, fd2data(fd1), PTE_P|PTE_W|PTE_U|PTE_SHARE)) < 0) goto err3;
// set up fd structures
fd0->fd_dev_id = devpipe.dev_id; fd0->fd_omode = O_RDONLY; //知道为什么fd[0]是为读而开
fd1->fd_dev_id = devpipe.dev_id; fd1->fd_omode = O_WRONLY; //fd[1]为写开的
if (debug) cprintf("[%08x] pipecreate %08x\n", env->env_id, vpt[VPN(va)]);
pfd[0] = fd2num(fd0); pfd[1] = fd2num(fd1); return 0;
err3: sys_page_unmap(0, va); err2: sys_page_unmap(0, fd1); err1: sys_page_unmap(0, fd0); err: return r; }
|
阅读(1364) | 评论(0) | 转发(0) |