Linux 进程间通讯,也称为IPC(InterProcess Communication)
在 Linux 中每个进程都具备独立的进程地址空间,对每个进程的独立地址空间进行划分,在0G - 3G部分被划分为用户空间,而3G - 4G部分被划分为内核地址空间。注意此0G - 4G 为虚拟地址空间,实际上会通过MMU映射到物理地址空间。
在进行地址映射的时候,每个进程的用户空间在实际物理空间上将被映射到多个地址空间,而多个进程的内核空间将会被被映射到同一块区域,因此多个进程之间具备相同的内核地址空间,通过此共同的内核地址空间实现线程间数据交互即为进程间通讯,也即IPC。
Linux进程间通讯的方式主要分为以下四大种类型:
pipe
signal
mmap
socket
使用管道实现进程间通讯的优点是:使用简单!
管道分为有名管道 FIFO
和匿名管道 pipe
,匿名管道仅能用于具备血缘关系的进程间通讯;而有名管道因其具备了名字可以被找到,因此可用于无血缘关系的进程间通讯。
管道本质是一个伪文件,是由内核管理的一个缓冲区,同时此缓冲区被设计成环形,具备两个端口,一端连接数据的写入,一端连接数据的输出,因此管道仅可用于单向通讯的场合。
当管道中无有效数据时,从管道中读取数据时进入阻塞等待状态,直至有数据从管道的写端写入。
当管道中数据被写满时,再次往管道内写入数据会进入阻塞等待状态,直至有数据从管道的读端被读走。
pipe
创建管道:
#include <unistd.h>
int pipe(int pipefd[2]);
pipefd[0]
指向管道的读端;pipefd[1]
指向管道的写端示例:
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <string.h>
int main(int argc, char **argv)
{
int fd[2] = { 0 };
int ret = 0;
pid_t pid = 0;
char buf[512] = {0};
ret = pipe(fd);
if (ret < 0) {
perror("create pipe failed");
return -1;
}
pid = fork();
if (pid == 0) {
printf("----------Is child process ------------\n");
close(fd[0]); /* close the read of pipe. */
write(fd[1], "hello world!\n", sizeof("hello world!\n"));
} else {
if (pid < 0) {
perror("fork failed");
return -1;
}
printf("----------Is father process -----------\n");
close(fd[1]); /* close the write of pipe. */
while (1) {
memset(buf, 0, sizeof(buf));
ret = read(fd[0], buf, sizeof(buf));
printf("ret:%d read:%s\n", ret, buf);
sleep(1);
}
}
return 0;
}
运行结果如下:
运行分析:
pipe
创建一个匿名管道fork
创建子进程,父子进程均拥有此pipe
FIFO
创建有名管道:
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);
(mode & ~umask)
,通常此值使用0664
注意事项:
如果存在有名管道对应名字的文件,则会返回错误 errno = EEXIST
,因此没有做特殊处理时,重复运行此程序通常会报错。
创建好了有名管道之后,open
此有名管道时,必须要求此管道的读端和写端均被打开,否则 open
函数会进入阻塞状态
open("test_fifo", O_RDONLY)
打开,则open
函数会阻塞直至其他函数采用O_WRONLY
或 O_RDWR
打开写端open("test_fifo", O_WRONLY)
打开,则open
函数会阻塞直至其他函数采用O_RDONLY
或 O_RDWR
打开读端open("test_fifo", O_RDONLY | O_NONBLOCK)
不会报错,但采用非阻塞方式打开只写接口 open("test_fifo", O_WRONLY | O_NONBLOCK)
会返回 No such device or address
错误open("test_fifo", O_RDWR)
,但应用不一定安全!示例:
fifo_r.c:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
int main(int argc, char **argv)
{
int fd = 0, ret = 0;
char buf[512] = {0};
if (mkfifo("test_fifo", 0664) < 0 && errno != EEXIST) {
perror("mkfifo error");
return -1;
}
//fd = open("test_fifo", O_RDONLY | O_NONBLOCK);
fd = open("test_fifo", O_RDONLY );
if (fd < 0) {
perror("open fifo error");
return -1;
}
while (1) {
ret = read(fd, buf, sizeof(buf));
printf("read: ret:%d, buf = %s\n", ret, buf);
sleep(1);
}
return 0;
}
fifo_w.c:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
int main(int argc, char **argv)
{
int fd = 0, ret = 0;
int cnt = 0;
char buf[512] = {0};
if (mkfifo("test_fifo", 0664) < 0 && errno != EEXIST) {
perror("mkfifo error");
return -1;
}
//fd = open("test_fifo", O_RDWR);
fd = open("test_fifo", O_WRONLY);
if (fd < 0) {
perror("open fifo error");
return -1;
}
while (1) {
cnt ++;
snprintf(buf, sizeof(buf), "hello world! cnt:%d", cnt);
ret = write(fd, buf, strlen(buf)+1);
printf("write: ret:%d, buf = %s\n", ret, buf);
sleep(2);
}
return 0;
}
运行结果:
对应创建的test_fifo
文件如下
//TODO:
//TODO:
//TODO: