当前位置: 首页 > news >正文

Linux 进程通信(IPC)一站式笔记:概念 → 常用方式 → 函数原型与参数详解

文章目录

  • Linux 进程通信(IPC)一站式笔记:概念 → 常用方式 → 函数原型与参数详解(含示例)
    • 1. 进程为什么要通信?什么是 IPC?
    • 2. 常用通信方式概览(按“用途/特点”记忆)
    • 3. 信号(Signals):事件通知的万能扳手
      • 3.1 快速上手示例
      • 3.2 信号分类与默认动作
      • 3.3 发送信号
        • A) 自动发送
        • B) 系统调用 `kill(2)`
        • C) 命令 `kill(1)` / `pkill(1)`
      • 3.4 设置信号处理
        • `signal(2)`(简单)
        • `sigaction(2)`(推荐,健壮)
      • 3.5 信号屏蔽字(阻塞/解除/恢复)
      • 3.6 补充:信号集操作小抄
    • 4. 其它 IPC 方式的“怎么记、怎么用”(极简可用版)
      • 4.1 匿名管道 `pipe(2)`(亲缘进程单向字节流)
      • 4.2 命名管道 FIFO `mkfifo(3)`(非亲缘也可)
      • 4.3 共享内存(System V 或 POSIX)
      • 4.4 信号量
      • 4.5 消息队列(System V)
      • 4.6 本地套接字(Unix Domain Socket)
    • 5. 实战建议与易踩坑
    • 6. 函数与参数小抄(可快速回看)
      • 信号收发
      • 管道/FIFO
      • 共享内存(POSIX)
      • 信号量(POSIX)
      • 消息队列(SysV)
      • 本地套接字(UNIX)
    • 7. 读完就能用:两段最小可运行骨架
      • 7.1 “父发信号控制子”的骨架
      • 7.2 “匿名管道抓子进程输出”的骨架
    • 8. 收尾:如何选择 IPC?

Linux 进程通信(IPC)一站式笔记:概念 → 常用方式 → 函数原型与参数详解(含示例)

这篇就是“看一眼就能想起来怎么用”的那种。覆盖:为什么要通信、IPC 概念、常见 7 大方式、信号完整用法(含屏蔽/排队/挂起/恢复)、关键系统调用的原型与参数解释、易踩坑与实战建议


1. 进程为什么要通信?什么是 IPC?

  • 进程彼此隔离(独立地址空间),安全但“互相看不到”。
  • IPC(Inter-Process Communication)进程间通信:为协作而在受控前提下交换数据/事件
  • 典型场景:多进程服务、日志采集、前后台任务协作、GUI 与后端守护进程交互、生产者-消费者等。

2. 常用通信方式概览(按“用途/特点”记忆)

方式适合场景主要特点难度
信号 (signal)事件通知、打断系统调用轻量、带语义;传统信号不排队
信号量 (semaphore)同步/互斥计数/锁;不传数据
匿名管道 (pipe)亲缘进程单向字节流简单、内核缓冲、半双工
命名管道 (FIFO)非亲缘进程字节流文件路径可见、半双工
共享内存 (shm)大量数据高性能共享最快;需配合互斥同步
消息队列 (msg queue)结构化消息、排队有消息边界、内核队列
本地套接字 (Unix Domain Socket)客户端/服务器模型通用可靠、双向、支持传句柄中偏高

本文重点把信号讲细(你给的素材覆盖最全),其它方式给“记忆点+调用小抄”。


3. 信号(Signals):事件通知的万能扳手

3.1 快速上手示例

#include <stdio.h>
#include <unistd.h>
#include <signal.h>void signal_handler(int sig) {printf("Received signal: %d\n", sig);
}int main(void) {signal(SIGINT, signal_handler);    // Ctrl+C → SIGINTint count = 1;while (1) {printf("working... %d\n", count++);sleep(1);}return 0;
}

运行后按 Ctrl + C,会触发 SIGINT,进入处理函数。


