进程间通信机制
进程间通信机制又称IPC机制,包括管道、信号量、共享内存、消息队列、套接字。
今天先简单介绍一下IPC机制之一的管道。
管道
在Linux系统中操作时,你一定会用到管道。
管道可以用来在两个进程之间传递数据,比如:
ps -ef | grep “bash”
其中‘|’就是管道,其作用就是将 ps 命令的结果写入管道文件,然后 grep 再从管道文件中读出该数据进行过滤。
那么现在大家可以思考一个问题:
如果进程 a 要将从键盘获取的数据循环传递给另一个进程 b, 用已具备的知识思考应该如何完成?
我们可以有很多种方法,比如:把进程a获取的数据放在一个文件中,然后再让进程b去获取。
但是,我们知道文件是存放在磁盘中的,从磁盘中获取数据速度肯定是非常慢的,那么我们可以把他放在内存中可以加快获取速度。
而我们的管道就是存放在内存中的,可以把进程a的数据暂时放在管道中,进程b从管道中获取数据。
有名管道
有名管道可以在任意两个进程之间通信。
有名管道的创建方法:
- 命令创建:
mkfifo fifo //fifo是管道名
- 系统调用创建
#include <sys/types.h>
#include <sys/stat.h>
//filename 是管道名 mode 是创建的文件访问权限
int mkfifo(const char *filename, mode_t mode);
命令创建结果如图:
现在我们通过有名管道来解决我们刚才思考的问题。
进程a的代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#include <string.h>
#include <fcntl.h>
int main()
{
int fd = open("FIFO", O_WRONLY);
assert(fd != -1);
printf("open FIFO success\n");
while(1)
{
printf("please input: ");
char buff[128] = {0};
fgets(buff, 128, stdin);
write(fd, buff, strlen(buff) - 1);
if(strncmp(buff, "end", 3) == 0)
{
break;
}
}
close(fd);
exit(0);
}
进程b的代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#include <string.h>
#include <fcntl.h>
int main()
{
int fd = open("FIFO", O_RDONLY);
assert(fd != -1);
printf("open FIFO success\n");
while(1)
{
char buff[128] = {0};
int n = read(fd, buff, 127);
if(n <= 0 || 0 == strncmp(buff, "end", 3))
{
break;
}
printf("%s\n", buff);
}
close(fd);
exit(0);
}
运行结果如下:
无名管道
无名管道主要应用于父子进程间的通信。
无名管道的创建方式:
#include <unistd.h>
/*
pipe()成功返回 0,失败返回-1
fds[0]是管道读端的描述符
fds[1]是管道写端的描述符
*/
int pipe(int fds[2]);
无名管道代码演示:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#include <string.h>
int main()
{
int fd[2];
int res = pipe(fd);
assert( res != -1 );
pid_t pid = fork();
assert( pid != -1 );
if( pid == 0 )//子进程操作
{
close(fd[1]);//关闭写端
char buff[128] = {0};
read(fd[0], buff, 127);
printf("child read: %s\n", buff);
close(fd[0]);//关闭读端
}
else
{
close(fd[0]);
write(fd[1], "hello", 5);
close(fd[1]);
}
exit(0);
}
运行结果如下:
管道的特点
- 无论有名还是无名,写入管道的数据都在内存中
- 管道是一种半双工通信方式(通信方式有单工、半双工、全双工)
- 有名和无名管道的区别:有名可以在任意进程间使用,而无名主要在父子进程间。
那么管道是怎样实现的呢?
管道其实像一个循环队列一样,有头指针和尾指针控制管道间的数据。
具体表现为下图: