一、進程通信概述
(一)進程通信的目的
在企業開發中,一個項目常常需要多個進程共同協作,而這些進程之間需要進行通信(交換信息)以便協作。本章內容主要圍繞信號講解,其它進程通信的常用方式請期待后續文章的發布
(二)進程通信的常用方式
- 信號
- 信號量
- 匿名管道
- 命名管道
- 共享內存
- 消息隊列
- 本地套接字
二、信號的基本概念
(一)信號的定義
在 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;
}