Linux:进程间通信
目录
匿名管道
特性
通信情况
命名管道
system V 共享内存
shmget
shmat
shmdt
shmctl
ftok
为什么要进程间通信?
- 数据传输:⼀个进程需要将它的数据发送给另⼀个进程
- 资源共享:多个进程之间共享同样的资源
- 通知事件:⼀个进程需要向另⼀个或⼀组进程发送消息,通知它(它们)发⽣了某种事件(如进程终⽌时要通知父进程)
- 进程控制:有些进程希望完全控制另⼀个进程的执⾏(如Debug进程),此时控制进程希望能够拦截另⼀个进程的所有陷⼊和异常,并能够及时知道它的状态改变
匿名管道
#include <unistd.h>int pipe(int fd[2]);
这里我们需要传入一个整形大小为2的数组
函数内部会为我们创建一个管道,其中一端为写端,另一端为读端,读端的文件描述符会放在我们传入的数组中下标为[0]中, 写端放在下标为[1]中
返回值:成功返回0,失败返回错误代码
所以我们可以在创建管道后,再fork创建一个子进程
假设父进程读,子进程写
那么我们就只需要将父进程的写端关闭,而将子进程的读端关闭,让父进程往fd[0]里读,让子进程往fd[1]里写,这样就可以让我们的父子进程之间进行通信了
#include <iostream>
#include <unistd.h>int main(int argc, char* argv[])
{int pipefd[2];if (pipe(pipefd) == -1)ERR_EXIT("pipe error");pid_t pid;pid = fork();if (pid == 0) {close(pipefd[0]);write(pipefd[1], "hello", 5);close(pipefd[1]);exit(EXIT_SUCCESS);}close(pipefd[1]);char buf[10] = { 0 };read(pipefd[0], buf, 10);printf("buf=%s\n", buf);return 0;
}
特性
匿名管道的5种特性:
- 匿名管道只能用来进行具有血缘关系的进程进行进程间通信(常用于父子)
- 管道文件自带同步机制
- 管道是面向字节流的
- 管道是单向通信的
- 管道文件的生命周期是随进程的
通信情况
匿名管道的4种通信情况
- 写慢,读快 —— 读端进程要被堵塞
- 写快,读慢 —— 当写满了,写端就需要阻塞等待
- 写关闭,读继续 —— read时读到返回值为0,表示读到文件结尾
- 读关闭,写继续 —— 写端的写入没有任何意义(OS会发异常信号杀掉写端进程)
命名管道
匿名管道的缺点是需要有血缘关系的进程之间才能通信,如果是两个毫不相干的进程我们就可以用命名管道来进行通信
命名管道有两种创建方式:
1. 命令行中创建
mkfifo filename
2.程序中创建
#include <sys/types.h>
#include <sys/stat.h>int mkfifo(const char *pathname,mode_t mode);
需要传入两个参数,第一个为文件名(路径名),第二个为文件权限
返回值:成功返回0,失败返回-1
创建成功后,当前路径下就会出现创建好的管道文件了,这时候只需要两个进程分别打开这个管道文件进行正常的文件通信即可
system V 共享内存
共享内存区是最快的IPC(Inter-Process Communication进程间通信)形式
一旦这样的内存映射到共享它的进程的地址空间,这些进程间数据的传递形式不再涉及内核,不需要进入内核调用系统调用来传递彼此间的数据
shmget
#include <sys/ipc.h>
#include <sys/shm.h>int shmget(key_t key, size_t size, int shmflg);
功能:用来创建共享内存
key用于标识共享内存段的键值。通常通过ftok函数生成,以确保其唯一性。如果设置为 IPC_PRIVAE,则表示创建一个私有的共享内存段
size表示指定共享内存段的大小(以字节为单位)
shmflg由九个权限标志构成,它们的用法和创建文件时使用的mode模式标志是一样的。取值为
IPC_CREAT:共享内存不存在,创建并返回;共享内存已存在,获取并返回。
IPC_CREAT && IPC_EXCL:共享内存不存在创建并返回;共享内存已存在,出错返回
成功返回一个非负整数,即该共享内存段的标志码,失败返回-1
shmat
shmat是用于将共享内存段附加到调用进程的地址空间的函数。附加后,进程可以通过返回的指针直接访问共享内存中的数据。
void *shmat(int shmid, const void *shmaddr, int shmflg);
shmid:共享内存段的标识符,由shmget返回
shmaddr:指定共享内存附加到进程地址空间的地址。通常设置为 NULL
,让系统自动选择合适的地址。如果需要指定地址,必须确保该地址对齐。
shmflg:控制附加操作的标志。常用的标志有:0,默认操作,附加共享内存。SHM_RDONLY,以只读方式附加共享内存
返回值:
成功:返回指向共享内存段的指针。
失败:返回(void*)-1,并设置errno以指示错误原因
shmdt
shmdt是用于将共享内存段从调用进程的地址空间分离的函数。分离后,进程不再能够通过之前返回的指针访问共享内存。
int shmdt(const void *shmaddr);
shmaddr:指向共享内存段的指针,由shmat返回。
返回值:
成功:返回0。
失败:返回-1,并设置errno以指示错误原因
shmctl
shmctl是用于对共享内存段进行控制操作的函数,例如获取共享内存的状态、修改权限、删除共享内存等。
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
shmid:共享内存段的标识符,由shmget返回。
cmd:指定要执行的控制命令。常见的命令包括:
- IPC_RMID:删除共享内存段。
- IPC_STAT:获取共享内存段的状态信息,存储到buf指向的结构体中。
- IPC_SET:设置共享内存段的权限和大小等属性。
buf:指向struct shmid_ds的指针,用于存储共享内存段的状态信息或设置新的属性。
struct shmid_ds结构体
struct shmid_ds {struct ipc_perm shm_perm; // 操作权限size_t shm_segsz; // 共享内存段的大小(字节)pid_t shm_lpid; // 最后一个操作的进程 IDpid_t shm_cpid; // 创建共享内存段的进程 IDshort shm_nattch; // 当前附加到共享内存段的进程数// 其他系统依赖的字段...
};
ftok
#include <sys/ipc.h>key_t ftok(const char *pathname, int proj_id);
ftok是一个用于生成唯一键值(key_t类型)的函数
pathname:指定一个文件路径,该文件必须存在且可访问。ftok会根据该文件的 inode 编号和设备号生成键值
proj_id:一个整数,通常是一个小的非负整数(1 字节的值,范围为 0-255),用于进一步区分同一路径下的不同 IPC 资源
返回值:
成功:返回一个唯一的键值(key_t类型)
失败:返回-1,并设置errno以指示错误原因
最后的通信就是让双方进程用ftok相同的参数创建出先相同的键值创建出(获取)共享内存,将字节放在shmat返回的共享内存段中,获取或者写入即可
完