厦门做模板网站的公司,江苏住房城乡建设部部官方网站,wordpress留言板comments.php添加自定义字段,h5手机网站制作本文主要介绍进程通信中的管道通信。
前面我们学习进程的过程中#xff0c;我们知道#xff0c;进程是具有独立性的。这也就导致了进程不能够直接地把数据进行传递。为了实现进程之间地通信#xff0c;我们就需要通过另外地方式来实现进程之间数据地传递。
1.进程通信的目…本文主要介绍进程通信中的管道通信。
前面我们学习进程的过程中我们知道进程是具有独立性的。这也就导致了进程不能够直接地把数据进行传递。为了实现进程之间地通信我们就需要通过另外地方式来实现进程之间数据地传递。
1.进程通信的目的
首先在正式学习进程间通信前我们需要了解进程间通信的目的
1数据传输:一个进程需要将它的数据发送给另一个进程 2资源共享:多个进程之间共享同样的资源。 3通知事件:一个进程需要向另一个或一组进程发送消息通知它(它们)发生了某种事件(如进程终止时要通知父进程)。 4进程控制:有些进程希望完全控制另一个进程的执行(如Debug进程)此时控制进程希望能够拦截另一个进程的所有陷入和异常并能够及时知道它的状态改变。
总的来说就是我们往往需要多个进程进行协作完成一些事情。
2.管道通信的相关原理
1.一般规律 假设我们有两个进程我们要实现这两个进程之间的数据通信肯定是不能把一个进程上的数据直接拷贝到另一个进程的空间上这样的作法无法保证进程之间的独立性。所以我们需要通过一块内存空间来实现两个进程之间的数据。同时这块空间还不能由通信双方任何一个提供如果是由其中一个提供那就允许另一个进程访问这会破坏进程的独立性。 所以进程间通信的本质就是让不同的进程看到同一块空间资源这块资源一般由OS提供。而OS提供的“空间”有不同的样式就决定了不同的通信方式。 2.实现方式 前面我们在介绍文件时我们了解到进程是通过文件描述符表来控制文件的。其中一个文件被不同的方式打开是要占据不同的文件描述符的而我们再创建一个子进程时文件描述符表也会跟着创建一份但是里面的内容是和父进程一致的。这两个进程都会指向同一个文件。 这里父子进程就指向了同一块空间并且这块空间是由操作系统提供说明我们可以通过文件的方式来实现进程之间的通信。这种通信的方式就叫做管道通信。
管道通信只能被设计成单向的通信也就是一个进程读另一个进程写。正常情况下我们要以读方式和写方式分别打开两次文件在不同的进程中关闭不同的文件描述符这样做是为了让父子进程都可以当作读端或写端。我们把父进程以读方式打开的文件描述符关闭把子进程以写方式打开的文件描述符关闭这样就可以实现父进程写子进程读。 相关接口
为了支持管道通信系统给我们提供相关的系统接口
1pipe int pipefd[2]是输出型参数用于存放两个fd分别是以读和写方式打开的文件描述符。通过该接口我们就不需要向磁盘中刷新和向磁盘中创建文件。通俗的说就是创建内存级的文件叫匿名文件(管道)。这个文件不用把数据加载到磁盘也不用实现标准输入、输出、错误等等。
匿名管道通信的特点就是只能让有血缘关系的进程进行进程间通信(常用于父子进程)。
这个接口如果返回零那么就表示调用成功如果失败了就返回-1。如果成功调用那么pipefd[0]中存放的是读端的文件描述符而pipefd[1] 中存放的是写端的文件描述符。
下面我们可以用一段代码验证上述的结论
#includestdio.h
#includeunistd.hint main()
{int pipefd[2];int n pipe(pipefd);if(n 0) return 1;printf(%d %d\n,pipefd[0],pipefd[1]);return 0;
}
运行结果 下面简单实现一下用父子进程间进行通信
#includestdio.h
#includeunistd.h
#includesys/types.h
#includesys/wait.h
#includestring.h
#includeunistd.h
#includestdlib.hvoid Write(int wfd)
{const char* str hello linux;char buffer[1024];int pid getpid();while(1){snprintf(buffer,sizeof(buffer),pid:%d str:%s\n,pid,str);write(wfd,buffer,sizeof(buffer));}}
void Read(int rfd)
{int cnt 20;char buffer[1024];while(cnt--){ssize_t n read(rfd, buffer,sizeof(buffer));(void)n;printf(%s, buffer);sleep(1);}
}int main()
{int pipefd[2];int n pipe(pipefd);if(n 0) return 1;printf(%d %d\n,pipefd[0],pipefd[1]);pid_t id fork();//写端if(id 0){close(pipefd[0]);Write(pipefd[1]);exit(0);}//父进程close(pipefd[1]);Read(pipefd[0]);wait(NULL);
} 运行结果 这样我们就实现父子进程的简单通信。
关于管道通信的几种情况。
1管道内部没有数据 子进程不关闭自己的写端文件fd读端(父进程)就要阻塞等待直到管道有数据。
2管道内部被写满了 读端(父进程)不关闭自己的fd写段(子进程)写满之后就要阻塞等待。管道的默认大小是4kBunbantu 20.04版本下。在这种情况下读端会尽可能多的读取数据当读取到一定数量的数据时写端又会重新向管道写入数据。
3对于写端而言不写了关闭了管道读端会将pipe中的数据读完最后就会读到返回值为0表示读结束类似与读到文件的结尾。
4读端不读关闭写端在写OS会直接终止写入的进程(子进程)通过信号13 SIGPIPE进程终止。(下图是让读端关闭(父进程)写端(子进程)继续写并打印出退出码和退出信号。 管道的几种的特性
1自带同步机制也就是执行时有一定的顺序。
2有血缘关系之间的通信
3管道是面向字节流的(读端和写端的次数没有直接的联系)
4父子进程退出管道自动的释放文件的声明周期是随进程的。
5管道只能单向通信。半双工的一种特殊情况
而我们学习的命令行管道本质上也就是本文所述的管道。而我们在使用命令行管道时一个命令就是一个进程一个竖划线就是一个管道这些进程的父进程都是bash进程。
应用场景进程池。
由于我们每次创建进程都要向系统中进行申请这个过程比较麻烦所以我们可以直接先申请多个进程由一个主进程进行控制每个进程都和父进程之间创建管道这个就叫进程池。在创建完毕后我们可以进程池内的进程分配任务一个进程不能执行全部的任务而是要让所有的进程都执行一些任务这个分配规则就叫负载均衡。
命名管道
匿名管道适用于父子进程之间的通信而我们如果要在完全不相干的两个进程之间进行通信就需要使用命名管道。 如上图所示当我们进程A和进程B以不同的方式打开file.txt时正常来说会生成两个文件缓冲区但是由于两个文件缓冲区内容是一样的所以我们就只需要一个文件缓冲区即可。由于此时的文件缓冲区是不需要向文件中刷新数据的(会浪费空间而且没必要)所以文件缓冲区就可以作为一个管道实现两个进程之间的通信。
如何保障两个打开的是同一个文件呢(也就是确保同一缓冲区)我们可以使用文件的路径文件名的方式锁定文件这样可以保证打开的就是同一文件。
具体方法
我们可以使用mkfifo命令创建管道然后在实现两个进程之间的通信。