Linux 进程间通信方式主要有下面 6 种:
1. 管道 和命名管道:管道(无名管道) 有亲缘关系进程间的通信,命名管道还允许无亲缘关系进程间通信
2. 信号 signal:在软件层模拟中断机制,通知进程某事发生
3. 消息队列:消息的链表包括 posix 消息队列和 SystemV 消息队列
4. 共享内存:多个进程访问一块内存主要用于同步
5. 信号量:进程间同步
6. 套接字 socket:不同机器间进程通信
无名管道
- 只能在具有亲缘关系的进程间进行通信。
- 无名管道在内核当中, 是不可见的。
- 无名管道本质上是一段内存。
- 创建一个无名管道需要调用pipe 接口,创建一个无名管道会同时创建一个读端口,一个写端口。
读端口具有读数据的 功能,写端口具有写数据的功能。
读端口 通过 fd[ 0] 来表示
写端口 通过 fd[ 1] 来表示
管道的容量由 PIPE_BUF 的值决定
可以把无名管道比喻成一个水管,一端进水一端出水,进水的这端就是写端口出水这端就是读端口 。
API:
pipe() 创建一个无名管道
#include <unistd.h>
原型:int pipe(int pipefd[2]);
功能: 创建一个无名管道
参数: fd[2] :
fd[0] : 读端 :读取数据
fd[1] : 写端 : 写数据
返回值: 成功: 0 失败:-1 以及设置相应的 errno 全局的变量
帮助记忆:创建一个数组, fd[0]读数据,fd[1] 写数据
端口是特殊的文件描述符
案例1: 验证创建的无名管道的成功,以及读端进行读写端进行成功,并没有进行进程间的通信。
#include <stdio.h>
#include <unistd.h>
#include <string.h>
//创建管道
int main()
{
char * p = "hello";
char buf[1024] = {'0'};
ssize_t retrd;
int fd[2];
int ret = pipe(fd);
if(ret < 0)
{
printf("pipe is error\r\n");
return -1;
}
//wirte
printf("----------------------------write-----------------------\r\n");
retrd = write(fd[1],p,strlen(p));
printf("----------------------------write end-----------------------\r\n");
printf("----------------------------read-----------------------\r\n");
read(fd[0],buf,retrd);
printf("read buf:%s\r\n",buf);
return 0;
}
运行:
[root@localhost ]# gcc pipe.c
[root@localhost]# ./a.out
----------------------------write-----------------------
----------------------------write end-----------------------
----------------------------read-----------------------
read buf:hello
案例:在父子进程间实现 通信比如 父进程 写hello 子进程读到一个hello 即可 -----单向通信
#include <stdio.h>
#include <unistd.h>
#include <string.h>
//创建管道
int main()
{
char p[10] = {'0'};
char buf[1024] = {'0'};
ssize_t retrd;
int fd[2];
int ret = pipe(fd);//
if(ret < 0)
{
printf("pipe is error\r\n");
return -1;
}
pid_t pid = fork();
if(pid < 0)
{
printf("fork is error\r\n");
return -1;
}else if (0 == pid)
{
sleep(5);
printf(" i am child \r\n");
printf("----------------------------read-----------------------\r\n");
retrd = read(fd[0],buf,sizeof(p));
if(retrd < 0)
{
printf("read is error\r\n");
return -1;
}
printf("read buf:%s\r\n",buf);
}else
{
printf(" i am parent \r\n");
//wirte
printf("----------------------------write-----------------------\r\n");
gets(p); //自己键盘输入
//scanf("%s",p);
retrd = write(fd[1],p,sizeof(p)); //往写管道端写数据
printf("p:%s\r\n",p);
wait(NULL);
printf("----------------------------write end-----------------------\r\n");
}
return 0;
}
[root@localhost ]# gcc pipe.c
[root@localhost ]# ./a.out
i am parent
----------------------------write-----------------------
qqqqqqqqqq
p:qqqqqqqqqq
i am child
----------------------------read-----------------------
read buf:qqqqqqqqqq
----------------------------write end-----------------------
[root@localhost]#
父子进程通过管道实现双向通信(两根管道)
#include <stdio.h>
#include <unistd.h>
#include <string.h>
//创建管道
int main()
{
char p[20] = {'0'};
char buf[20] = {'0'};
ssize_t retrd;
int fd[2];
int fds[2];
//创建管道
int ret = pipe(fd);
if(ret < 0)
{
printf("pipe is error\r\n");
return -1;
}
//创建管道2
int ret1 = pipe(fds);
if(ret1 < 0)
{
printf("pipe is error\r\n");
return -1;
}
//fork进程
pid_t pid = fork();
if(pid < 0)
{
printf("fork is error\r\n");
return -1;
}else if (0 == pid)
{
while(1)
{
memset(buf,0,sizeof(buf));
retrd = read(fd[0],buf,sizeof(p));
if(retrd < 0)
{
printf("read is error\r\n");
return -1;
}
printf("child recvbuf:%s\r\n",buf);
//write
printf("chlid:\r\n");
memset(p,0,sizeof(p));
gets(p);
retrd = write(fds[1],p,sizeof(p));
}
}else
{
printf(" i am parent \r\n");
while(1)
{
//wirte
printf("parent:\r\n");
memset(p,0,sizeof(p));
gets(p);
retrd = write(fd[1],p,sizeof(p));
memset(buf,0,sizeof(buf));
retrd = read(fds[0],buf,sizeof(p));
if(retrd < 0)
{
printf("read is error\r\n");
return -1;
}
printf("parent recvbuf:%s\r\n",buf);
}
//回收子进程的退出状态
wait(NULL);
}
return 0;
}
运行结果:
[root@localhost ]# ./a.out
i am parent
parent:
qqqqq
child recvbuf:qqqqq
chlid:
wwwwwwwwww
parent recvbuf:wwwwwwwwww
parent:
eeeeeeeeee
child recvbuf:eeeeeeeeee
chlid:
zzzzzzzzzz
parent recvbuf:zzzzzzzzzz
parent:
案例3: 能否利用一根管道实现 双向通信。
可以,利用挂起进程的时候,对端口进行读写(有局限性)
#include <stdio.h>
#include <unistd.h>
#include <string.h>
//创建管道
int main()
{
char * p = "hello parent";
char *buf ="hello child" ;
char buf2[20] = {'0'};
ssize_t retrd;
int fd[2];
//创建管道
int ret = pipe(fd);
if(ret < 0)
{
printf("pipe is error\r\n");
return -1;
}
//fork进程
pid_t pid = fork();
if(pid < 0)
{
printf("fork is error\r\n");
return -1;
}else if (0 == pid)
{
sleep(1);
retrd = read(fd[0],buf2,strlen(p));
if(retrd < 0)
{
printf("read is error\r\n");
return -1;
}
printf("child recvbuf:%s\r\n",buf2);
//write
printf("chlid:\r\n");
retrd = write(fd[1],p,strlen(p));
}else
{
printf(" i am parent \r\n");
//wirte
printf("parent:\r\n");
//memset(p,0,sizeof(p));
//gets(p);
retrd = write(fd[1],buf,strlen(buf));
sleep(2);
memset(buf2,0,sizeof(buf2));
retrd = read(fd[0],buf2,strlen(p));
if(retrd < 0)
{
printf("read is error\r\n");
return -1;
}
printf("parent recvbuf:%s\r\n",buf2);
//回收子进程的退出状态
wait(NULL);
}
return 0;
}
运行结果:
[root@localhost 201811.19]# gcc pipe.c
[root@localhost 201811.19]# ./a.out
i am parent
parent:
child recvbuf:hello child
chlid:
parent recvbuf:hello parent
[root@localhost 201811.19]#
读写端口都是特殊的文件描述符 。close 进行关闭 。
思考:当管道的读端口 关闭之后,向管道的写端口 写内容 有没有意义?
答:
当管道的读端口 关闭之后,写端口向读管道写内容没有意义 ,并且 如果写进程一直向 管道写内容 会使 进程收到来自内核发送的信号 ,SIGPIPE ,收到这个信号会使进程终止 。
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
//信号处理函数
void fun(int arg)
{
printf("recv from SIGPIPE signal\r\n");
}
//创建管道
int main()
{
char * p = "hello parent";
char *buf ="hello child" ;
char buf2[20] = {'0'};
ssize_t retrd;
ssize_t retrd1;
int fd[2];
int fds[2];
//信号的捕捉 当捕捉到SIGPIPE 信号的时候 会启动信号处理函数
signal(SIGPIPE,fun);
//创建管道
int ret = pipe(fd);
if(ret < 0)
{
printf("pipe is error\r\n");
return -1;
}
//fork进程
pid_t pid = fork();
if(pid < 0)
{
printf("fork is error\r\n");
return -1;
}else if (0 == pid)
{
retrd = read(fd[0],buf2,strlen(p));
if(retrd < 0)
{
printf("read is error\r\n");
return -1;
}
printf("child recvbuf:%s\r\n",buf2);
close(fd[0]); //关闭读端口
}else
{
printf(" i am parent \r\n");
close(fd[0]); //关闭读端口
retrd = write(fd[1],buf,strlen(buf));
//回收子进程的退出状态
wait(NULL);
printf("kkkkkkkkkk\r\n");
retrd1 = write(fd[1],buf,strlen(buf));
if(retrd1 < 0)
{
printf("write+++\r\n");
}
printf("write end..........\r\n");
}
return 0;
}
运行结果:
[root@localhost ]# gcc pipe.c
[root@localhost ]# ./a.out
i am parent
child recvbuf:hello child
kkkkkkkkkk
recv from SIGPIPE signal
write+++
write end..........
[root@localhost ]#