3.2 信号分类与默认动作

  • 非实时信号(1–31)不排队,同类信号可能合并(丢数目,只记“来过”)。
  • 实时信号(32–64,宏 SIGRTMINSIGRTMAX支持排队,不丢。

常见非实时信号(摘要):

信号默认说明
SIGHUP1Term终端挂起;常用作“重载配置”
SIGINT2TermCtrl+C
SIGQUIT3CoreCtrl+\
SIGILL4Core非法指令
SIGABRT6Coreabort()
SIGFPE8Core算术异常
SIGKILL9Term不可捕获/忽略
SIGSEGV11Core访问越界
SIGPIPE13Term向已关闭管道写
SIGALRM14Termalarm() 定时
SIGTERM15Term建议的“优雅退出”
SIGCHLD17Ign子进程状态变化
SIGSTOP19Stop不可捕获/忽略,暂停
SIGTSTP20StopCtrl+Z
SIGCONT18Continue从停止恢复

默认动作简记:Term 终止、Ign 忽略、Core 终止并产生 core、Stop/Cont 停/继续。


3.3 发送信号

A) 自动发送
  • 终端 Ctrl+C → SIGINTCtrl+Z → SIGTSTPfg/bg 恢复会发 SIGCONT
B) 系统调用 kill(2)
#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid, int sig);   // 返回 0 成功,-1 失败(看 errno)

例:父控子进程暂停/继续/杀死

#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <signal.h>int main(void){pid_t pid = fork();if (pid == 0) {           // 子int c = 1;while (1) { printf("child %d\n", c++); sleep(1); }} else {                  // 父int n;while (scanf("%d",&n)==1){if (n==1) kill(pid, SIGSTOP);else if (n==2) kill(pid, SIGCONT);else if (n==3) { kill(pid, SIGKILL); waitpid(pid, NULL, 0); break; }}}
}
C) 命令 kill(1) / pkill(1)
kill -TERM <PID>     # 优雅结束
kill -KILL <PID>     # 强制结束
pkill nginx          # 按名字发信号

3.4 设置信号处理

signal(2)(简单)
#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
// handler 可取:SIG_IGN(忽略)、SIG_DFL(默认)、或函数指针

返回上一次的 handler(或 SIG_ERR)。

sigaction(2)(推荐,健壮)
#include <signal.h>
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);struct sigaction {void (*sa_handler)(int);                 // 简单处理void (*sa_sigaction)(int, siginfo_t*, void*); // 带信息处理(需 SA_SIGINFO)sigset_t sa_mask;                        // 处理期间需要屏蔽的信号集int sa_flags;                            // 行为标志void (*sa_restorer)(void);               // 兼容字段,忽略
};

常用 sa_flags

  • SA_RESTART:被信号打断的可重启系统调用自动重启(如 read)。
  • SA_SIGINFO:启用 sa_sigaction,可获取发送者 PID、UID、附加数据等。
  • SA_NOCLDSTOP/SA_NOCLDWAIT:影响 SIGCHLD 语义(子停/子退是否通知/是否产生僵尸)。

示例:带 SA_RESTART 的可重启 read

#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>void sig_handler(int s){ printf("Received: %d\n", s); }int main(void){struct sigaction sa = {0};sa.sa_handler = sig_handler;sa.sa_flags = SA_RESTART;          // 关键sigemptyset(&sa.sa_mask);sigaction(SIGUSR1, &sa, NULL);printf("pid=%d\n", getpid());char buf[512];while (1) {int n = read(STDIN_FILENO, buf, sizeof(buf)-1);if (n == -1) {if (errno == EINTR) printf("read interrupted\n");} else {buf[n] = 0; printf("read: %s", buf);}}
}

3.5 信号屏蔽字(阻塞/解除/恢复)

#include <signal.h>
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
// how: SIG_BLOCK | SIG_UNBLOCK | SIG_SETMASK
// set: 要加入/移除/覆盖的集合,oldset:返回原屏蔽字

完整流程示例:阻塞 SIGINT → 检查挂起 → 恢复

#include <stdio.h>
#include <unistd.h>
#include <signal.h>void h(int s){ printf("SIGINT handled: %d\n", s); }int main(void){struct sigaction sa = {0};sa.sa_handler = h; sigemptyset(&sa.sa_mask);sigaction(SIGINT, &sa, NULL);puts("20s later will block SIGINT");sleep(20);sigset_t mask, oldmask;sigemptyset(&mask);sigaddset(&mask, SIGINT);sigprocmask(SIG_BLOCK, &mask, &oldmask);   // 开始屏蔽puts("blocked SIGINT");sigset_t pend; sigpending(&pend);if (sigismember(&pend, SIGINT)) puts("SIGINT is pending!");puts("20s later will restore");sleep(20);sigprocmask(SIG_SETMASK, &oldmask, NULL);  // **正确恢复**puts("restored");while (1) pause();
}

