Linux进程5-进程通信常见的几种方式、信号概述及分类、kill函数及命令、语法介绍
目录
1.进程间通信概述
1.1进程通信的主要方式
1.2进程通信的核心对比
2.信号
2.1 信号的概述
2.1.1 信号的概念
2.2信号的核心特性
2.3信号的产生来源
2.4信号的处理流程
2.5关键系统调用与函数
2.6常见信号的分类及说明
2.6.1. 标准信号(Standard Signals)
2.6.2. 实时信号(Real-Time Signals)
2.6.3. 不可捕获/忽略的信号
2.6.4. 用户自定义信号
2.6.5. 调试与跟踪信号
2.6.6. 终端与作业控制信号
2.6.7. 系统与资源信号
2.7信号分类总结
3.kill函数
3.1 kill命令终端执行
3.1.1基本语法
3.1.1.1. 终止进程
3.1.1.2. 暂停与恢复进程
3.1.1.3. 自定义操作
3.1.1.4. 批量终止进程
3.1.2 kill终端运行程序
1.进程间通信概述
- 数据传输:进程间交换数据(如管道、套接字)。
- 资源共享:多个进程共享同一资源(如共享内存、文件)。
- 协调同步:控制进程执行顺序(如信号量、互斥锁)。
- 事件通知:异步告知进程某事件发生(如信号)。
1.1进程通信的主要方式
根据通信原理和适用场景,IPC 可分为以下类别:
1. 基于文件的通信
- 普通文件:多个进程读写同一文件(需同步机制)。
- 内存映射文件(mmap):将文件映射到内存,多进程直接访问同一内存区域。
- 命名管道(FIFO):通过文件系统路径标识的管道,无关进程可通过路径名通信。
2. 基于内核的通信
- 匿名管道(Pipe):父子进程间的单向通信,通过
|
或pipe()
创建。 - 消息队列(Message Queue):内核维护的链表结构,支持结构化消息(类型、优先级)。
- 共享内存(Shared Memory):多个进程映射同一物理内存区域,速度最快,但需同步机制(如信号量)。
- 信号量(Semaphore):控制对共享资源的访问,解决竞态条件(如 System V 或 POSIX 信号量)。
- 信号(Signal):内核向进程发送异步事件通知(如
SIGTERM
终止进程)。
3. 基于网络的通信
- 套接字(Socket):支持跨网络或本地进程通信(如 TCP/UDP 套接字、Unix 域套接字)。
4. 其他高级机制
- RPC(远程过程调用):跨进程/机器的函数调用抽象。
- DBus:Linux 桌面环境中的高级消息总线,用于进程间服务调用。
1.2进程通信的核心对比
机制 | 方向 | 数据量 | 同步需求 | 适用场景 |
---|---|---|---|---|
匿名管道 | 单向 | 小 | 无 | 父子进程简单通信 |
命名管道 | 单向 | 中 | 无 | 无关进程顺序通信 |
消息队列 | 双向 | 中 | 无 | 结构化消息传递(异步) |
共享内存 | 双向 | 大 | 必需 | 高性能数据共享(需同步) |
信号 | 单向 | 无 | 异步 | 进程控制(如终止、挂起) |
信号量 | 无数据 | 无 | 同步 | 资源访问互斥 |
套接字 | 双向 | 大 | 可选 | 跨网络/本地可靠通信 |
2.信号
2.1 信号的概述
2.1.1 信号的概念
信号(Signal) 是操作系统内核向进程发送的异步事件通知机制,用于通知进程发生了特定事件(如用户中断、程序错误等)。它是进程间通信(IPC)中最轻量级的方式之一,常用于进程控制、错误处理和简单事件通知。



