一 Linux进程间通信( 二 )


父子进程通过匿名管道通信原理:匿名管道是提供给有亲缘关系两个进程进行通信的 。所以我们可以在创建管道之后通过fork函数创建子进程 , 这样父子进程就看到同一份资源,且父子进程都有这个管道的读写文件描述符 。我们可以关闭父进程的读端,关闭子进程的写端,这样子进程往管道里面写数据,父进程往管道里面读数据,这样两个进程就可以实现通信了 。原理解读:
fork函数调用成功后,将为子进程申请PCB和用户内存空间,子进程是父进程的副本,在用户空间将复制父进程用户空间所有的数据(代码段、数据段、BBS、栈、堆,实际上是复制的父进程的虚拟空间的地址),子进程从父进程继承下列属性:有效用户、组号、进程组号、环境变量、信号处理方式设置、信号屏蔽集合、当前工作目录、根目录、文件模式掩码、文件大小限制和打开的文件描述符(特别注意:共享同一文件表项) 。
共享同一文件表项就造成了一种现象,父子进程无论谁对文件进行操作 , 那么另外一个进程的文件表也会受到相同的影响 。

一 Linux进程间通信

文章插图
从图中可以看出,虽然在子进程的表项中式复制了关于打开文件的信息,但是他们是共享文件表的,所以如果一个进程对文件指针进行移动 , 那么肯定会影响到另外的进程 。
思考:这是不是和写时拷贝相违背了,为什么文件表就能共享了呢?
要知道在linux源码中,每个进程都存在一个PCB结构体,每个PCB中,存放了一个结构体指针指向一个我们理解为文件描述符的结构体struct file,而这个结构体里,才存了文件的id,值得注意的是,这个结构体里有一个指针才是指向真正文件的 。文件系统存在于磁盘当中,对磁盘的操作操作系统不会拷贝一份文件给子进程,相反,像那些临时创建存放于堆区和栈区的数据,操作系统会采用写时拷贝,进行复制 。
一 Linux进程间通信

文章插图
总结:父子进程共享文件表,对文件表进行的任何操作都会对父子进程造成相同的影响 , 与写时拷贝进行区分 。
父子进程通过创建匿名管道通信具体过程如下:
1.父进程创建管道(管道创建要在进程创建之前)
一 Linux进程间通信

文章插图
2.fork创建子进程(子进程继承父进程的管道文件描述符)
一 Linux进程间通信

文章插图
3.关闭父进程的写段,子进程的读端
一 Linux进程间通信

文章插图
实例演示: 子进程每隔1秒往管道里面写数据,父进程每隔1秒往管道里读数据
#include <stdio.h>#include <unistd.h>#include <stdlib.h>#include <string.h>#include <sys/types.h>int main(){int pipefd[2];int ret = pipe(pipefd);if (ret == -1){// 管道创建失败perror("make piep");exit(-1);}pid_t id = fork();if (id < 0){perror("fork failed");exit(-1);}else if (id == 0){// child// 关闭读端close(pipefd[0]);const char* msg = "I am child...!\n";//int count = 0;// 写数据while (1){ssize_t s = write(pipefd[1], msg, strlen(msg));printf("child is sending message...\n");sleep(1);}}else{// parentclose(pipefd[1]);char buf[64];while (1){ssize_t s = read(pipefd[0], buf, sizeof(buf)/sizeof(buf[0])-1);if (s > 0){buf[s] = '\0';// 字符串后放一个'\0'printf("father get message:%s", buf);}else if (s == 0){// 读到文件结尾写端关闭文件描述符 读端会读到文件结尾printf("father read end of file...\n ");}sleep(1);}}return 0;}运行结果如下:
一 Linux进程间通信

文章插图
匿名管道读写规则
一 Linux进程间通信

文章插图
读写规则总结: