1)引入2)管道
Ⅰ匿名管道pipe
①匿名管道特性(MARK一下)②验证匿名管道的特性:同步
总结 ③管道大小 Ⅱ命名管道
①用处②指令mkfifo③函数mkfifo 3)system V IPC
System V 消息队列System V 共享内存System V 信号量 4)POSIX IPC
消息队列共享内存信号量互斥量条件变量读写锁
1)引入进程间通信可以:
数据传输:一个进程需要将它的数据发送给另一个进程资源共享:多个进程之间共享同样的资源通知事件:一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止时要通知父进程)进程控制:有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另一个进程的所有陷入和异常,并能够及时知道它的状态改变
2)管道 Ⅰ匿名管道pipe
我们用到的 ‘|’ 其实就是匿名管道
①匿名管道特性(MARK一下)#include
int pipe(int pipefd[2]);
#define _GNU_SOURCE
#include
#include
int pipe2(int pipefd[2], int flags);
功能:创建一个管道,一个可用于进程间通信的单向数据通道
解释:pipefd[2]: 输出型参数,拿到两个文件描述符数组 pipefd 用于返回引用的两个文件描述符,pipefd[0] 指的是管道的读取端。 pipefd[1] 指的是管道的写端flags:如果 flags 为 0,则 pipe2() 与 pipe() 相同。 可以在标志中对以下值进行按位或运算以获得不同的行为
返回值:
返回值:成功返回0,失败返回错误代码
下面是一个例子,子进程向父进程传送数据(文件描述符表示)
问:为什么要打开(读、写)两个文件?
答:1. 只以读/写打开,子进程也只能继承读/写,不能使用管道 2. 为了灵活通信
问:为什么父子进程要关闭?
答:语义上证明管道的单向特性,防止误操作
②验证匿名管道的特性:同步特性:
管道自带同步机制管道是单向通信的管道是面向字节流(写入数据的量和读出数据的量互不相关)的管道只能保证是具有血缘关系的进程进程通信常用于父子管道可以保证一定程度的数据读取的原子性写入
注意: 进程退出,曾经打开的文件也会被关掉。管道也是文件,管道的生命周期跟随进程
总结测试代码:
int main() { int pipefd[2]={0}; if(pipe(pipefd)<0)//创建管道 { perror("pipe"); return 1; } pid_t id = fork(); if(id < 0){ perror("fork"); return 1; } else if(id == 0){ //child close(pipefd[0]); int count=5; const char* msg="The mesage send to parentn"; while(count){ write(pipefd[1],msg,strlen(msg)); sleep(1); count--; } close(pipefd[1]); exit(0); } else{//parent close(pipefd[1]); char buffer[64]; while(1){ buffer[0]=0;//清空buffer ssize_t size=read(pipefd[0],buffer,sizeof(buffer)-1);//最后一个 去了 if(size>0){ buffer[size]=0;//添加 printf("recieve from child: %s",buffer); } else if(size == 0){ printf("pipe close,child quitn"); break; } else break;//TODO } int status=0; if(waitpid(id,&status,0)>0) printf("wait successn"); } return 0; }
父进程没有设置sleep(1)却每隔1秒才打印
从结果我们可以得出在子进程没有写入的时候,父进程处于等待状态
当我们把写端改为一直写,读端不读,当达到最大写入时会停下来等有空间再进行写入
while(1){ write(pipefd[1],msg,strlen(msg)); //sleep(1); printf("write:%d",count); count++; } //father:sleep(100);
可以得出在读端没有读的时候,写端处于等待状态
关闭父进程的读端,同时打印一下退出码(status&0x7f)
else{//parent close(pipefd[1]); close(pipefd[0]);
写端被OS发送SIGPIPE信号杀死
关闭子进程的写端
else if(id == 0){ //child close(pipefd[0]); close(pipefd[1]);
父进程的read接收到0,文件结束
| 写端 | 读端 | 结果 |
|---|---|---|
| 不读 | 写 | 写端阻塞 |
| 读 | 不写 | 读端阻塞 |
| 关闭 | 写 | 写端被OS发送SIGPIPE信号杀死 |
| 读 | 关闭 | 父进程的read接收到0,文件结束 |
使用ulimit -a查看系统资源
验证:
让child每次写入一个字节记录次数
观察到近似65535字节,64KB
两个结果为什么不一样呢?
man 7 pipe可以看到一句话:超过PIPE BUF(4KB)的不具有原子性
4kb的pipe size其实是以原子性写入管道的单元大小
Ⅱ命名管道 ①用处
②指令mkfifo匿名管道应用的一个限制就是只能在具有共同祖先(具有亲缘关系)的进程间通信,如果我们想在不相关的进程之间交换数据,可以使用FIFO文件来做这项工作,它经常被称为命名管道,命名管道是一种特殊类型的文件
③函数mkfifo指令mkfifo [文件名],创建一个有名管道
指令测试:
输入重定向到myfifo
#include
#include
int mkfifo(const char *pathname, mode_t mode);
功能:在pathname下创建一个命名管道
解释:pathname:路径/文件名mode:(mode & ~umask)是文件的8进制权限,例如0644
返回值:
成功返回0,失败-1
3)system V IPC System V 消息队列 System V 共享内存 System V 信号量 4)POSIX IPC 消息队列 共享内存 信号量 互斥量 条件变量 读写锁测试代码:
server.cint ret=mkfifo("./fifo",0644); if(ret==-1){ perror("mkfifo"); return 1; } int fd=open("./fifo",O_RDONLY); if(fd<0){ perror("open"); return 2; } char buffer[128]; while(1){ buffer[0]=0;//清空 ssize_t size=read(fd,buffer,sizeof(buffer)-1); if(size>0){ buffer[size]=0; printf("client :> %sn",buffer); } else if(size==0){ printf("client quitn"); break; } else break; } close(fd); return 0;client.c
int fd=open("./fifo",O_WRONLY); if(fd<0){ perror("open"); return 2; } printf("Please Enter:>n"); fflush(stdout); char buffer[128]; while(1){ buffer[0]=0;//清空 ssize_t size=read(0,buffer,sizeof(buffer)-1);//fd==0就是stdin键盘输入 if(size>0){ buffer[size]=0; write(fd,buffer,strlen(buffer)); } else if(size==0){ break; } else break; } close(fd); return 0;结果:



