linux当中的六大进程间通信方式

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 机制
相关推荐
RisunJan1 小时前
Linux命令-ssh-keyscan(从主机收集公钥)
linux·ssh
mgaofeid3 小时前
Ubuntu 24.04 中文输入法安装
linux·运维·ubuntu
xywww1683 小时前
真实后台页实测:Opus 5 看图写前端的可用边界在哪
linux·服务器·前端·数据库·人工智能·gpt
qetfw5 小时前
Debian OpenSSL CA 证书配置参考:根证书、服务证书与验证
linux·debian·ssl·openssl
测试运维日常笔记5 小时前
RHEL/CentOS 离线部署指南:配置本地 ISO 镜像作为 YUM 仓库
linux·运维·centos
梓沂6 小时前
CentOS 7.9 静默安装 Oracle 11gR2 企业版全记录
linux·oracle·centos
蜀道山老天师6 小时前
Zabbix监控Docker容器应用实践完整指南
linux·运维·docker·容器·zabbix
hweiyu006 小时前
Linux命令:info
linux·运维
Dr.kangder7 小时前
嵌入式面试总结(七)——哈佛架构与冯·诺依曼架构
面试·职场和发展·架构·嵌入式·软件工程