一、进程通信概述
(一)进程通信的目的
在企业开发中,一个项目常常需要多个进程共同协作,而这些进程之间需要进行通信(交换信息)以便协作。本章内容主要围绕信号讲解,其它进程通信的常用方式请期待后续文章的发布
(二)进程通信的常用方式
- 信号
- 信号量
- 匿名管道
- 命名管道
- 共享内存
- 消息队列
- 本地套接字
二、信号的基本概念
(一)信号的定义
在 Linux 编程中,信号(Signal)是一种异步通信机制,用于通知进程系统中发生了某种事件。它类似于软件中断,可打断进程的正常执行流,使其处理特定事件。
(二)信号的快速入门实例
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>void signal_handler(int arg) {printf("Received signal: %d\n", arg);
}int main(void) {int count = 1;signal(SIGINT, signal_handler);while (1) {printf("working...%d\n", count++);sleep(1);}return 0;
}
程序启动后,按下ctrl + c
会向当前进程发送SIGINT
信号,触发signal_handler
处理函数。
三、信号的分类
这里我只显示常用的,不常用的信号如果你们遇到了问一下AI就行了
(一)非实时信号(1-31)
-
特点:不支持排队,可能造成信号丢失。
-
常见非实时信号:
-
信号 属性值 默认处理方式 定义描述 SIGHUP 1 Term 用户终端连接结束时发出 SIGINT 2 Term 键盘输入 Ctrl+c
时发出,通知前台进程SIGKILL 9 Term 程序员使用 kill -9
指令发出SIGCHLD 17 Ign 子进程结束时父进程收到的信号 SIGSTOP 19 Stop 停止进程执行(暂停不结束) SIGTSTP 20 Stop 键盘按下 Ctrl+Z
时发出 -
处理方式:
- Term:终止进程
- Ign:忽略信号
- Core:终止进程并产生 core dump 文件
- Stop:暂停进程
- Continue:让停止的进程继续执行至
(二)实时信号(32-64)(不常用)
- 特点:支持排队,不会造成信号丢失。
- 范围:从
SIGRTMIN
(34)到SIGRTMAX
(64),如SIGRTMIN+1
、SIGRTMIN+2
等。
四、信号的发送方式
(一)自动发送
例如在终端按下ctrl + c
,会自动向前台进程发送SIGINT
信号至。
(二)使用 kill 系统调用
- 函数原型:
int kill(pid_t pid, int sig);
- 功能:向指定进程(
pid
)发送指定信号(sig
),成功返回 0,失败返回 - 1 至。 - 代码:
#include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/wait.h> #include <unistd.h> int main(void) {pid_t pid = fork();if (pid < 0) {perror("fork failed.");exit(1);}if (pid == 0) {int count = 1;while (1) {printf("child process worked. %d\n", count++);sleep(1);}}else {int n;while (1) {printf("\nPlease select: 1)Stop child process 2)Continue child ""process 3)Killchild process\n");scanf("%d", &n);switch (n) {case 1:kill(pid, SIGSTOP);break;case 2:kill(pid, SIGCONT);break;case 3:kill(pid, SIGKILL);break;default:break;}if (n == 3) {int status;waitpid(pid, &status, 0); // 等待子进程结束并回收子进程break;}}}return 0; }
(三)使用 kill 命令
- 本质:kill 命令的内部实现调用了 kill 系统调用。
- 示例:
kill -9 2483
相当于pid = 2483,调用了kill(2483,
SIGKILL)函数
。
五、信号的处理方式
(一)signal 函数
(这个函数不如sigaction安全,建议编程时尽量用sigaction,代码演示在信号屏蔽之后)
- 函数原型:
sighandler_t signal(int signum, sighandler_t handler);
- 参数:
signum
:除SIGKILL
和SIGSTOP
外的任意信号handler
:可以是SIG_IGN
(忽略信号)、SIG_DFL
(恢复默认处理)或自定义处理函数指针
- 返回值:成功返回上一次的 handler,失败返回
SIG_ERR
至。
(二)sigaction 函数
- 优势:比
signal
更健壮。 - 函数原型:
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
- 结构体
sigaction
:struct sigaction {void (*sa_handler)(int);void (*sa_sigaction)(int, siginfo_t *, void *);sigset_t sa_mask;int sa_flags;void (*sa_restorer)(void); };
sa_flags
常用选项:SA_RESTART
:被信号打断的系统调用自动重新发起SA_NOCLDSTOP
:子进程暂停 / 继续时父进程不接收SIGCHLD
SA_SIGINFO
:使用sa_sigaction
作为处理函数至。
六、信号屏蔽字与信号集操作
(一)信号屏蔽字的处理
- 默认处理:当信号处理函数被调用时,内核自动将当前信号加入进程的信号屏蔽字,处理结束后恢复至。
- 自定义处理函数:
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
how
:SIG_BLOCK
(阻塞信号集)、SIG_UNBLOCK
(解除阻塞)、SIG_SETMASK
(设置新屏蔽集)至。
(二)信号集相关函数
sigemptyset(sigset_t *set)
:初始化信号集为空sigfillset(sigset_t *set)
:初始化信号集包含所有信号sigaddset(sigset_t *set, int signum)
:添加信号到信号集sigdelset(sigset_t *set, int signum)
:从信号集删除信号sigismember(const sigset_t *set, int signum)
:判断信号是否在集合中至。
七、代码演示
#include <signal.h> // 包含信号处理相关函数声明,如sigaction、sigprocmask等
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h> // 包含Unix系统函数,如sleep、fork等// 信号处理函数,当接收到信号时被调用
void sig_handler(int signum)
{printf("Received signal: %d\n", signum); // 打印接收到的信号编号
}int main(void)
{struct sigaction act; // 定义sigaction结构体,用于设置信号处理方式act.sa_handler = sig_handler; // 设置信号处理函数为sig_handleract.sa_flags = 0; // 清空标志位,使用默认处理行为// 清空信号屏蔽集,确保初始时不屏蔽任何信号sigemptyset(&act.sa_mask);// 设置SIGINT信号的处理方式为act中定义的处理方式sigaction(SIGINT, &act, 0); printf("5 秒后,将屏蔽信号 SIGINT\n");// 倒计时5秒,期间可接收SIGINT信号for (int i = 5; i >= 1; i--) {printf("%d\n", i);sleep(1);}// 初始化信号集mask并将SIGINT添加到其中sigset_t mask;sigemptyset(&mask);sigaddset(&mask, SIGINT);// 阻塞mask中的信号(SIGINT),并保存原来的信号屏蔽字sigset_t old_mask;sigprocmask(SIG_BLOCK, &mask, &old_mask);printf("已经将 SIGINT 添加到信号集\n");// 检查是否有悬而未决的信号(即被阻塞但未处理的信号)sigset_t pend_mask;sigpending(&pend_mask);if (sigismember(&pend_mask, SIGINT)) {printf("SIGINI 被挂起了\n");}printf("5 秒后,将恢复信号屏蔽字\n");// 倒计时5秒,期间SIGINT信号会被阻塞for (int i = 5; i >= 1; i--) {printf("%d\n", i);sleep(1);}// 恢复之前保存的信号屏蔽字,解除对SIGINT的阻塞sigprocmask(SIG_SETMASK, &old_mask, 0);printf("信号屏蔽字已恢复\n");while (1) {sleep(1);}return 0;
}