【study】linux 进程通信之管道
2017-08-30 09:55
134 查看
1.管道的本质:固定大小的内核缓冲区。
2.管道是半双工的,数据只能一个方向流动。
3.只有调用fork 父子进程才能管道通信。
4.管道的读写规则
一.如果所有管道读端对应的文件描述符被关闭,则write操作会产生信号SIGPIPE.
二.如果所有管道写端对应的文件描述符被关闭,则read返回0.
三.当管道不停的被写,写满的时候
O_NONBLOCK disable: write调用阻塞
O_NONBLOCK enable:调用返回-1,errno值为EAGAIN
以测试管道容量为例:
(F_GETFL 开始写成 F_GETFLD 结果是write一直阻塞状态 无法退出哎)
5. 当要写入的数据量不大于PIPE_BUF时,linux将保证写入的原子性。
当要写入的数据量大于PIPE_BUF时,linux将不再保证写入的原子性。
执行结果如下:
AB交叉了。write不再是一次性全部写入。管道容量可以测试:65536(我的)
6.最后 深入理解下管道
用管道实现了 shell中管道ls | wc -l
2.管道是半双工的,数据只能一个方向流动。
3.只有调用fork 父子进程才能管道通信。
4.管道的读写规则
一.如果所有管道读端对应的文件描述符被关闭,则write操作会产生信号SIGPIPE.
#include<stdio.h> #include<stdlib.h> #include<unistd.h> #include<signal.h> #include<fcntl.h> void handler(int num) { int mypid; if(num == SIGCHLD) { while((mypid =waitpid(-1,NULL,WNOHANG)) > 0) { printf("exit pid id %d \n",mypid); } } if(num == SIGPIPE) { printf("sigpipe is happen \n"); } } int main() { int pipefd[2]={0}; pipe(pipefd); signal(SIGCHLD,handler); signal(SIGPIPE,handler); pid_t pid = fork(); if(pid <0) { perror("fork error \n"); } if(pid == 0) { int i = 0; int ret = 0; int count = 0; int flags = 0; close(pipefd[0]); flags = fcntl(pipefd[1],F_GETFD); flags |= O_NONBLOCK; ret = fcntl(pipefd[1],F_SETFD,flags); if(ret == -1) { printf("fcntl error \n"); } while(1) { ret =write(pipefd[1],"l",1); if(ret == -1) { printf("count is %d \n",count); break; } count++; } printf("cout is %d \n",count); close(pipefd[1]); exit(0); }else{ close(pipefd[0]); sleep(1); } printf("father is break \n"); return 0; }
二.如果所有管道写端对应的文件描述符被关闭,则read返回0.
三.当管道不停的被写,写满的时候
O_NONBLOCK disable: write调用阻塞
O_NONBLOCK enable:调用返回-1,errno值为EAGAIN
以测试管道容量为例:
(F_GETFL 开始写成 F_GETFLD 结果是write一直阻塞状态 无法退出哎)
void handler(int num) { int mypid; if(num == SIGCHLD) { while((mypid =waitpid(-1,NULL,WNOHANG)) > 0) { printf("exit pid id %d \n",mypid); } } if(num == SIGPIPE) { printf("sigpipe is happen \n"); } } int main() { int pipefd[2]={0}; int rett; rett = pipe(pipefd); if(rett == -1) { perror("error......\n"); } signal(SIGCHLD,handler); signal(SIGPIPE,handler); pid_t pid = fork(); if(pid <0) { perror("fork error \n"); } if(pid == 0) { int i = 0; int ret = 0; int count = 0; close(pipefd[0]); int flags = fcntl(pipefd[1], F_GETFL); flags = flags | O_NONBLOCK; ret = fcntl(pipefd[1], F_SETFL, flags); if (ret == -1) { printf("fcntl err.\n"); exit(0); } while(1) { ret =write(pipefd[1],"l",1); if(ret == -1) { perror("begin break \n"); break; } count++; } printf("cout is %d \n",count); close(pipefd[1]); exit(0); }else if(pid >0){ sleep(3); close(pipefd[0]); close(pipefd[1]); } printf("father is break \n"); return 0; }
5. 当要写入的数据量不大于PIPE_BUF时,linux将保证写入的原子性。
当要写入的数据量大于PIPE_BUF时,linux将不再保证写入的原子性。
#include<stdio.h> #include<stdlib.h> #include<fcntl.h> #include<unistd.h> #include<errno.h> #include<string.h> #define num_k 68*1024 int main() { char a[num_k]; char b[num_k]; int fd[2] = {0}; memset(a,'A',sizeof(a)); memset(b,'B',sizeof(b)); int ret = pipe(fd); pid_t pid = fork(); if(pid < 0) { perror("fork error.....\n"); return -1; } if(pid == 0) { close(fd[0]); ret = write(fd[1],a,sizeof(a)); printf("son1 pid is %d,ret:%d \n",getpid(),ret); exit(0); } pid = fork(); if(pid == 0) { close(fd[0]); ret = write(fd[1],b,sizeof(b)); printf("son2 pid is %d,ret:%d \n",getpid(),ret); exit(0); } close(fd[1]); sleep(2); char buf_read[1024*4] = {0}; while(1) { int read_ret = 0; ret = read(fd[0],buf_read,sizeof(buf_read)); if(ret == 0) return -1; printf("pid id %d,read buf[4095]%c \n",getpid(),buf_read[4095]); }
执行结果如下:
pid id 50648,read buf[4095]A pid id 50648,read buf[4095]A pid id 50648,read buf[4095]A pid id 50648,read buf[4095]A pid id 50648,read buf[4095]A pid id 50648,read buf[4095]A pid id 50648,read buf[4095]A son1 pid is 50649,ret:69632 pid id 50648,read buf[4095]A pid id 50648,read buf[4095]A pid id 50648,read buf[4095]A pid id 50648,read buf[4095]A pid id 50648,read buf[4095]A pid id 50648,read buf[4095]A pid id 50648,read buf[4095]A pid id 50648,read buf[4095]A pid id 50648,read buf[4095]A pid id 50648,read buf[4095]B pid id 50648,read buf[4095]A pid id 50648,read buf[4095]B pid id 50648,read buf[4095]B pid id 50648,read buf[4095]B pid id 50648,read buf[4095]B pid id 50648,read buf[4095]B pid id 50648,read buf[4095]B pid id 50648,read buf[4095]B pid id 50648,read buf[4095]B pid id 50648,read buf[4095]B pid id 50648,read buf[4095]B pid id 50648,read buf[4095]B pid id 50648,read buf[4095]B pid id 50648,read buf[4095]B pid id 50648,read buf[4095]B son2 pid is 50650,ret:69632 pid id 50648,read buf[4095]B pid id 50648,read buf[4095]B
AB交叉了。write不再是一次性全部写入。管道容量可以测试:65536(我的)
6.最后 深入理解下管道
#include <unistd.h> #include <sys/stat.h> #include <sys/wait.h> #include <sys/types.h> #include <fcntl.h> #include <stdlib.h> #include <stdio.h> #include <errno.h> #include <string.h> #include <signal.h> int main(void ) { int pipefd[2]; pid_t pid; if (pipe(pipefd) == -1 ) { printf("pipe() err..\n"); return -1; } pid = fork(); if (pid == -1) { printf("fork err..\n"); return -1; } if (pid == 0) { close(pipefd[0]); //复制文件描述符pipefd[1],给标准输出,言外之意:execlp的ls命令输出到管道中 dup2(pipefd[1], STDOUT_FILENO); close(pipefd[1]); execlp("ls", "ls", NULL); //如果替换新的进程印象失败,则会执行下面一句话 sprintf(stderr, "execute the cmd ls err..\n"); exit(0); } else if (pid > 0 ) { int len = 0; char buf[100] = {0}; close(pipefd[1]); //复制文件描述符pipefd[0],给标准输入,言外之意:execlp的wc命令从管道中读 dup2(pipefd[0], STDIN_FILENO); close(pipefd[0]); //len = read(pipefd[0], buf, 100); execlp("wc", "wc", "-w", NULL); printf("len:%d, buf:%s \n", len , buf); //close(pipefd[0]); } wait(NULL); printf("parent ..quit\n"); return 0; }
用管道实现了 shell中管道ls | wc -l
相关文章推荐
- Linux进程通信 命名管道
- linux进程通信之使用匿名管道进行父子进程通信
- [置顶] 【Linux】 进程通信--匿名管道
- Linux进程通信之管道
- linux下进程间的通信——有名管道fifo学习笔记
- Linux操作系统分析(10) - 进程通信之管道与信号量
- linux进程通信----FIFO(有名管道)
- Linux编程实践----进程间的通信IPC--管道
- Linux下进程通信之管道
- Linux(七):进程通信IPC(一)之简单的匿名管道编写
- linux进程通信-有名管道
- linux下父子进程间的通信——管道
- linux进程通信:管道(pipe)
- linux下的多进程通信(IPC)原理及实现方案(管道、队列、信号量、共享内存)
- linux 进程间信号通知,线程间同步与通信 Test study Model
- linux进程通信之管道
- Linux C 进程间的管道通信
- Linux间进程通信-----之使用命名管道
- 【网络编程基础】Linux下进程通信方式(共享内存,管道,消息队列,Socket)
- Linux进程通信方法--管道