常见坑:恢复时不要再用 SIG_BLOCK,要么 SIG_UNBLOCK 指定某些信号,要么 SIG_SETMASK 恢复原集。


3.6 补充:信号集操作小抄

int sigemptyset(sigset_t *set);                // 置空
int sigfillset(sigset_t *set);                 // 全 1(包含所有信号)
int sigaddset(sigset_t *set, int signum);      // 加入
int sigdelset(sigset_t *set, int signum);      // 删除
int sigismember(const sigset_t *set, int sig); // 是否成员
int sigpending(sigset_t *set);                 // 取挂起集

4. 其它 IPC 方式的“怎么记、怎么用”(极简可用版)

4.1 匿名管道 pipe(2)(亲缘进程单向字节流)

#include <unistd.h>
int pipe(int fd[2]);          // fd[0]=读端, fd[1]=写端

fork() 配合:子进程 dup2(fd[1], STDOUT_FILENO),父进程读 fd[0]

4.2 命名管道 FIFO mkfifo(3)(非亲缘也可)

#include <sys/stat.h>
int mkfifo(const char *path, mode_t mode);
// 打开用 open(),读写与普通文件一致,但遵循管道语义(阻塞/唤醒)

4.3 共享内存(System V 或 POSIX)

System V

#include <sys/shm.h>
int shmid = shmget(key_t key, size_t size, int shmflg);
void* addr = shmat(shmid, const void *shmaddr, int shmflg);
int shmdt(const void *shmaddr);
int shmctl(int shmid, int cmd, struct shmid_ds *buf); // 删除等

POSIX(更现代):

#include <sys/mman.h>
#include <fcntl.h>
#include <sys/stat.h>
int fd = shm_open("/name", O_CREAT|O_RDWR, 0600);
ftruncate(fd, size);
void* p = mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
munmap(p, size); close(fd); shm_unlink("/name");

同步:共享内存只共享数据,不共享“顺序”,配合互斥量/信号量

4.4 信号量

POSIX 有名信号量

#include <semaphore.h>
#include <fcntl.h>
sem_t* sem = sem_open("/semname", O_CREAT, 0600, 1);
sem_wait(sem);   // P
sem_post(sem);   // V
sem_close(sem); sem_unlink("/semname");

匿名(进程内/共享内存内)

sem_t sem;
sem_init(&sem, 1, 1); // pshared=1 表示可跨进程(需放在共享内存中)

4.5 消息队列(System V)

#include <sys/msg.h>
int msgid = msgget(key_t key, int msgflg);
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);

消息结构:

struct msgbuf { long mtype; char mtext[1]; };

4.6 本地套接字(Unix Domain Socket)

流式(SOCK_STREAM) 与 TCP 相同用法,但走内核本地协议、支持传文件描述符

#include <sys/socket.h>
#include <sys/un.h>
// server: socket(AF_UNIX, SOCK_STREAM, 0) → bind("/tmp/sock") → listen → accept
// client: socket → connect("/tmp/sock") → read/write

传 FD 参考 sendmsg/recvmsg + SCM_RIGHTS


5. 实战建议与易踩坑

  • 信号处理函数内只做“异步信号安全”的事:设置标志、write(2);避免 malloc/printf
  • SIGKILL/SIGSTOP 不可捕获/忽略:不要尝试自定义它们的处理。
  • SA_RESTART 与系统调用:不是所有调用都能重启(如 select 有细节);处理好 EINTR
  • 共享内存一定配同步:信号量/互斥量/读写锁,或事件通知 + 序列号。
  • 资源清理:FIFO、消息队列、共享内存段、命名信号量记得 unlink/ctl(IPC_RMID)
  • 安全:Unix 域套接字默认走文件权限模型;共享内存/队列注意创建权限。
  • 调试strace -f -p <PID> 看看谁在阻塞/被信号打断;ipcs/ipcrm 管理 SysV 资源。

6. 函数与参数小抄(可快速回看)

信号收发

int kill(pid_t pid, int sig);
int sigaction(int sig, const struct sigaction *act, struct sigaction *old);
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
int sigpending(sigset_t *set);
int sigemptyset(sigset_t *set); int sigfillset(sigset_t *set);
int sigaddset(sigset_t *set, int sig); int sigdelset(sigset_t *set, int sig);
int sigismember(const sigset_t *set, int sig);

管道/FIFO

int pipe(int fd[2]);
int mkfifo(const char *path, mode_t mode);

共享内存(POSIX)

