进程间通信(IPC)
概述
进程间通信(Inter Process Communication)是指在两个或多个不同的进程间传递或者交换信息。
进程是一个独立的资源管理单元,不同的进程之间资源是独立的,不能在一个进程中直接访问另一个进程的资源,但是进程间不是孤立的,也需要一些信息的交互和状态传递,所以就需要进程间数据传递,同步和异步的机制
进程间通讯的目的
- 数据传输:一个进程需要将它的数据发送给另一个进程
- 共享数据:多个进程想要操作共享数据,一个进程对共享数据的修改,别的进程应该立刻看到
- 通知事件:一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种时间(如进程终止时要通知父进程)
- 资源共享的同步:多个进程之间共享同样的资源。为了作到这一点,需要内核提供锁和同步机制
- 进程控制:有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另一个进程的所有陷入和异常,并能够及时知道它的状态改变。
linux进程间通信方式:
- 管道:无名管道(pipe)和有名管道(FIFO)
- 消息队列
- 共享内存
- 信号量
- 信号(signal)
- 套接字(socket)
匿名管道与有名管道
匿名管道
概述
无名管道,是一种具有两个端点的通信通道,管道的一端用于读取管道内数据,另一端用于将数据写入到管道内。创建一个管道后,会获取一对文件描述符,用于读写。匿名管道通常用在父子进程之间,由父进程创建匿名管道,子进程会继承管道的读端和写端,实现通讯。
管道是单向的、先进先出、无结构的字节流。写进程在管道的尾端写入数据,读进程在管道的首端读出数据。数据读出后将从管道中移走,其他读进程都不能再读到这些数据。管道提供了简单的流控制机制。进程试图读空管道时,在有数据写入管道前,进程将一直阻塞。同样,管道已经满时,进程再试图写管道,在其他进程从管道中移走数据之前,写进程将一直阻塞。
- 由于pipe存在于内存中,并且只有创建它的进程可以直接拿到读写两个文件描述符,其他的进程想要获取到这两个描述符相对困难,通常做法是父进程先创建无名管道,再创建子进程。由于子进程继承父进程打开的文件描述符,所以父子进程就可以通过无名管道进行通信。
- 无名管道是临时的,在通讯完成进程退出后,将自动消失,对管道的读写可以使用read和write函数
- 无名管道默认情况下只有64kb
pipe
调用头文件
#include <unistd.h>
函数原型
int pipe(int fd[2]);
功能
创建一个匿名管道
参数
-
fd[2]:管道的两个文件描述符
管道创建后,可以直接操作这两个文件描述符
fd[0]
:读端fd[1]
:写端
返回值
成功返回0,失败返回**-1**,提供错误码,可使用perror
无名管道示例:
//父子进程通信 #include "head.h" int main(int argc, char const *argv[]) { //建立2个无名管道(一个用来主进程发,一个用来子进程发) int fds1[2] = {0}; int fds2[2] = {0}; int r1 = pipe(fds1); int r2 = pipe(fds2); if (r1 == -1 || r2 == -1) { perror("pipe failed"); return -1; } //创建子进程 pid_t pid = fork(); if (pid == 0) { char data[100] = {0}; read(fds1[0],data,sizeof(data)); printf("Child receive:%s\n",data); char *msg = "Child send"; write(fds2[1],msg,strlen(msg)); } else if (pid > 0) { char *msg = "Parent send"; write(fds1[1],msg,strlen(msg)); char data[100] = {0}; read(fds2[0],data,sizeof(data)); printf("Parent receive:%s\n",data); } else { perror("fork failed"); return -1; } return 0; }
有名管道
概述
有名管道,有名管道可以用于不相关的进程之间的通信。
创建有名管道的进程可以给管道取名字以及路径,该路径名是以特殊的文件存放在文件系统中,因此两个进程可以通过访问该路径来建立练习,进行进程间的数据交换,有名管道和无名管道都遵循先进先出的原则。
有名管道是一个特殊的文件,它和普通文件一样具有磁盘存放路径,文件权限和其他属性,但两者又有区别,有名管道并不会在磁盘中存放真正的信息,信息存放在内存中。通信的两个进程结束后,信息会自动消失,但管道文件路径依然存在。
有名管道的特点
- 如果以写的方式打开有名管道,其他进程需要以读的方式打开,否则进程会阻塞;如果以读的方式打开,而没有写进程,读进程会阻塞,反之亦然。如果一个进程以读写的方式打开,此进程充当了读写两个身份,则进程不会阻塞。
- 如果两个进程分别以读写的方式打开有名管道,其中一个进程中途退出:
- 如果退出的是读操作,则写操作返回SIGPIPE信号;
- 如果退出的是写操作,测读操作将不再阻塞,直接返回0。
mkfifo
调用头文件
#include <sys/types.h>
#include <sys/stat.h>
函数原型
int mkfifo(const char *pathname, mode_t mode);
功能
创建一个命名管道(文件)
参数
- pathname:想要建立的FIFO文件路劲和文件名
- mode:FIFO文件权限
返回值
成功,返回0,失败返回**-1**,提供错误码,可以使用perror
标签:int,FIFO,间通信,管道,fd,进程,buf From: https://blog.csdn.net/Are_pro_bald/article/details/145228040有名管道示例(两个进程通信):
写
#include "head.h" #define FIFO_PATH "/tmp/temp_fifo" int main(int argc, char const *argv[]) { //判断文件存不存在,如果不存在,则新建 if (access(FIFO_PATH, F_OK) == -1) { int r1 = mkfifo(FIFO_PATH, 0777); if (r1 == -1) { perror("mkfifo failed"); return -1; } } //打开文件 int fd = open(FIFO_PATH, O_WRONLY); if (fd == -1) { perror("open failed"); return -1; } while (1) { char buf[64] = {0}; //从键盘输入数据 fgets(buf, 64, stdin); //将读到的换行符删掉(换成字符串结束标志) buf[strcspn(buf, "\n")] = '\0'; //写入文件 write(fd, buf, strlen(buf)); //如果写入的是bye,则退出 if (strcmp(buf, "bye") == 0) { exit(0); } memset(buf, 0, sizeof(buf)); } return 0; }
读
#include "head.h" #define FIFO_PATH "/tmp/temp_fifo" int main(int argc, char const *argv[]) { //判断文件存不存在,若不存在,则创建 if (access(FIFO_PATH, F_OK) == -1) { int r1 = mkfifo(FIFO_PATH, 0777); if (r1 == -1) { perror("mkfifo failed"); return -1; } } //读方式打开文件 int fd = open(FIFO_PATH, O_RDONLY); if (fd == -1) { perror("open failed"); return -1; } while (1) { char buf[64] = {0}; //读数据 read(fd, buf, sizeof(buf)); //判断接收到的是不是bye,如果是,则退出 if (strcmp(buf, "bye") == 0) { exit(0); } //不是,则打印字符串 printf("receive:%s\n", buf); memset(buf, 0, sizeof(buf)); } return 0; }