2.2信号的核心特性
- 异步性:信号可能在任何时间点到达进程,打断其正常执行流程。
- 有限信息:仅传递信号编号(如
SIGINT
),无法携带额外数据。 - 预定义类型:Linux 定义了一些标准信号(
1~31
和34~64
),如:SIGINT
(2):终端中断(Ctrl+C)。SIGKILL
(9):强制终止进程(不可捕获或忽略)。SIGSEGV
(11):非法内存访问(段错误)。SIGTERM
(15):请求进程正常终止(优雅终止进程)。SIGUSR1
(10) 和SIGUSR2
(12):用户自定义信号。
- 处理方式:进程可选择忽略、捕获(执行处理函数)或执行默认操作。
2.3信号的产生来源
来源 | 示例场景 |
---|---|
用户输入 | 终端按下 Ctrl+C (发送 SIGINT )。 |
内核事件 | 进程访问非法内存(触发 SIGSEGV )、子进程终止(发送 SIGCHLD )。 |
其他进程 | 通过 kill() 或 kill 命令发送信号(如 kill -9 PID 发送 SIGKILL )。 |
程序自身 | 调用 raise() 或 abort() 触发信号(如 SIGABRT )。 |
2.4信号的处理流程
- 信号产生:由内核、用户或其他进程触发。
- 信号递送:内核将信号放入目标进程的待处理信号队列。
- 信号处理:进程在用户态和内核态切换时检查待处理信号,并根据注册的处理方式响应:
- 默认行为(如终止、暂停、忽略)。
- 捕获信号:执行用户自定义的信号处理函数。
- 忽略信号(
SIGKILL
和SIGSTOP
除外)。
2.5关键系统调用与函数
- 发送信号:
kill(pid, sig)
:向指定进程发送信号。raise(sig)
:向当前进程发送信号。sigqueue(pid, sig, value)
:发送信号并附带额外数据(需配合sigaction
使用)。
- 处理信号:
signal(sig, handler)
:简单注册信号处理函数(不推荐,可能不可靠)。sigaction(sig, act, oldact)
:更安全的信号处理配置(推荐使用)。
- 阻塞信号:
sigprocmask()
:屏蔽或解除屏蔽信号,防止处理函数被中断。
- 等待信号:
pause()
:挂起进程直到收到信号。sigsuspend()
:临时修改信号掩码并等待信号。
2.6常见信号的分类及说明
2.6.1. 标准信号(Standard Signals)
编号范围:1~31
(即 SIGRTMIN
之前的信号),这些是传统的 UNIX 信号,功能固定。
核心分类:
类型 | 信号 | 默认行为 | 典型触发场景 |
---|---|---|---|
进程终止 | SIGTERM (15) | 终止进程 | kill 默认发送,允许优雅退出 |
SIGKILL (9) | 立即终止进程 | kill -9 强制终止,不可捕获或忽略 | |
SIGQUIT (3) | 终止并生成 core 文件 | 终端按下 Ctrl+\ | |
用户交互 | SIGINT (2) | 终止进程 | 终端按下 Ctrl+C |
SIGTSTP (20) | 暂停进程(可恢复) | 终端按下 Ctrl+Z | |
程序错误 | SIGSEGV (11) | 终止并生成 core 文件 | 非法内存访问(段错误) |
SIGFPE (8) | 终止并生成 core 文件 | 算术错误(如除零) | |
SIGILL (4) | 终止并生成 core 文件 | 非法指令(如执行损坏的二进制文件) | |
进程控制 | SIGCHLD (17) | 忽略 | 子进程终止或状态变化 |
SIGCONT (18) | 恢复进程运行 | 用于恢复被暂停的进程(如 fg 命令) | |
SIGSTOP (19) | 立即暂停进程 | 不可捕获或忽略,用于作业控制 |
2.6.2. 实时信号(Real-Time Signals)
编号范围:34~64
(即 SIGRTMIN
到 SIGRTMAX
),支持队列化和携带附加数据。
- 特点:
- 可排队:同一信号多次发送不会丢失(标准信号可能合并为一次)。
- 可携带数据:通过
sigqueue()
发送时附加sival_int
或sival_ptr
。
- 用途:高可靠性事件通知(如自定义通信)。
2.6.3. 不可捕获/忽略的信号
- SIGKILL (9) 和 SIGSTOP (19):
- 由内核直接处理,进程无法修改其行为(无法捕获或忽略)。
- 用于强制终止(
SIGKILL
)或立即暂停(SIGSTOP
)进程。
2.6.4. 用户自定义信号
- SIGUSR1 (10) 和 SIGUSR2 (12):
- 默认行为是终止进程,但通常由程序重定义为自定义逻辑(如重载配置、触发备份)。
- 示例:
bash
# 发送 SIGUSR1 到进程 1234 kill -USR1 1234
2.6.5. 调试与跟踪信号
信号 | 默认行为 | 用途 |
---|---|---|
SIGTRAP (5) | 终止并生成 core 文件 | 调试断点触发(如 gdb 单步执行) |
SIGABRT (6) | 终止并生成 core 文件 | 程序调用 abort() 主动终止(断言失败) |
2.6.6. 终端与作业控制信号
信号 | 默认行为 | 触发场景 |
---|---|---|
SIGHUP (1) | 终止进程 | 终端断开连接(常用于通知守护进程重载配置) |
SIGWINCH (28) | 忽略 | 终端窗口大小改变(如 vim 动态调整界面) |
2.6.7. 系统与资源信号
信号 | 默认行为 | 触发场景 |
---|---|---|
SIGPIPE (13) | 终止进程 | 写入无读端的管道(如 `管道 |
SIGALRM (14) | 终止进程 | 定时器到期(alarm() 或 setitimer() ) |
SIGXCPU (24) | 终止进程 | 进程超出 CPU 时间限制 |
SIGXFSZ (25) | 终止进程 | 文件大小超出限制 |
2.7信号分类总结
分类 | 代表信号 | 核心用途 |
---|---|---|
进程终止 | SIGTERM , SIGKILL , SIGQUIT | 终止或强制终止进程 |
用户交互 | SIGINT , SIGTSTP | 终端控制(暂停/终止) |
程序错误 | SIGSEGV , SIGFPE , SIGILL | 处理非法操作或崩溃 |
进程控制 | SIGCHLD , SIGCONT , SIGSTOP | 管理子进程或作业控制 |
实时通信 | SIGRTMIN ~SIGRTMAX | 高可靠性事件通知(支持队列化和数据携带) |
调试与资源 | SIGTRAP , SIGXCPU , SIGALRM | 调试、资源超限或定时任务 |
3.kill函数
函数原型:
#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid, int signum);功能:
给指定进程发送信号。参数:
pid:详见下页
signum:信号的编号返回值:
成功返回 0,失败返回 -1。pid 的取值有 4 种情况:
pid>0: 将信号传送给进程 ID 为 pid 的进程。
pid=0: 将信号传送给当前进程所在进程组中的所有进程。
pid=-1: 将信号传送给系统内所有的进程。
pid<-1: 将信号传给指定进程组的所有进程。这个进程组号等于 pid 的绝对值。
程序1:未加入kill函数
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>int main(int argc, char *argv[])
{pid_t pid;pid = fork();//通过fork函数创建一个子进程if(pid < 0){perror("fail to fork");//创建失败exit(1);//退出}else if(pid > 0) //父进程的代码区{int i = 0;while(1){printf("父进程正在运行 %d 次\n", i);i++;sleep(1);}printf("父进程 运行 末尾 \n");}else //子进程的代码区 {printf("子进程正在运行 ... \n");printf("子进程 运行 末尾 \n");}return 0;
}
运行结果:
程序2:加入kill函数
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>int main(int argc, char *argv[])
{pid_t pid;pid = fork();//通过fork函数创建一个子进程if(pid < 0){perror("fail to fork");//创建失败exit(1);//退出}else if(pid > 0) //父进程的代码区{int i = 0;while(1){printf("父进程正在运行 %d 次\n", i);i++;sleep(1);}printf("父进程 运行 末尾 \n");}else //子进程的代码区 {printf("子进程正在运行 ... \n");//子进程在3秒之后,让父进程退出sleep(3);//使用kill给父进程发送信号,然后父进程接收到信号后直接退出就可以了// int kill(pid_t pid, int signum);kill(getppid(), SIGINT);// SIGINT 终止进程(终端按下 Ctrl+C)//kill(getppid(), SIGKILL);// SIGKILL 立即终止进程(kill -9 强制终止,不可捕获或忽略)printf("子进程 运行 末尾 \n");}return 0;
}
运行结果:
3.1 kill命令终端执行
kill 是 Linux/Unix 系统中用于向进程发送信号的命令行工具,核心用途是控制进程的行为(如终止、暂停、恢复或自定义操作)。
3.1.1基本语法
kill [选项] <信号> <进程ID>
kill [选项] -<信号名> <进程ID>
常用选项:
-l:列出所有支持的信号名称和编号。
-s <信号>:指定要发送的信号(默认是 SIGTERM)。
信号表示方式:
信号名(如 SIGTERM、SIGKILL)。
信号编号(如 9 对应 SIGKILL)。
3.1.1.1. 终止进程
-
默认行为:默认发送
SIGTERM
(允许进程优雅退出)。kill 1234 # 发送 SIGTERM 到 PID=1234 的进程 kill -15 1234 # 同上(SIGTERM 的编号是 15)
-
强制终止:发送
SIGKILL
(立即终止,不可被捕获或忽略)。kill -9 1234 # 强制终止 PID=1234 的进程 kill -SIGKILL 1234 # 同上(使用信号名) kill -s SIGKILL 1234 # 同上(使用信号名)
3.1.1.2. 暂停与恢复进程
-
暂停进程:发送
SIGSTOP
(立即暂停进程)。kill -SIGSTOP 1234
-
恢复进程:发送
SIGCONT
(继续运行被暂停的进程)。kill -SIGCONT 1234
3.1.1.3. 自定义操作
- 触发用户定义逻辑:使用
SIGUSR1
或SIGUSR2
。kill -SIGUSR1 1234 # 通知进程执行自定义操作(如重载配置)
3.1.1.4. 批量终止进程
- 终止同一进程组:使用负 PID(如
-1234
终止进程组 1234)。kill -9 -1234 # 强制终止进程组 1234 的所有进程
3.1.2 kill终端运行程序
程序:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>int main(int argc, char *argv[])
{pid_t pid;pid = fork();//通过fork函数创建一个子进程if(pid < 0){perror("fail to fork");//创建失败exit(1);//退出}else if(pid > 0) //父进程的代码区{int i = 0;while(1){printf("父进程正在运行 %d 次\n", i);i++;sleep(3);}printf("父进程 运行 末尾 \n");}else //子进程的代码区 {printf("子进程正在运行 ... \n");printf("子进程 运行 末尾 \n");}return 0;
}
运行结果:若无kill函数,父进程每隔3秒打印一次。
(1)终端1运行./a.out ,在终端2执行kill命令退出当前进程。
终端2先执行 ps -ajx | grep a.out 命令,ps ajx //前三列分别为 ppid pid pgid ,
在根据pid执行kill -9 pid号 命令。
(2)终端1运行./a.out ,在终端2执行kill命令退出当前进程。
终端2先执行 ps -ajx | grep a.out 命令,ps ajx //前三列分别为 ppid pid pgid ,
在根据pid执行 kill -s SIGKILL pid号 命令。
(3)终端1运行./a.out ,在终端2执行kill命令退出当前进程。
终端2先执行 ps -ajx | grep a.out 命令,ps ajx //前三列分别为 ppid pid pgid ,
在根据pid执行 kill -SIGKILL pid号 命令。
(4)终端1运行./a.out ,在终端2执行kill命令退出当前进程。
终端2先执行 ps -ajx | grep a.out 命令,ps ajx //前三列分别为 ppid pid pgid ,
在根据pid执行 kill pid号 命令。(kill 默认发送 SIGTERM
(允许进程优雅退出))
(5)终端1运行./a.out ,在终端2执行kill命令退出当前进程。
终端2先执行 ps -ajx | grep a.out 命令,ps ajx //前三列分别为 ppid pid pgid ,
在根据pid执行 kill SIGKILL pid号 命令。