int shm_open(const char *name, int oflag, mode_t mode);
int ftruncate(int fd, off_t length);
void *mmap(void *addr, size_t len, int prot, int flags, int fd, off_t off);
int munmap(void *addr, size_t len);
int shm_unlink(const char *name);

信号量(POSIX)

sem_t* sem_open(const char *name, int oflag, mode_t mode, unsigned value);
int sem_close(sem_t *sem); int sem_unlink(const char *name);
int sem_wait(sem_t *sem); int sem_post(sem_t *sem);
int sem_init(sem_t *sem, int pshared, unsigned value); // 匿名
int sem_destroy(sem_t *sem);

消息队列(SysV)

int msgget(key_t key, int msgflg);
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
int msgctl(int msqid, int cmd, struct msqid_ds *buf);

本地套接字(UNIX)

int socket(int domain, int type, int protocol);          // AF_UNIX
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
int listen(int sockfd, int backlog);
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
ssize_t send(int fd, const void *buf, size_t len, int flags);
ssize_t recv(int fd, void *buf, size_t len, int flags);

7. 读完就能用:两段最小可运行骨架

7.1 “父发信号控制子”的骨架

pid_t pid = fork();
if (pid == 0) {while (1) { /* 工作 */ sleep(1); }
} else {kill(pid, SIGSTOP);  // 暂停sleep(2);kill(pid, SIGCONT);  // 继续sleep(2);kill(pid, SIGTERM);  // 优雅结束waitpid(pid, NULL, 0);
}

7.2 “匿名管道抓子进程输出”的骨架

int p[2]; pipe(p);
pid_t pid = fork();
if (pid == 0) {close(p[0]); dup2(p[1], 1);    // 子把 stdout 指向管道写端execlp("echo", "echo", "hello IPC", (char*)NULL);_exit(127);
}
close(p[1]); char buf[256]; ssize_t n = read(p[0], buf, sizeof buf);
write(1, buf, n); close(p[0]); waitpid(pid, NULL, 0);

8. 收尾:如何选择 IPC?

  • 事件/唤醒/打断:信号(+ sigwaitinfo/signalfd 也可考虑)
  • 少量字节流:管道/FIFO
  • 大量数据:共享内存(+ 同步原语)
  • 结构化消息/多生产者排队:消息队列/本地套接字
  • 通用 Client/Server:本地套接字(需要传 FD 时更合适)

http://www.dtcms.com/a/570072.html

相关文章:

  • 盐城网站推广wordpress手机pc分开模板
  • 建立网站时要采用一定的链接结构网站建设最简单的教程视频教程
  • 泰安手机网站建设电话肇庆自助网站建设系统
  • 基于Vue的地铁综合服务管理系统7949eg04(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
  • 房产网站建设接单如何在手机上做微电影网站
  • Oracle AWR管理与快照操作完整指南
  • 济源专业做网站公司又拍云WordPress 插件
  • 蓝色风格网站网站案例上海
  • 如何在 MySQL Server 中配置 SSL 证书 ?
  • UGUI笔记——3D坐标转换成UGUI坐标
  • 2025 | 时序预测新范式:多智能体AI系统实现全流程自动化!
  • 营销型网站建设模板郴州网络科技有限公司
  • 网站服务器如何做热备价网络公司排名前十名有哪些
  • 视频转图片工具
  • 长春网站建设方案优化网络规划设计师教程第二版电子版
  • 怎样建商业网站wordpress升级机制
  • CANN算子开发实战:从动态Shape到测试验证的深度解析
  • re一下--day8--字符串(一)
  • 网站关键词在哪里修改网站建设80hoe
  • 企业数据服务新选择:“五度易链” SaaS/API/ 本地化部署方案适配全规模需求
  • 【JUnit实战3_27】第十六章:用 JUnit 测试 Spring 应用:通过实战案例深入理解 IoC 原理
  • 网站源码模板免费网站服务器2020
  • ftp怎么连接网站空间如何建立外贸网站
  • 不同防滑设计在复杂牙拔除中的效能评估
  • 基于springboot的精准扶贫管理系统开发与设计
  • 电子学会青少年软件编程(C/C++)5级等级考试真题试卷(2025年9月)
  • linux系统rsync文件传输
  • 服务器建站用哪个系统好新闻稿件
  • 基于51单片机的宠物喂食器的设计与实现(论文+源码)
  • 建设网站入不入无形资产云南建设厅网站监理员培训