Linux----进程间通信
Posted 4nc414g0n
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux----进程间通信相关的知识,希望对你有一定的参考价值。
进程间通信
1)引入
进程间通信可以:
- 数据传输:一个进程需要将它的数据发送给另一个进程
- 资源共享:多个进程之间共享同样的资源
- 通知事件:一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止时要通知父进程)
- 进程控制:有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另一个进程的所有陷入和异常,并能够及时知道它的状态改变
2)管道
Ⅰ匿名管道pipe
我们用到的 ‘
|
’ 其实就是匿名管道
#include <unistd.h>
int pipe(int pipefd[2]);
#define _GNU_SOURCE /* See feature_test_macros(7) */
#include <fcntl.h> /* Obtain O_* constant definitions */
#include <unistd.h>
int pipe2(int pipefd[2], int flags);
功能
:创建一个管道,一个可用于进程间通信的单向数据通道
解释:
- pipefd[2]:
输出型参数,拿到两个文件描述符
数组 pipefd 用于返回引用的两个文件描述符,pipefd[0] 指的是管道的读取端。 pipefd[1] 指的是管道的写端- flags:如果 flags 为 0,则 pipe2() 与 pipe() 相同。 可以在标志中对以下值进行按位或运算以获得不同的行为
返回值:
- 返回值:成功返回0,失败返回错误代码
下面是一个例子,子进程向父进程传送数据(文件描述符表示)
问
:为什么要打开(读、写)两个文件?
答
:1. 只以读/写打开,子进程也只能继承读/写,不能使用管道 2. 为了灵活通信
问
:为什么父子进程要关闭?
答
:语义上证明管道的单向特性,防止误操作
①匿名管道特性(MARK一下)
特性:
- 管道自带同步机制
- 管道是单向通信的
- 管道是
面向字节流
(写入数据的量和读出数据的量互不相关)的- 管道只能保证是具有血缘关系的进程进程通信常用于父子
- 管道可以保证
一定程度的
数据读取的原子性写入
注意
: 进程退出,曾经打开的文件也会被关掉。管道也是文件,管道的生命周期跟随进程
②验证匿名管道的特性:同步
测试代码:
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 parent\\n"; 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);//最后一个\\0去了 if(size>0) buffer[size]=0;//添加\\0 printf("recieve from child: %s",buffer); else if(size == 0) printf("pipe close,child quit\\n"); break; else break;//TODO int status=0; if(waitpid(id,&status,0)>0) printf("wait success\\n"); 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其实是以原子性写入管道的单元大小
Ⅱ命名管道
①用处
匿名管道应用的一个限制就是只能在具有共同祖先(具有亲缘关系)的进程间通信,如果我们想
在不相关的进程之间交换数据
,可以使用FIFO文件来做这项工作,它经常被称为命名管道,命名管道是一种特殊类型的文件
②指令mkfifo
指令
mkfifo [文件名]
,创建一个有名管道
指令测试
:
输入重定向到myfifo
③函数mkfifo
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);
功能
:在pathname下创建一个命名管道
解释:
- pathname:路径/文件名
- mode:(mode & ~umask)是文件的8进制权限,例如0644
返回值:
- 成功返回0,失败-1
测试代码:
server.c
int 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 :> %s\\n",buffer); else if(size==0) printf("client quit\\n"); 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;
结果:
3)system V IPC
System V 消息队列
System V 共享内存
System V 信号量
4)POSIX IPC
消息队列
共享内存
信号量
互斥量
条件变量
读写锁
以上是关于Linux----进程间通信的主要内容,如果未能解决你的问题,请参考以下文章