創建兩個子進程;
子進程1對文件mytest.txt進行寫操作,
每5秒寫入一次,
寫入當時的時間。
子進程2對文件mytest.txt進行讀操作,
每5秒中讀一次,
讀取並打印文件中所寫入的最新時間。
讀寫操作都使用文件鎖進行保護。
main.c
time_t currtime;
time(&currtime);
char *time_string = ctime(&currtime);
信號
什麼是信號?
信號是一種事件。
不能自定義信號,所有信號都是系統預定義的。
信號由誰產生?
1) 由shell終端根據當前發生的錯誤(段錯誤、非法指令等)而產生相應的信號
比如:
socket通信或者管道通信,
如果讀端都已經關閉,
執行寫操作(或者發送數據),
將導致執行寫操作的進程收到SIGPIPE信號
(表示管道破裂)
該信號的默認行為:終止該進程。
2) 在shell終端,使用kill或killall命令產生信號
實例:main1.c
./a.out &
kill -HUP 13733 /* 向PID為13733的進程發送SIGHUP */
3) 在程序代碼中,調用kill系統調用產生信號
SIGABORT 進程異常終止
SIGALRM 超時告警
SIGFPE 浮點運算異常
SIGHUP 連接掛斷
SIGILL 非法指令
SIGINT 終端中斷 (Ctrl+C將產生該信號)
SIGKILL *終止進程
SIGPIPE 向沒有讀進程的管道寫數據
SIGQUIT 終端退出(Ctrl+\將產生該信號)
SIGSEGV 無效內存段訪問
SIGTERM 終止
SIGUSR1 *用戶自定義信號1
SIGUSR2 *用戶自定義信號2
————————————–>以上信號如果不被捕獲,則進程接受到後都會終止!
SIGCHLD 子進程已停止或退出
SIGCONT *讓暫停的進程繼續執行
SIGSTOP *停止執行(即“暫停”)
SIGTSTP 中斷掛起
SIGTTIN 後台進程嘗試讀操作
信號的捕獲
信號的捕獲,是指,指定接受到某種信號後,去執行指定的函數。
注意:SIGKILL和SIGSTOP不能被捕獲,
即,這兩種信號的響應動作不能被改變。
信號的安裝
1) 使用signal
用法:man 2 signal
typedef void (*sighandler_t)(int); sighandler_t signal(int signum, sighandler_t handler); 注:signal的返回類型,和它的第二個參數,都是函數指針類型 signal的參數2可去以下特殊值: SIG_IGN 忽略信號 SIG_DFL 恢復默認行為 實例: main2.c 改變終端中斷信號的行為 此時就不能結束該進程了! 只能通過其他終端,給該進程發送一個其他信號,使它終止 #ps ax | grep ./a.out //查詢進程號 #kill -HUP 進程號 恢復信號的默認行為 main3.c 使用SIG_DFL時, 僅當第一次調用自定義的行為後 馬上使用SIG_DFL就可恢復, 如果連續捕獲多次後,就不確定。
2) 使用sigaction
sigaction與signal的區別:
sigaction比signal更“健壯”,建議使用sigaction
用法:man 2 sigaction
結構struct sigaction
struct sigaction {
void (sa_handler)(int); / 信號的響應函數 */
sigset_t sa_mask; /* 屏蔽信號集 */
int sa_flags; /* 當sa_flags中包含 SA_RESETHAND時,接受到該信號並調用
* 指定的信號處理函數執行之後,把該信號的響應行為重置為默認行為SIG_DFL */
…
}
補充: 當sa_mask包含某個信號A時, 則在信號處理函數執行期間,如果發生了該信號A, 則阻塞該信號A(即暫時不響應該信號), 直到信號處理函數執行結束。 即,信號處理函數執行完之後,再響應該信號A 實例:main4.c 用sigaction改變響應動作 即:用sigaction改寫main2.c main5.c 用sigaction恢復默認動作 用sigaction改寫main3.c
信號的發送
信號的發送方式:
在shell終端用快捷鍵產生信號
使用kill,killall命令。
使用kill函數和alarm函數
1) 使用kill函數
給指定的進程發送指定信號
用法:man 2 kill
注意:
給指定的進程發送信號需要“權限”:
普通用戶的進程只能給該用戶的其他進程發送信號
root用戶可以給所有用戶的進程發送信號
kill失敗
失敗時返回-1
失敗原因:
權限不夠
信號不存在
指定的進程不存在
實例:main6.c 創建一個子進程 子進程每秒中輸出字符串“child process work!" 父進程等待用戶輸入, 如果用戶按下字符A, 則向子進程發信號SIGUSR1, 子進程的輸出字符串改為大寫 如果用戶按下字符a, 則向子進程發信號SIGUSR2, 子進程的輸出字符串改為小寫
實例:main7.c
“鬧鐘”
創建一個子進程
子進程在5秒鐘之後給父進程發送一個SIGALRM
父進程收到SIGALRM信號之後,“鬧鈴”(用打印模擬)
2)使用alarm函數
作用:在指定時間之內給該進程本身發送一個SIGALRM信號。
用法:man 2 alarm
注意:時間的單位是“秒”
實際鬧鐘時間比指定的時間要大一點。
如果參數為0,則取消已設置的鬧鐘。
如果鬧鐘時間還沒有到,再次調用alarm,則鬧鐘將重新定時
每個進程最多只能使用一個鬧鐘。
返回值: 失敗:返回-1 成功:返回上次鬧鐘的剩余時間(秒) 實例:“鬧鈴” main8.c 用alarm改寫main7.c
3) 使用raise
給本進程自身發送信號。
原型: int raise (int sig)
發送多個信號:
某進程正在執行某個信號對應的操作函數期間(該信號的安裝函數), 如果此時,該進程又多次收到同一個信號(同一種信號值的信號),則: 如果該信號是不可靠信號(<32),則只能再響應一次。 如果該信號是可靠信號(>32),則能再響應多次(不會遺漏)。 但是,都是都必須等該次響應函數執行完之後,才能響應下一次。
某進程正在執行某個信號對應的操作函數期間(該信號的安裝函數),
如果此時,該進程收到另一個信號(不同信號值的信號),則:
如果該信號被包含在當前信號的signaction的sa_mask(信號屏蔽集)中,
則不會立即處理該信號。
直到當前的信號處理函數執行完之後,才去執行該信號的處理函數。
否則:
則立即中斷當前執行過程(如果處於睡眠,比如sleep, 則立即被喚醒)
而去執行這個新的信號響應。
新的響應執行完之後,再在返回至原來的信號處理函數繼續執行。
例:main4_2.c
信號集
1). 什麼是信號集
信號集,用sigset_t類型表示,實質是一個無符號長整形。
用來表示包含多個信號的集合。
2). 信號集的基本操作
sigemptyset 把信號集清空
sigfillset 把所有已定義的信號填充到指定信號集
sigdelset 從指定的信號集中刪除指定的信號
sigaddset 從指定的信號集中添加指定的信號
sigismember 判斷指定的信號是否在指定的信號集中 如果是, 返回 1 如果不是, 返回 0 信號無效, 返回-1 詳細用法見 man
3) 進程的“信號屏蔽字”
進程的“信號屏蔽字”是一個信號集
想目標進程發送某信號時,如果這個信號在目標進程的信號屏蔽字中,
則目標進程將不會捕獲到該信號,即不會執行該信號的處理函數。
當該進程的信號屏蔽字不再包含該信號時,則會捕獲這個早已收到的信號(執行對應的函數)
修改進程的“信號屏蔽字” 使用sigprocmask int sigprocmask(int how, const sigset_t *set, sigset_t *oldset); 參數: how: SIG_BLOCK 把參數set中的信號添加到信號屏蔽字中 SIG_UNBLOCK 把參數set中的信號從信號屏蔽字中刪除 SIG_SETMASK 把參數set中的信號設置為信號屏蔽字 oldset 返回原來的信號屏蔽字 例:main4_3.c
4) 獲取未處理的信號
當進程的信號屏蔽字中信號發生時,這些信號不會被該進程響應,
可通過sigpending函數獲取這些已經發生了但是沒有被處理的信號
用法: man sigpending
返回值:成功則返回0
失敗則返回-1
5) 阻塞式等待信號
(1) pause
阻塞進程,直到發生任一信號後
(2) sigsuspend
用指定的參數設置信號屏蔽字,然後阻塞時等待信號的反生。
即,只等待信號屏蔽字之外的信號
6) 使用sigaction結構中的sa_flags標志
sigaction函數的參數使用struct sigaction
struct sigaction結構有sa_flags標志:
SA_RESETHAND 指定的信號處理函數執行之後,
把該信號的響應行為重置為默認行為SIG_DFL
SA_NOCLDSTOP 子進程停止時,不產生SIGCHLD信號
SA_NODEFER 默認情況下,當某信號的處理函數正在執行時,
該信號將被添加到進程的信號屏蔽字中,
以防止當前信號處理函數沒有處理完時,
如果又發生該信號而導致該信號處理函數再次運行。
該信號的處理函數執行完之後,再把該信號從該進程的信號屏蔽字中刪除。
以上默認設置的原因: 如果信號處理函數是“不可重入”的,則被打斷後重新執行,將產生錯誤! 以上默認設置,可避免同一信號反復出現而使該信號處理函數, 在未執行完的情況下反復執行。 如果使用SA_NODEFER,將使得該信號不會添加到信號屏蔽字中。 SA_RESTART 默認情況下,可中斷的Linux的系統調用在執行期間, 如果收到一個信號,則該系統調用將返回一個錯誤, 並設置errno為EINTR 如果使用SA_RESTART, 則該信號處理函數執行完之後, 被該信號中斷的系統調用將被重新執行, 而不是簡單地返回一個錯誤。
函數的“可重入”
在信號處理函數中,應只能調用“可重入”的函數。
因為信號處理函數在執行期間,很可能被中斷。
不可重入的函數在上次運行未完成就結束後,
當再次運行時,就可能發生問題!
例如 :printf函數就是不可重入的。
所以在信號處理函數中,不應調用printf
解決辦法: 在信號處理函數中,設置一個標志, 在信號處理函數之外去判斷這個標志, 根據這個標志的狀態來使用printf
附錄:
Linux的信號:
不可靠信號
1) SIGHUP
2) SIGINT
3) SIGQUIT
4) SIGILL
5) SIGTRAP
6) SIGABRT
7) SIGBUS
8) SIGFPE
9) SIGKILL
10) SIGUSR1
11) SIGSEGV
12) SIGUSR2
13) SIGPIPE
14) SIGALRM
15) SIGTERM
16) SIGSTKFLT
17) SIGCHLD
18) SIGCONT
19) SIGSTOP
20) SIGTSTP
21) SIGTTIN
22) SIGTTOU
23) SIGURG
24) SIGXCPU
25) SIGXFSZ
26) SIGVTALRM
27) SIGPROF
28) SIGWINCH
29) SIGIO
30) SIGPWR
31) SIGSYS
實時信號(可靠信號)
34) SIGRTMIN
35) SIGRTMIN+1
36) SIGRTMIN+2
37) SIGRTMIN+3
38) SIGRTMIN+4
39) SIGRTMIN+5
40) SIGRTMIN+6
41) SIGRTMIN+7
42) SIGRTMIN+8
43) SIGRTMIN+9
44) SIGRTMIN+10
45) SIGRTMIN+11
46) SIGRTMIN+12
47) SIGRTMIN+13
48) SIGRTMIN+14
49) SIGRTMIN+15
50) SIGRTMAX-14
51) SIGRTMAX-13
52) SIGRTMAX-12
53) SIGRTMAX-11
54) SIGRTMAX-10
55) SIGRTMAX-9
56) SIGRTMAX-8
57) SIGRTMAX-7
58) SIGRTMAX-6
59) SIGRTMAX-5
60) SIGRTMAX-4
61) SIGRTMAX-3
62) SIGRTMAX-2
63) SIGRTMAX-1
64) SIGRTMAX
沒有定義32和33