Linux_3:进程间通信
IPC
1.什么是IPC?Inter Process Communication
2.进程间通信常用的几种方式
1,管道通信:有名管道,无名管道
2,信号- 系统开销小
3,消息队列-内核的链表
4,信号量-计数器
5,共享内存
6,内存映射
7,套接字
无名管道
管道的概念
1.本质
内核缓冲区
伪文件-不占用磁盘空间
2特点:
两部分: 读端,写端,对应两个文件描述符
数据写端流入,读端流出
操作管理的进程被销毁之后,管道自动被释放
管道默认是阻塞的
管道的原理
1.内部实现方式:
队列-环形队列
特点:先进先出
2.缓冲区大小
默认4K,大小会根据实际情况做适当调整
管道的局限性
1.队列: 数据只能读取一次,不能重复读取
2.半双工:
单工:遥控器
半双工:对讲机
创建匿名管道
int pipe(int fd[2])
fd‐传出参数:
fd[0]‐读端
fd[1]‐写端
返回值:
0:成功
‐1:创建失败
父子进程使用管道通信
实现 ps aux | grep "bash"
数据重定向:dup2
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>int main()
{int ret;int fd[2];ret = pipe(fd);if(ret == -1){printf("pipe creat failed\n");exit(1);}printf("pipe creat success!\n");printf("fd[0] is %d\n",fd[0]);printf("fd[1] is %d\n",fd[1]);close(fd[0]);close(fd[1]);return 0;
}
~
注012是标准输入输出和报错
int dup2(int oldfd, int newfd);//新段指向旧端
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>int main()
{pid_t pid;int ret;int fd[2];ret = pipe(fd);if(ret == -1){printf("pipe creat failed\n");exit(1);}printf("pipe creat success!\n");pid = fork();if(pid == -1){printf("fork failed\n");exit(1);}if(pid > 0){close(fd[0]);dup2(fd[1],STDOUT_FILENO);execlp("ps","ps","aux",NULL);perror("excelp");exit(1);}else if (pid == 0){close(fd[1]);dup2(fd[0],STDIN_FILENO);execlp("grep","grep","bash","--color=auto",NULL);}return 0;
}
单个进程也可以使用管道
父子进程在使用管道的时候,父进程写的时候要关闭读,子进程读的时候要关闭写
管道的读写行为
1.读操作
1)有数据:read(fd[0]) 正常读,返回读出的字节数
2)无数据:
写端被全部关闭,read返回0,相当于读文件到了尾部
没有全部关闭,read阻塞
2.写操作
1)读端全部关闭:
管道破裂,进程被终止
内核给当前进程发送信号SIGPIPE-13,默认处理动作
2)读端没全部关闭:
缓冲区写满了,write阻塞
缓冲区没满,write继续写,直到写满,阻塞
3.如何设置非阻塞?
1)默认读写两端都阻塞
2)设置读端为非阻塞pipe(fd):
fcntl-变参函数:复制文件描述符-dup;修改文件属性-open的时候对应flag属性
设置方法
//获取原来的flags
int flags = fcntl(fd[0],F+GETFL);
//设置新的flags
flag |=O_NONBLOCK;
fcntl(fd[0],F_SETFL,flags);
fcntl(fd[0],F_SETFL,flags);
查看管道缓冲区大小
命令:
ulimit -a
fpathconf
long fpathconf(int fd, int name);
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>int main()
{int ret;int fd[2];ret = pipe(fd);if(ret == -1){printf("pipe creat failed\n");exit(1);}printf("pipe creat success!\n");long size = fpathconf(fd[0],_PC_PIPE_BUF);printf("size id %ld\n",size);printf("fd[0] is %d\n",fd[0]);printf("fd[1] is %d\n",fd[1]);close(fd[0]);close(fd[1]);return 0;
}
有名管道
函数形式:int mkfifo(const char \*filename,mode_t mode);
功能:创建管道文件
参数:管道文件文件名,权限,创建的文件权限仍然和umask有关系。
返回值:创建成功返回0,创建失败返回-1。
特点
有名管道
在磁盘上有这样一个文件 ls -l ->p
也是一个伪文件,在磁盘大小永久为0
数据存在内核中有一个对应的缓冲区
半双工通信方式
使用场景
没有血缘关系的进程间通信
创建方式
命令:mkfifo 管道名
函数:mkfifo()
int mkfifo(const char *pathname, mode_t mode);
fifo文件可以使用io函数进程操作
open/close,read/write
不能执行lseek操作
读函数
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>int main()
{int ret;int fd;int nread;char readBuff[50] = {0};ret = mkfifo("/home/u/process/myfifo",0777);if(ret == -1){return -1;}printf("creat file success!\n");fd = open("./myfifo",O_RDONLY);if(fd < 0){return -1;}printf("open file success!\n");nread = read(fd,readBuff,50);printf("read %d byte from fifo :%s\n",nread,readBuff);close(fd);return 0;
}
写程序
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>int main()
{int fd;char *str = "hello world!";fd = open("./myfifo",O_WRONLY);if(fd < 0){return -1;}printf("open file success!\n");write(fd,str,strlen(str));close(fd);return 0;
}
~