IPC-InterProcess Communication
管道 (Pipe)
传输无格式的字节流
半双工
匿名管道 (Anonymous Pipe)
只存在在内存当中
父子等亲缘进程间的单向数据
生存周期:随着亲缘进程的退出而消失
在shell当中的 | 就是创建了匿名管道
命名管道 (Named Pipe / FIFO)
是一种文件,存储在文件系统当中
任何进程之间都可以使用有名管道进行通信
生命周期:一直存在在文件系统当中,主动删除才会消失
c
// 程序当中创建fifo
mkfifo(管道文件路径, 权限位);
bash
# shell当中创建fifo
mkfifo 管道名
信号 (Signal)
软中断
本质:进程信息结构体中的int类型的信号位
一种异步通知 机制,用于通知进程发生了某种事件,传递信息量小,主要用于异常处理或进程控制,而非大批量数据交换。
不可靠信号(1-31)
| 信号名称 | 编号 | 默认动作 | 说明(常见触发原因) |
|---|---|---|---|
| SIGHUP | 1 | 终止(Term) | 挂断信号,通常由控制终端挂起或控制进程退出时发出。 |
| SIGINT | 2 | 终止(Term) | 来自键盘的中断信号,通常通过 Ctrl+C 触发。 |
| SIGQUIT | 3 | 终止并转储核心(Core) | 来自键盘的退出信号,通常通过 Ctrl+\ 触发,会生成core dump文件。 |
| SIGILL | 4 | 终止并转储核心(Core) | 进程执行了非法指令。 |
| SIGTRAP | 5 | 终止并转储核心(Core) | 跟踪或断点陷阱,常用于调试器。 |
| SIGABRT | 6 | 终止并转储核心(Core) | 进程调用 abort() 函数时发出的异常终止信号。 |
| SIGIOT | 6 | 终止并转储核心(Core) | IOT陷阱,与SIGABRT是等价的。 |
| SIGBUS | 7 | 终止并转储核心(Core) | 总线错误,通常由不正确的内存访问导致。 |
| SIGFPE | 8 | 终止并转储核心(Core) | 浮点运算异常(如除零错误)。 |
| SIGKILL | 9 | 终止(Term) | 强制杀死进程。此信号不可被捕获、阻塞或忽略。 |
| SIGUSR1 | 10 | 终止(Term) | 用户自定义信号1,可由应用程序自行定义用途。 |
| SIGSEGV | 11 | 终止并转储核心(Core) | 段错误,进程试图访问非法内存地址。 |
| SIGUSR2 | 12 | 终止(Term) | 用户自定义信号2,可由应用程序自行定义用途。 |
| SIGPIPE | 13 | 终止(Term) | 管道破裂。进程向一个没有读端的管道(或socket)写入数据。 |
| SIGALRM | 14 | 终止(Term) | 由 alarm() 或 setitimer() 函数设置的定时器超时。 |
| SIGTERM | 15 | 终止(Term) | 终止信号 ,是 kill 命令的默认信号,允许进程进行清理后退出。 |
| SIGSTKFLT | 16 | 终止(Term) | 协处理器栈错误,该信号在现代Linux中已很少使用。 |
| SIGCHLD | 17 | 忽略(Ign) | 子进程状态发生变化(如停止或终止)时,向父进程发送此信号。 |
| SIGCONT | 18 | 继续(Cont) | 让一个被暂停的进程继续运行。 |
| SIGSTOP | 19 | 停止进程(Stop) | 暂停进程。此信号不可被捕获、阻塞或忽略。 |
| SIGTSTP | 20 | 停止进程(Stop) | 来自终端的停止信号,通常通过 Ctrl+Z 触发。 |
| SIGTTIN | 21 | 停止进程(Stop) | 后台进程尝试从控制终端读取数据时发送。 |
| SIGTTOU | 22 | 停止进程(Stop) | 后台进程尝试向控制终端写入数据时发送。 |
| SIGURG | 23 | 忽略(Ign) | 套接字上出现紧急数据或带外数据。 |
| SIGXCPU | 24 | 终止并转储核心(Core) | 进程的CPU使用时间超过了其资源限制(setrlimit)。 |
| SIGXFSZ | 25 | 终止并转储核心(Core) | 进程创建的文件大小超过了其资源限制(setrlimit)。 |
| SIGVTALRM | 26 | 终止(Term) | 虚拟定时器(setitimer)超时,只计算进程用户态CPU时间。 |
| SIGPROF | 27 | 终止(Term) | 系统资源定时器(setitimer)超时,统计进程用户和内核态CPU时间。 |
| SIGWINCH | 28 | 忽略(Ign) | 终端窗口大小发生改变。 |
| SIGIO/SIGPOLL | 29 | 终止(Term) | 文件描述符上可以进行I/O操作(异步I/O通知)。 |
| SIGPWR | 30 | 终止(Term) | 电源故障或重启信号(System V系统)。 |
| SIGSYS | 31 | 终止并转储核心(Core) | 进程调用了错误的系统调用参数。 |
套接字 (Socket)
Unix域套接字 (Unix Domain Socket) 最初由BSD开发,是最通用的IPC机制,支持不同计算机间的网络通信。其本地通信模式(Unix域套接字)也常用于单机内高效的双向通信。
TCP/UDP---ROS1
DDS---ROS2
消息队列(Message Queue)
在kernel当中的位置ipc/msg.c
数据结构:链表
c
//System V
#include <sys/msg.h>
// 创建---message get
int msgget(key_t key, int msgflg);
key:队列的键值
ftok()生成
IPC_PRIVATE
msgflg:权限和标志
IPC_CREAT创建
IPC_EXCL防止重复创建
权限位(eg:0666)
返回值:成功返回消息队列ID(msqid),失败返回-1
// 发送方---message send
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
msqid:msgget()返回的队列ID。
msgp:指向消息结构体(自定义)。
msgsz:消息数据部分大小。
msgflg:
0:阻塞
IPC_NOWAIT:非阻塞,队列满则返回错误
返回值:成功返回0,失败返回-1。
// 接收方---message receive
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
msqid:msgget()返回的队列ID。
msgp:指向消息结构体(自定义,需要与发送者一样)。
msgsz:消息数据部分大小。
msgtyp:按类型读取的关键参数。
0取队头消息,无论类型
>0取该类型第一条
<0取绝对值小于等于它的最小类型消息
msgflg:
0:阻塞接收消息
IPC_NOTWAIT:消息队列为空时返回错误
MSG_EXCEPT:返回队列当中第一个不为msgtype的消息
返回值:成功返回实际读取的数据字节数,失败返回-1
// 控制(包含删除)---message control
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
cmd:控制命令。
IPC_RMID用于删除队列。
IPC_STAT获取队列属性。
IPC_SET设置队列属性。
buf:用于存储或设置队列属性的结构体指针。
返回值:成功返回0,失败返回-1。
共享内存(Shared Memory)
实现方式:多个进程的虚拟内存映射到同一块物理内存区域
通信速度最快
c
#include
// 获取键值(key_t)--- file token
key_t ftok(const char *pathname, int proj_id);
pathname:必须是一个已存在且可以访问的文件路径
proj_id:项目标识符。它可以用来区分同一个文件生成的多个不同的键值
返回值:键值(key_t),失败返回 -1
// 创建---shared memory get
int shmget(key_t key, size_t size, int shmflg);
key:键值
size:共享内存大小(字节),建议为页大小(4096)的整数倍
shmflg:创建方式
IPC_CREAT:不存在则创建,存在则返回标识符
IPC_EXCL:如果存在则返回错误
0666:权限
返回值:共享内存标识符---句柄(shmid),失败返回 -1
// 挂载---shared memory attach
void *shmat(int shmid, const void *shmaddr, int shmflg);
shmid:共享内存标识符
shmaddr:通常设为NULL,由系统自动选择地址
shmflg:共享内存读写权限
0:可读写
SHM_RDONLY:只读
返回值:共享内存起始地址指针,失败返回 (void *)-1
// 分离---shared memory detach
int shmdt(const void *shmaddr);
shmaddr:要断开的内存的地址
返回值:成功返回0,失败返回 -1
// 控制(包括删除)---shared memory control
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
shmid:共享内存标识符
cmd:控制命令
IPC_RMID删除
IPC_STAT获取状态
IPC_SET设置共享内存状态信息
buf:保存状态信息的数据结构
返回值:成功返回0,失败返回 -1
ROS2当中的零拷贝,就是共享内存
信号量(Semaphore)
- 二值信号量
- 计数信号量
c
// 创建---semaphore get
int semget(key_t key, int nsems, int semflg);
key:信号量标识,**不同进程可以使用同一个信号量**
nsems:信号量集中信号量的个数
semflg:信号量集的访问权限
IPC_CREAT | 0666
返回值:信号量标识符---句柄(semid),失败返回 -1
// 控制信号量集---semaphore control
int semctl(int semid, int semnum, int cmd, ...);
semid:信号量标识符
semnum:信号量在集中的索引
cmd:控制命令
SETVAL:设置值
IPC_STAT:获取信号量集的状态信息。
IPC_RMID:删除信号量集。
GETVAL:获取信号量的当前值,
GETPID:获取最后一个对信号量执行wait操作的进程的PID。
GETNCNT:获取等待信号量值大于0的进程数。
GETZCNT:获取等待信号量值等于0的进程数。
..:可选参数,其类型和含义取决于cmd参数的值。
返回值:取决于cmd,失败返回 -1
// 使用(P/V操作)---semaphore operation
int semop(int semid, struct sembuf *sops, unsigned nsops);
semid:信号量标识符
sops:操作数组
包含 sem_num(信号量索引)、sem_op(操作值)、sem_flg(标志)
// nsops:操作数组的元素个数
// 返回值:成功返回0,失败返回 -1
struct sembuf{
unsigned short sem_num; // 信号量索引
short sem_op; // 操作值
short sem_fig; // 标志
}
System V IPC
这是AT&T贝尔实验室发展的一套经典IPC机制,适用于单机内部通信。共享内存是最快的IPC方式,而信号量则常作为同步工具配合使用。
POSIX IPC
为提升System V IPC的可移植性和灵活性而设计的标准化接口。它提供了更现代化的API,并且新增了线程同步原语(如互斥量、条件变量)。
POSIX消息队列 (mqueue.h)
POSIX 消息队列相比 System V 消息队列,提供了基于优先级的消息传递、异步通知机制(信号或线程)、以及更简洁的文件描述符式 API。编译时需链接
-lrt。
c
// 打开/创建消息队列
mqd_t mq_open(const char *name, int oflag, ... /* mode_t mode, struct mq_attr *attr */);
name:消息队列名称,必须以斜杠开头(如 "/myqueue"),且不含其他斜杠
oflag:访问标志
O_RDONLY:只读
O_WRONLY:只写
O_RDWR:读写
O_CREAT:不存在则创建(需提供 mode 和 attr 参数)
O_EXCL:与 O_CREAT 连用,队列已存在则失败
O_NONBLOCK:非阻塞模式
mode:创建权限(如 0666),仅在 O_CREAT 时使用
attr:队列属性,NULL 表示使用系统默认值
struct mq_attr {
long mq_flags; // 队列标志,0 或 O_NONBLOCK
long mq_maxmsg; // 队列最大消息数
long mq_msgsize; // 单条消息最大字节数
long mq_curmsgs; // 当前队列中消息数(只读)
};
返回值:成功返回消息队列描述符(mqd_t),失败返回 (mqd_t)-1 并设置 errno
// 发送消息
int mq_send(mqd_t mqdes, const char *msg_ptr, size_t msg_len,
unsigned int msg_prio);
mqdes:mq_open() 返回的队列描述符
msg_ptr:指向消息数据的指针
msg_len:消息长度(字节数),不能超过 mq_msgsize
msg_prio:消息优先级,值越大优先级越高,队列按优先级从高到低排序
返回值:成功返回 0,失败返回 -1 并设置 errno
说明:若队列满且未设置 O_NONBLOCK,调用线程将阻塞
// 带超时发送消息
int mq_timedsend(mqd_t mqdes, const char *msg_ptr, size_t msg_len,
unsigned int msg_prio, const struct timespec *abs_timeout);
abs_timeout:绝对超时时间(基于 CLOCK_REALTIME)
返回值:成功返回 0,超时返回 -1 且 errno 为 ETIMEDOUT
// 接收消息
ssize_t mq_receive(mqd_t mqdes, char *msg_ptr, size_t msg_len,
unsigned int *msg_prio);
mqdes:mq_open() 返回的队列描述符
msg_ptr:存储接收消息的缓冲区
msg_len:缓冲区大小,必须 >= mq_msgsize
msg_prio:若非 NULL,存储接收消息的优先级
返回值:成功返回接收到的字节数,失败返回 -1 并设置 errno
说明:始终接收队列中优先级最高的消息
// 带超时接收消息
ssize_t mq_timedreceive(mqd_t mqdes, char *msg_ptr, size_t msg_len,
unsigned int *msg_prio, const struct timespec *abs_timeout);
abs_timeout:绝对超时时间(基于 CLOCK_REALTIME)
返回值:成功返回接收到的字节数,超时返回 -1 且 errno 为 ETIMEDOUT
// 注册异步通知
int mq_notify(mqd_t mqdes, const struct sigevent *notification);
mqdes:mq_open() 返回的队列描述符
notification:通知方式
struct sigevent {
int sigev_notify; // 通知方式
SIGEV_NONE:不通知
SIGEV_SIGNAL:发送信号
SIGEV_THREAD:启动线程
int sigev_signo; // 信号编号(SIGEV_SIGNAL 时)
union sigval sigev_value; // 信号附带数据
void (*sigev_notify_function)(union sigval); // 线程函数(SIGEV_THREAD 时)
pthread_attr_t *sigev_notify_attributes; // 线程属性
};
返回值:成功返回 0,失败返回 -1 并设置 errno
说明:每个队列同一时间只能有一个进程注册通知
当队列从空变为非空时,系统向注册进程发送通知
通知发生后注册自动解除,需重新注册
// 获取队列属性
int mq_getattr(mqd_t mqdes, struct mq_attr *attr);
mqdes:mq_open() 返回的队列描述符
attr:存储属性的结构体指针
返回值:成功返回 0,失败返回 -1 并设置 errno
// 设置队列属性
int mq_setattr(mqd_t mqdes, const struct mq_attr *newattr, struct mq_attr *oldattr);
mqdes:mq_open() 返回的队列描述符
newattr:新属性(仅 mq_flags 中的 O_NONBLOCK 标志可修改)
oldattr:保存修改前的属性,不需要时设为 NULL
返回值:成功返回 0,失败返回 -1 并设置 errno
// 关闭消息队列
int mq_close(mqd_t mqdes);
mqdes:mq_open() 返回的队列描述符
返回值:成功返回 0,失败返回 -1 并设置 errno
说明:关闭描述符,但不删除队列。进程终止时自动关闭所有已打开的队列
// 删除消息队列
int mq_unlink(const char *name);
name:消息队列名称
返回值:成功返回 0,失败返回 -1 并设置 errno
说明:删除队列名称。若仍有进程打开该队列,队列在所有进程关闭后才被销毁
类似文件系统的 unlink 机制
POSIX信号量 (semaphore.h)
POSIX 信号量相比 System V 信号量,API 更简洁,分为有名信号量 (可用于不相关进程间)和无名信号量 (基于内存,用于线程或亲缘进程间)。编译时需链接
-pthread(无名信号量)或-lrt(部分系统有名信号量)。
有名信号量
c
// 打开/创建有名信号量
sem_t *sem_open(const char *name, int oflag, ... /* mode_t mode, unsigned int value */);
name:信号量名称,必须以斜杠开头(如 "/mysem")
oflag:访问标志
O_CREAT:不存在则创建(需提供 mode 和 value 参数)
O_EXCL:与 O_CREAT 连用,信号量已存在则失败
mode:创建权限(如 0666),仅在 O_CREAT 时使用
value:信号量初始值
返回值:成功返回信号量指针,失败返回 SEM_FAILED 并设置 errno
// 关闭有名信号量
int sem_close(sem_t *sem);
sem:sem_open() 返回的信号量指针
返回值:成功返回 0,失败返回 -1 并设置 errno
说明:关闭信号量,进程终止时自动关闭所有已打开的信号量
// 删除有名信号量
int sem_unlink(const char *name);
name:信号量名称
返回值:成功返回 0,失败返回 -1 并设置 errno
说明:删除信号量名称。若仍有进程打开,信号量在所有进程关闭后才被销毁
无名信号量
c
// 初始化无名信号量
int sem_init(sem_t *sem, int pshared, unsigned int value);
sem:指向信号量对象的指针
pshared:共享属性
0:仅在同一个进程的线程间共享
非0:可在进程间共享(信号量需位于共享内存区域)
value:信号量初始值
返回值:成功返回 0,失败返回 -1 并设置 errno
// 销毁无名信号量
int sem_destroy(sem_t *sem);
sem:指向信号量对象的指针
返回值:成功返回 0,失败返回 -1 并设置 errno
说明:销毁前确保没有线程在等待该信号量
仅对 sem_init 初始化的信号量使用,不可用于有名信号量
通用操作(有名/无名信号量通用)
c
// P操作 - 等待信号量(阻塞)
int sem_wait(sem_t *sem);
sem:指向信号量对象的指针
返回值:成功返回 0,失败返回 -1 并设置 errno
说明:将信号量值减1。若值已为0,调用线程阻塞直到值大于0
// P操作 - 尝试等待信号量(非阻塞)
int sem_trywait(sem_t *sem);
sem:指向信号量对象的指针
返回值:成功返回 0,失败返回 -1 且 errno 为 EAGAIN(信号量值为0)
// P操作 - 带超时等待信号量
int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);
sem:指向信号量对象的指针
abs_timeout:绝对超时时间(基于 CLOCK_REALTIME)
返回值:成功返回 0,超时返回 -1 且 errno 为 ETIMEDOUT
// V操作 - 释放信号量
int sem_post(sem_t *sem);
sem:指向信号量对象的指针
返回值:成功返回 0,失败返回 -1 并设置 errno
说明:将信号量值加1,若有线程在等待则唤醒其中一个
// 获取信号量当前值
int sem_getvalue(sem_t *sem, int *sval);
sem:指向信号量对象的指针
sval:存储信号量当前值的指针
返回值:成功返回 0,失败返回 -1 并设置 errno
说明:返回值为调用瞬间的快照,可能立即过时(仅作调试/监控用途)
POSIX共享内存 (sys/mman.h)
POSIX 共享内存通过内存映射文件(memfd)实现,使用文件描述符操作,比 System V 共享内存的 shmget/shmat 接口更统一。编译时需链接
-lrt(部分系统还需-lpthread)。
c
// 创建/打开共享内存对象
int shm_open(const char *name, int oflag, mode_t mode);
name:共享内存对象名称,必须以斜杠开头(如 "/myshm"),且不含其他斜杠
oflag:访问标志
O_RDONLY:只读
O_RDWR:读写
O_CREAT:不存在则创建
O_EXCL:与 O_CREAT 连用,对象已存在则失败
O_TRUNC:若对象已存在则将其截断为0长度
mode:创建权限(如 0666),仅在 O_CREAT 时使用
返回值:成功返回文件描述符,失败返回 -1 并设置 errno
说明:shm_open 仅创建对象,大小初始为0,需用 ftruncate 设置大小
// 设置共享内存大小
int ftruncate(int fd, off_t length);
fd:shm_open() 返回的文件描述符
length:共享内存大小(字节),建议为页大小(4096)的整数倍
返回值:成功返回 0,失败返回 -1 并设置 errno
说明:新创建的共享内存对象大小为0,必须调用 ftruncate 设置大小后才能映射
// 内存映射
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
addr:映射起始地址,通常设为 NULL 由系统选择
length:映射长度(字节)
prot:内存保护标志
PROT_READ:可读
PROT_WRITE:可写
PROT_EXEC:可执行
PROT_NONE:不可访问
flags:映射标志
MAP_SHARED:共享映射,修改对其他进程可见且同步到底层对象
MAP_PRIVATE:私有映射,修改不反映到底层对象(写时复制)
MAP_ANONYMOUS:匿名映射,不基于文件,fd 设为 -1
MAP_FIXED:强制映射到 addr 指定的地址(不推荐)
fd:shm_open() 返回的文件描述符(MAP_ANONYMOUS 时设为 -1)
offset:文件偏移量,必须为页大小的整数倍,通常设为 0
返回值:成功返回映射区域的指针,失败返回 MAP_FAILED ((void *)-1) 并设置 errno
// 解除映射
int munmap(void *addr, size_t length);
addr:mmap() 返回的映射地址
length:映射长度
返回值:成功返回 0,失败返回 -1 并设置 errno
说明:进程终止时自动解除所有映射
// 同步映射区域到磁盘
int msync(void *addr, size_t length, int flags);
addr:映射地址
length:同步长度
flags:同步方式
MS_SYNC:同步等待写入完成
MS_ASYNC:异步发起写入
MS_INVALIDATE:使缓存失效,使后续访问重新从磁盘读取
返回值:成功返回 0,失败返回 -1 并设置 errno
// 关闭共享内存对象
int close(int fd);
fd:shm_open() 返回的文件描述符
返回值:成功返回 0,失败返回 -1 并设置 errno
说明:关闭文件描述符,但不删除共享内存对象,也不解除已有的映射
// 删除共享内存对象
int shm_unlink(const char *name);
name:共享内存对象名称
返回值:成功返回 0,失败返回 -1 并设置 errno
说明:删除共享内存对象名称。若仍有进程映射该对象,对象在所有映射解除后才被销毁
类似文件系统的 unlink 机制