[TOC]
# 信號捕捉
## 信號處理方式
一個進程收到一個信號的時候,可以用如下方法進行處理:
1)執行系統默認動作
對大多數信號來說,系統默認動作是用來終止該進程。
2)忽略此信號(丟棄)
接收到此信號后沒有任何動作。
3)執行自定義信號處理函數(捕獲)
用用戶定義的信號處理函數處理該信號。
【注意】:SIGKILL 和 SIGSTOP 不能更改信號的處理方式,因為它們向用戶提供了一種使進程終止的可靠方法。
內核實現信號捕捉過程:

## signal函數
~~~
#include <signal.h>
?
typedef void(*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
功能:
注冊信號處理函數(不可用于 SIGKILL、SIGSTOP 信號),即確定收到信號后處理函數的入口地址。此函數不會阻塞。
?
參數:
signum:信號的編號,這里可以填數字編號,也可以填信號的宏定義,可以通過命令 kill - l("l" 為字母)進行相應查看。
handler : 取值有 3 種情況:
SIG_IGN:忽略該信號
SIG_DFL:執行系統默認動作
信號處理函數名:自定義信號處理函數,如:func
回調函數的定義如下:
void func(int signo)
{
// signo 為觸發的信號,為 signal() 第一個參數的值
}
?
返回值:
成功:第一次返回 NULL,下一次返回此信號上一次注冊的信號處理函數的地址。如果需要使用此返回值,必須在前面先聲明此函數指針的類型。
失敗:返回 SIG_ERR
~~~
該函數由ANSI定義,由于歷史原因在不同版本的Unix和不同版本的Linux中可能有不同的行為。因此應該盡量避免使用它,取而代之使用sigaction函數。
~~~
// 信號處理函數
void signal_handler(int signo)
{
if (signo == SIGINT)
{
printf("recv SIGINT\n");
}
else if (signo == SIGQUIT)
{
printf("recv SIGQUIT\n");
}
}
?
int main()
{
printf("wait for SIGINT OR SIGQUIT\n");
?
/* SIGINT: Ctrl+c ; SIGQUIT: Ctrl+\ */
// 信號注冊函數
signal(SIGINT, signal_handler);
signal(SIGQUIT, signal_handler);
?
while (1); //不讓程序結束
?
return 0;
}
~~~
## sigaction函數
~~~
#include <signal.h>
?
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
功能:
檢查或修改指定信號的設置(或同時執行這兩種操作)。
?
參數:
signum:要操作的信號。
act: 要設置的對信號的新處理方式(傳入參數)。
oldact:原來對信號的處理方式(傳出參數)。
?
如果 act 指針非空,則要改變指定信號的處理方式(設置),如果 oldact 指針非空,則系統將此前指定信號的處理方式存入 oldact。
?
返回值:
成功:0
失敗:-1
~~~
**struct 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); //已棄用
};
~~~
1) sa\_handler、sa\_sigaction:信號處理函數指針,和 signal() 里的函數指針用法一樣,應根據情況給sa\_sigaction、sa\_handler 兩者之一賦值,其取值如下:
a) SIG\_IGN:忽略該信號
b) SIG\_DFL:執行系統默認動作
c) 處理函數名:自定義信號處理函數
2) sa\_mask:信號阻塞集,在信號處理函數執行過程中,臨時屏蔽指定的信號。
3) sa\_flags:用于指定信號處理的行為,通常設置為0,表使用默認屬性。它可以是一下值的“按位或”組合:
? SA\_RESTART:使被信號打斷的系統調用自動重新發起(已經廢棄)
? SA\_NOCLDSTOP:使父進程在它的子進程暫停或繼續運行時不會收到 SIGCHLD 信號。
? SA\_NOCLDWAIT:使父進程在它的子進程退出時不會收到 SIGCHLD 信號,這時子進程如果退出也不會成為僵尸進程。
? SA\_NODEFER:使對信號的屏蔽無效,即在信號處理函數執行期間仍能發出這個信號。
? SA\_RESETHAND:信號處理之后重新設置為默認的處理方式。
? SA\_SIGINFO:使用 sa\_sigaction 成員而不是 sa\_handler 作為信號處理函數。
**信號處理函數:**
~~~
void(*sa_sigaction)(int signum, siginfo_t *info, void *context);
參數說明:
signum:信號的編號。
info:記錄信號發送進程信息的結構體。
context:可以賦給指向 ucontext_t 類型的一個對象的指針,以引用在傳遞信號時被中斷的接收進程或線程的上下文。
~~~
~~~
void myfunc(int sig)
{
printf("hello signal: %d\n", sig);
sleep(5);
printf("wake up .....\n");
}
?
int main()
{
// 注冊信號捕捉函數
struct sigaction act;
act.sa_flags = 0;
act.sa_handler = myfunc;
// 設置臨時屏蔽的信號
sigemptyset(&act.sa_mask); // 清空
// ctrl + 反斜杠
sigaddset(&act.sa_mask, SIGQUIT);
?
sigaction(SIGINT, &act, NULL); //注冊信號
?
while (1);
?
return 0;
}
~~~
## sigqueue函數
~~~
#include <signal.h>
?
int sigqueue(pid_t pid, int sig, const union sigval value);
功能:
給指定進程發送信號。
參數:
pid : 進程號。
sig : 信號的編號。
value : 通過信號傳遞的參數。
union sigval 類型如下:
union sigval
{
int sival_int;
void *sival_ptr;
};
返回值:
成功:0
失敗:-1
~~~
向指定進程發送指定信號的同時,攜帶數據。但如傳地址,需注意,不同進程之間虛擬地址空間各自獨立,將當前進程地址傳遞給另一進程沒有實際意義。
下面我們做這么一個例子,一個進程在發送信號,一個進程在接收信號的發送。
發送信號示例代碼如下:
~~~
/*******************************************************
*功能: 發 SIGINT 信號及信號攜帶的值給指定的進程
*參數: argv[1]:進程號 argv[2]:待發送的值(默認為100)
*返回值: 0
********************************************************/
int main()
{
if (argc >= 2)
{
pid_t pid, pid_self;
union sigval tmp;
?
pid = atoi(argv[1]); // 進程號
if (argc >= 3)
{
tmp.sival_int = atoi(argv[2]);
}
else
{
tmp.sival_int = 100;
}
?
// 給進程 pid,發送 SIGINT 信號,并把 tmp 傳遞過去
sigqueue(pid, SIGINT, tmp);
?
pid_self = getpid(); // 進程號
printf("pid = %d, pid_self = %d\n", pid, pid_self);
}
?
return 0;
}
~~~
接收信號示例代碼如下:
~~~
// 信號處理回調函數
void signal_handler(int signum, siginfo_t *info, void *ptr)
{
printf("signum = %d\n", signum); // 信號編號
printf("info->si_pid = %d\n", info->si_pid); // 對方的進程號
printf("info->si_sigval = %d\n", info->si_value.sival_int); // 對方傳遞過來的信息
}
?
int main()
{
struct sigaction act, oact;
?
act.sa_sigaction = signal_handler; //指定信號處理回調函數
sigemptyset(&act.sa_mask); // 阻塞集為空
act.sa_flags = SA_SIGINFO; // 指定調用 signal_handler
?
// 注冊信號 SIGINT
sigaction(SIGINT, &act, &oact);
?
while (1)
{
printf("pid is %d\n", getpid()); // 進程號
?
pause(); // 捕獲信號,此函數會阻塞
}
?
return 0;
}
~~~
- c語言
- 基礎知識
- 變量和常量
- 宏定義和預處理
- 隨機數
- register變量
- errno全局變量
- 靜態變量
- 類型
- 數組
- 類型轉換
- vs中c4996錯誤
- 數據類型和長度
- 二進制數,八進制數和十六進制數
- 位域
- typedef定義類型
- 函數和編譯
- 函數調用慣例
- 函數進棧和出棧
- 函數
- 編譯
- sizeof
- main函數接收參數
- 宏函數
- 目標文件和可執行文件有什么
- 強符號和弱符號
- 什么是鏈接
- 符號
- 強引用和弱引用
- 字符串處理函數
- sscanf
- 查找子字符串
- 字符串指針
- qt
- MFC
- 指針
- 簡介
- 指針詳解
- 案例
- 指針數組
- 偏移量
- 間接賦值
- 易錯點
- 二級指針
- 結構體指針
- 字節對齊
- 函數指針
- 指針例子
- main接收用戶輸入
- 內存布局
- 內存分區
- 空間開辟和釋放
- 堆空間操作字符串
- 內存處理函數
- 內存分頁
- 內存模型
- 棧
- 棧溢出攻擊
- 內存泄露
- 大小端存儲法
- 寄存器
- 結構體
- 共用體
- 枚舉
- 文件操作
- 文件到底是什么
- 文件打開和關閉
- 文件的順序讀寫
- 文件的隨機讀寫
- 文件復制
- FILE和緩沖區
- 文件大小
- 插入,刪除,更改文件內容
- typeid
- 內部鏈接和外部鏈接
- 動態庫
- 調試器
- 調試的概念
- vs調試
- 多文件編程
- extern關鍵字
- 頭文件規范
- 標準庫以及標準頭文件
- 頭文件只包含一次
- static
- 多線程
- 簡介
- 創建線程threads.h
- 創建線程pthread
- gdb
- 簡介
- mac使用gdb
- setjump和longjump
- 零拷貝
- gc
- 調試器原理
- c++
- c++簡介
- c++對c的擴展
- ::作用域運算符
- 名字控制
- cpp對c的增強
- const
- 變量定義數組
- 盡量以const替換#define
- 引用
- 內聯函數
- 函數默認參數
- 函數占位參數
- 函數重載
- extern "C"
- 類和對象
- 類封裝
- 構造和析構
- 深淺拷貝
- explicit關鍵字
- 動態對象創建
- 靜態成員
- 對象模型
- this
- 友元
- 單例
- 繼承
- 多態
- 運算符重載
- 賦值重載
- 指針運算符(*,->)重載
- 前置和后置++
- 左移<<運算符重載
- 函數調用符重載
- 總結
- bool重載
- 模板
- 簡介
- 普通函數和模板函數調用
- 模板的局限性
- 類模板
- 復數的模板類
- 類模板作為參數
- 類模板繼承
- 類模板類內和類外實現
- 類模板和友元函數
- 類模板實現數組
- 類型轉換
- 異常
- 異常基本語法
- 異常的接口聲明
- 異常的棧解旋
- 異常的多態
- 標準異常庫
- 自定義異常
- io
- 流的概念和類庫結構
- 標準io流
- 標準輸入流
- 標準輸出流
- 文件讀寫
- STL
- 簡介
- string容器
- vector容器
- deque容器
- stack容器
- queue容器
- list容器
- set/multiset容器
- map/multimap容器
- pair對組
- 深淺拷貝問題
- 使用時機
- 常用算法
- 函數對象
- 謂詞
- 內建函數對象
- 函數對象適配器
- 空間適配器
- 常用遍歷算法
- 查找算法
- 排序算法
- 拷貝和替換算法
- 算術生成算法
- 集合算法
- gcc
- GDB
- makefile
- visualstudio
- VisualAssistX
- 各種插件
- utf8編碼
- 制作安裝項目
- 編譯模式
- 內存對齊
- 快捷鍵
- 自動補全
- 查看c++類內存布局
- FFmpeg
- ffmpeg架構
- 命令的基本格式
- 分解與復用
- 處理原始數據
- 錄屏和音
- 濾鏡
- 水印
- 音視頻的拼接與裁剪
- 視頻圖片轉換
- 直播
- ffplay
- 常見問題
- 多媒體文件處理
- ffmpeg代碼結構
- 日志系統
- 處理流數據
- linux
- 系統調用
- 常用IO函數
- 文件操作函數
- 文件描述符復制
- 目錄相關操作
- 時間相關函數
- 進程
- valgrind
- 進程通信
- 信號
- 信號產生函數
- 信號集
- 信號捕捉
- SIGCHLD信號
- 不可重入函數和可重入函數
- 進程組
- 會話
- 守護進程
- 線程
- 線程屬性
- 互斥鎖
- 讀寫鎖
- 條件變量
- 信號量
- 網絡
- 分層模型
- 協議格式
- TCP協議
- socket
- socket概念
- 網絡字節序
- ip地址轉換函數
- sockaddr數據結構
- 網絡套接字函數
- socket模型創建流程圖
- socket函數
- bind函數
- listen函數
- accept函數
- connect函數
- C/S模型-TCP
- 出錯處理封裝函數
- 多進程并發服務器
- 多線程并發服務器
- 多路I/O復用服務器
- select
- poll
- epoll
- epoll事件
- epoll例子
- epoll反應堆思想
- udp
- socket IPC(本地套接字domain)
- 其他常用函數
- libevent
- libevent簡介