信號是發生某種事件的通知機制,signal.h庫文件包括對信號的定義,信號最大編號為常量NSIG
### 信號集
  信號集結構類型為sigset_t,包含一組信號
### 信號掩碼
  信號掩碼的作用在阻塞信號,例如程序為了防止被信號中斷,把信號添加到信號掩碼中;當信號被觸發時,程序不會響應處在掩碼集合中的信號,而是放在等待信號
集合中(sigpending),直到從信號掩碼中移除才會執行信號處理器;信號掩碼中添加和刪除信號用sigprocmask函數
### 暫停進程進行,等待進程的到達
### 信號處理器
  信號處理器用于響應信號的處理事件,當進程收到信號時,會中斷程序的執行,等待信號處理器完成之后,繼續從中斷處執行;如下圖

使用signal安裝信號處理器(注:一般不用signal,信號處理器用sigaction兼容性好)
```c
#include <signal.h>
static void signHandle(int sig)
{
printf("%s\n", "Ouch");
}
int main(int argc, char const *argv[])
{
int i;
// 安裝信號
if (signal(SIGINT, signHandle) == SIG_ERR)
{
printf("%s\n","signal");
}
for (i = 0; ; ++i)
{
printf("%d\n", i);
sleep(3);
}
return 0;
}
```
當終端Ctrl+c中止程序時,主程序會收到SIGINT信號,并觸發信號處理器,當信號處理器打印signal之后,程序繼續往下執行
### 信號系統函數
##### kill發送信號
kill可以向進程發送信號,也可以用于檢查進程是否存在,例如kill(pid, 0)檢測pid進程是否存在
```c
// return 0 on success, or -1 on error
kill(pid_t pid, int sig)
```
- pid>0,發送到pid進程
- pid=0,發送到與調用進程同組的所有進程
- pid=-1,發送給所有進程,除了init進程和調用進程
- pid<-1,發送進程組等于pid絕對值的下屬進程
##### strsignal信號描述
```c
// return pointer to signal description string
strsignal(int sig)
```
##### sigemptyset 初始化一個未包含任何成員的信號集
```c
// return 0 on success, or -1 on error
sigemptyset(sigset_t *set)
```
##### sigfillset 初始化一個包含所有成員的信號集
```c
// return 0 on success, or -1 on error
sigfillset(sigset_t *set)
```
##### sigaddset 向信號集添加信號
```c
// return 0 on success, or -1 on error
sigaddset(sigset_t *set, int sig)
```
##### sigdelset 向信號集刪除信號
```c
// return 0 on success, or -1 on error
sigdelset(sigset_t *set, int sig)
```
##### sigismember 檢查信號是否屬于信號集
```c
// return 1 on true, or 0 on false
sigismember(sigset_t *set, int sig)
```
##### sigaction 信號處理器
```c
// return pointer to signal description string
sigaction(int sig, struct sigaction *act, struct sigaction *oldact)
```
- sig要處理的信號
- struct sigaction *act指向信號處置后的新結構
- struct sigaction *oldact執行信號處理之前的結構
sigaction的結構如下:
```c
struct sigaction {
void (*handler)(int); // 信號處理器內存地址,即函數指針
sigset_t sa_mask; // 信號掩碼
int flags; // 位掩碼,標識信號處理器的行為
void (*sa_restorer)(void)
}
```
- sa_mask定義了一組信號,該組信號將被添加到信號掩碼中,處理器函數返回時自動刪除;除此之外,處理器處理的信號也會被添加到掩碼中,這意味著,程序在處理信號同時,
如果再產生一次信號,那么程序不會遞歸中斷,即再次產生的信號會被阻塞,不會觸發信號信號處理器
- flags是位掩碼,多個位掩碼用與
- SA_NODEFER 不會在執行處理器程序時候將該信號自動添加到進程掩碼中
- SA_ONSTACK 使用了sigaltstack()安裝的備選棧
- SA_SIGINFO 信號處理器程序攜帶了額外的參數
- SA_RESTART 重啟
當flags為SA_SIGINFO時,額外會提供信號其他信息,此時的sigaction結構多一個sa_sigaction字段,結構體如下:
```c
struct sigaction {
union {
void (*sa_hander)(int);
void (*sa_sigaction)(int,siginfo_t *,void *);
},
sigset_t sa_mask; // 信號掩碼
int flags; // 位掩碼,標識信號處理器的行為
void (*sa_restorer)(void)
}
```
以下是sigaction的使用
```c
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
// SIGQUIT信號處理器
void handler(int sig)
{
printf("sig %i %s\n", sig, strsignal(sig));
}
int main(int argc, char const *argv[])
{
struct sigaction sa;
sigemptyset(&sa.sa_mask); // 掩碼集合初始化為空
sa.sa_flags = 0;
sa.sa_handler = handler;
if (sigaction(SIGQUIT, &sa, NULL) == -1) // 終端ctrl+\觸發信號
{
printf("sigaction failed\n");
}
for(;;)
{
sleep(2);
}
return 0;
}
```
##### sigprocmask 向信號掩碼添加或刪除信號
```c
// return pointer to signal description string, or -1 on error
sigprocmask(int how, sigset_t *set, sigset_t *oldset)
```
- how決定了sigprocmask的行為
- SIG_BLOCK,set信號集的信號會添加到掩碼中,oldset會保存上一次掩碼
- SIG_UNBLOCK,set信號集的信號會從掩碼中移除
- SIG_SETMASK,將set信號集賦值給信號掩碼
- set信號集,如果只是想獲得信號掩碼,可以設置為NULL
- oldset會保存上一次信號掩碼集合,如果不關心這個可以設置為NULL
##### sigpending 等待信號集合
```c
// return 0 on success, or -1 on error
sigpending(sigset_t *set)
```
一個例子:
```c
// 打印阻塞信號
void printSigMask(FILE *of, const char *msg)
{
sigset_t signalMask;
if (msg != NULL)
{
fprintf(of, "%s\n", msg);
}
if (sigprocmask(SIG_BLOCK, NULL, &signalMask) == -1)
{
fprintf(of, "sigprocmask failed");
}
printSigset(of, &signalMask, NULL);
}
// 打印所有信號
void printSigset(FILE *of, sigset_t *set, const char *msg)
{
if (msg != NULL) {
fprintf(of, "%s\n", msg);
}
int total = 0;
for (int i = 0; i < NSIG; ++i)
{
fprintf(of, "sig %i %s\n", i, strsignal(i));
total++;
}
if (total == 0)
{
fprintf(of, "sigset is empty\n");
}
}
// 打印等待信號
void printPendingSigs(FILE *of, const char *msg)
{
sigset_t signalPending;
if (msg != NULL)
{
fprintf(of, "%s\n", msg);
}
if (sigpending(&signalPending) == -1)
{
fprintf(of, "sigpending failed");
}
printSigset(of, &signalPending, NULL);
}
```
### 一些常見信號
- SIGKILL 強制殺掉進程,如kill -9 pid,程序處理器無法忽略,捕獲,阻塞
- SIGSTOP 暫停,程序處理器無法忽略,捕獲,阻塞,不允許修改信號的默認行為
- SIGINT 中斷信號,Ctrl + c
- SIGTERM 以正常的方式結束程序來終止(預先清除臨時文件,釋放資源)
- SIGHUP 啟動被終止的信號,類似于重啟
- SIGQUIT Ctrl + \ , 退出信號,并生成可用于調試的核心轉儲文件,提供給gdb調試器調試使用
- SIGABRT 異常終止程序
- SIGSEGV 空間不夠用,默認動作是終止進程
### 參考
- Linux系統編程手冊(上冊)
- php
- 編譯安裝
- 基本概念
- 垃圾回收機制
- 生命周期
- zval底層實現
- c擴展開發
- gdb調試工具
- 自定義擴展簡單demo
- 鉤子函數
- 讀取php.ini配置
- 數組
- 函數
- 類
- yaf擴展底層源碼
- swoole擴展底層源碼
- memoryGlobal內存池
- swoole協程使用記錄
- 單點登錄sso原理
- compser使用
- session實現機制
- c & linux
- gcc
- 指針
- 結構體,聯合和位字段
- 宏定義井號說明
- printf家族函數和可變參數
- 共享函數
- 靜態庫和動態庫
- makefile自動化構建
- 信號一
- 信號二
- inotify監控文件事件
- socket編程
- 簡介
- UNIX DOMAIN
- Internet DOMAIN
- TCP/IP
- 文件IO多路復用
- 內存管理
- 進程組,會話和控制終端
- daemon守護進程
- 多進程
- 多線程
- 常用進制轉換
- go
- 入門知識
- 字節和整數裝換
- python
- redis
- 應用場景
- 消息隊列
- 熱點數據
- 掃碼登錄
- 訂閱發布
- 次數限制
- 搶購超賣
- 持久化機制
- mysql
- 工作流程
- MyISAM和InnoDB區別
- 用戶和權限管理
- 執行計劃
- sql優化
- 事務和鎖
- 慢查詢日志
- case...when...then...end用法
- sql
- 參考
- linux
- 內核參數優化
- 防火墻設置
- docker
- docker入門知識
- 算法
- 多維數組合
- DFA算法
- 紅包金額分配