java開發(fā)手機(jī)網(wǎng)站開發(fā)??诰W(wǎng)站關(guān)鍵詞優(yōu)化
問題
如何編寫信號安全的應(yīng)用程序?
Linux 應(yīng)用程序安全性討論
場景一:不需要處理信號
- 應(yīng)用程序?qū)崿F(xiàn)單一功能,不需要關(guān)注信號
- 如:數(shù)據(jù)處理程序,文件加密程序,科學(xué)計算程序
場景二:需要處理信號
- 應(yīng)用程序長時間運(yùn)行,需要關(guān)注信號,并即使處理
- 如:服務(wù)端程序,上位機(jī)程序
場景一:不需要信號處理 (單一功能應(yīng)用程序)
場景二:需要處理信號 (長時間運(yùn)行的應(yīng)用)
同步方案
- 通過標(biāo)記同步處理信號,整個應(yīng)用中只有一個執(zhí)行流
異步方案
- 專用任務(wù)處理,應(yīng)用中存在多個執(zhí)行流 (多線程應(yīng)用)
- 設(shè)置專用信號處理任務(wù),其它任務(wù)忽略信號,專注功能實(shí)現(xiàn)
同步解決方案 (單任務(wù))
信號處理邏輯與程序邏輯位于同一個上下文
- 即:信號處理函數(shù)和主函數(shù)不存在資源競爭關(guān)系
方案設(shè)計一
- 將任務(wù)分解為子任務(wù)(每個任務(wù)可對應(yīng)一個函數(shù))
- 信號遞達(dá)時,信號處理函數(shù)中僅標(biāo)記遞達(dá)狀態(tài)
- 子任務(wù)處理結(jié)束后,真正執(zhí)行信號處理
同步方案示例一
存在的問題
由于給每個信號唯一的標(biāo)記位置,因此,所有信號轉(zhuǎn)變?yōu)椴豢煽啃盘?#xff1b;并且僅保留最近遞達(dá)的信號信息
方案設(shè)計二
將任務(wù)分解為子任務(wù) (每個任務(wù)可對應(yīng)一個函數(shù))
- 創(chuàng)建信號文件描述符,并阻塞所有信號 (可靠信號遞達(dá)前位于內(nèi)核隊列中)
- 子任務(wù)處理結(jié)束后,通過 select 機(jī)制判斷是否有信號需要處理
- true => 處理信號? false => 等待超時
關(guān)鍵系統(tǒng)函數(shù)
#include <sys/select.h>
#include <sys/signalfd.h>
int signalfd(int fd, const sigset_t* mask, int flag);
int select(int nfds, fd_set* readfds, fd_set* writefds, fd_set* exceptfds, struct timeval* timeout);
使用 signalfd() 處理信號
先屏蔽所有信號 (無法遞達(dá)進(jìn)程),之后為屏蔽信號創(chuàng)建文件描述符;當(dāng)時機(jī)成熟,通過 read() 系統(tǒng)調(diào)用讀取未決信號 (主動接收信號)
使用 select() 監(jiān)聽文件描述符
使用 select() 處理信號
存在的問題
由于使用了 select 機(jī)制,即便沒有信號需要處理,也需要等待 select 超時,任務(wù)實(shí)時性受到影響
異步解決方案 (多任務(wù))
使用獨(dú)立任務(wù)處理信號,程序邏輯在其他任務(wù)中執(zhí)行
即:通過多線程分離信號處理與程序邏輯
- 主線程:專用于信號處理
- 其他線程:完成程序功能
多線程信號處理
信號的發(fā)送目標(biāo)是進(jìn)程,而不是某個特定的線程
發(fā)送給進(jìn)程的信號僅遞送給一個進(jìn)程
內(nèi)核從不會阻塞目標(biāo)信號的線程中隨機(jī)選擇
每個線程擁有獨(dú)立的信號屏蔽掩碼
異步解決方案 (多任務(wù))
主線程:對目標(biāo)信號設(shè)置信號處理的方式
- 當(dāng)信號遞達(dá)進(jìn)程時,只可能時主線程進(jìn)行信號處理
其他線程:首先屏蔽所有可能的信號,之后執(zhí)行任務(wù)代碼
- 無法接收到信號,不具備信號處理能力
進(jìn)程與線程
進(jìn)程:應(yīng)用程序的一次加載執(zhí)行 (系統(tǒng)執(zhí)行資源分配的基本單位)
線程:進(jìn)程中的程序執(zhí)行流
- 一個進(jìn)程中可以存在多個線程 (至少存在一個線程)
- 每個線程執(zhí)行不同的任務(wù) (多個線程可并行執(zhí)行)
- 同一個進(jìn)程中的多個線程共享進(jìn)程的系統(tǒng)資源
Linux 多線程 API 函數(shù)
頭文件:#include<pthread.h>
線程創(chuàng)建函數(shù):int pthread_create(pthread_t* thread, const pthread_attr_t* attr, void* (*start_routine)(void*), void* arg);
- thread:pthread_t 變量的地址,用于返回線程標(biāo)識
- attr:線程的屬性,可設(shè)置為 NULL,即:使用默認(rèn)屬性
- start_routine:線程入口函數(shù)
- arg:線程入口函數(shù)參數(shù)
線程標(biāo)識:
- pthread_t pthread_self(void);
- 獲取當(dāng)前線程的 ID 標(biāo)識
線程等待:
- int pthread_join(pthread_t thread, void** retval);
- 等待目標(biāo)線程執(zhí)行結(jié)束
多線程編程示例
異步方案示例 -- 主線程
異步方案示例 -- 任務(wù)線程
信號設(shè)計模式小結(jié)
多數(shù)模式不需要處理信號,因此可直接屏蔽信號
需要處理信號的程序,重點(diǎn)考慮信號安全性問題
- 同步處理方案,通過設(shè)計讓任務(wù)代碼和信號處理代碼交替執(zhí)行
- 問題:信號處理是否及時?任務(wù)執(zhí)行是否實(shí)時?
- 異步處理方案,任務(wù)代碼與信號處理代碼位于不同執(zhí)行流
- 問題:將信號安全性問題轉(zhuǎn)換為線程安全性問題
- 因此,程序本身是否能做到線程安全?