Unix 进程间通信 IPC
Unix进程间通信IPC
Unix进程间通信,通常有以下几种方式:
1)管道 pipe
2)命名管道 named pipe
3)消息队列
4)信号量
5)共享内存
6)Unix域套接字
7)网络socket
简单介绍下几种通信方式各自的特点:
1)管道:经典简单。但要求两个进程存在某种关系,通常是父子进程间通信。一个pipe()返回两个文件描述符,父子进程点对点使用。
2)命名管道:简单。要求进程知道管道的名字才能使用。区别于管道是只在内存中创建空间,命名管道可以在文件系统中看到。
3)消息队列:顾名思义,区别于点对点的管道,消息队列可以多个进程都可以读写。
4)信号量:类似于一个计数器,多个进程对共享对象的访问。PV的加减操作,多见于二元信号量,类似锁。
5)共享内存:多个进程有大量信息要通信,适合用共享存储方式,通信速度快。俩进程地址空间映射同一片物理内存空间,使用需要注意加锁或用信号量控制。
6)Unix域套接字:适用于同一台机器两个进程通信,比走网络套接字更高效,不会丢失数据。
7)网络套接字:走网络协议的套接字通信。
理论大都抽象枯燥,结合具体代码示例会让知识有血有肉。这里介绍下管道、命名管道。
管道 pipe
命令函数手册 man pipe
管道通常使用pipe(),这个函数可以创建一个管道,并得到两个文件描述符– f[0]是读,f[1]是写,一个给父进程用,一个给子孙进程用。这样通过fork()以后,子孙进程继承了父进程这俩文件描述符。ok,这样父子进程就可以开始通信了。
我们经常会在shell中使用管道命令,一个竖杠| 。比如:
grep xx * | grep sth
这在内部实现即是通过fork进程 和进程之间pipe通信的例子。
pipe 代码示例 父写子读的例子
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
int main(void){
int fd[2];
char str[1024] = "hello world";
char buf[1024];
int pret = pipe(fd);
if(pret < 0){
printf("%s\n", "pipe create err");
return pret;
}
pid_t pid;
pid = fork();
// 父写子读pipe
if(pid > 0){// 父进程
close(fd[0]);// 关闭读
write(fd[1], str, strlen(str));// 往pipe里写
printf("父进程写fd[1]=%d\n", fd[1]);
close(fd[1]);
wait(NULL);
} else if(pid == 0){// 子进程
close(fd[1]);// 关闭写
read(fd[0], buf, sizeof(buf));// 从pipe里读
printf("子进程读fd[0]=%d\nstr is %s\n", fd[0], buf);
close(fd[0]);
} else {
printf("%s\n", "fork fail");
return -1;
}
return 0;
}
named pipe 代码示例
- 可以先在shell中创建一个命名管道,并能在文件系统中查看到该管道类型的文件 prw-r–r– p表示管道类型
# mkfifo fifo1
# ls -l fifo1
- 两个进程,一个表示读,一个表示写
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
// 读
int main(void){
int fd;
int len;
char str[1024];
fd = open("fifo1", O_RDWR);
len = read(fd, str, 1024);
// 写到标准输出 打印下
write(STDOUT_FILENO, str, len);
close(fd);
return 0;
}
// 写
int main(void){
int fd;
char str[1024] = "hey boy";
char str1[1024];
fd = open("fifo1", O_WRONLY);
write(fd, str, strlen(str));
close(fd);
return 0;
}
以上是对管道和命名管道通信的简单介绍。