三十三、Linux 程序與訊號——中斷系統呼叫和函式可重入性
阿新 • • 發佈:2018-12-30
33.1 中斷系統呼叫
- 程序呼叫 “慢” 系統呼叫時,如果發生了訊號,核心會重啟系統呼叫。
- 慢系統呼叫
- 可能會永久阻塞的系統呼叫
- 從終端裝置、管道或網路裝置上的檔案讀取
- 向上述檔案寫入
- 某些裝置上的檔案開啟
- pause 和 wait 系統呼叫
- 一些裝置的 ioctl 操作
- 一些程序間通訊函式
33.1.1 慢系統呼叫引起的呼叫重啟
1 #include <unistd.h> 2 #include <signal.h> 3 #include <stdio.h> 4#include <stdlib.h> 5 6 7 void sig_handler(int signo) 8 { 9 if(signo == SIGTSTP){ 10 printf("SIGTSTP occured\n"); 11 } 12 } 13 14 int main(void) 15 { 16 char buffer[512]; 17 ssize_t size; 18 19 if(signal(SIGTSTP, sig_handler) == SIG_ERR){ 20 perror("signal sigtstp error"); 21 } 22 23 printf("begin running and waiting for signal\n"); 24 size = read(STDIN_FILENO, buffer, 512); 25 if(size < 0){ 26 perror("read error"); 27 } 28 29 printf("reading finished\n"); 30 31 if(write(STDOUT_FILENO, buffer, size) != size) {32 perror("write error"); 33 } 34 printf("end running\n"); 35 return 0; 36 }
33.1.2 自定義函式
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <signal.h> 4 #include <unistd.h> 5 6 void sig_handler(int signo) 7 { 8 if(signo == SIGTSTP){ 9 printf("SIGTSTP occured\n"); 10 } 11 } 12 13 void call_fun(void) 14 { 15 printf("begin running call_fun\n"); 16 sleep(10); 17 printf("end running call_fun\n"); 18 } 19 20 int main() 21 { 22 if(signal(SIGTSTP, sig_handler) == SIG_ERR){ 23 perror("signal sigtstp error"); 24 } 25 26 printf("begin running main\n"); 27 call_fun(); 28 printf("end running main\n"); 29 }
33.2 函式可重入性
- 在呼叫某個函式過程中出現訊號,且該訊號處理函式中再次呼叫該函式
- 訪問全域性或靜態變數的函式是不可重入函式
- 即前後資料不一致
- 若是函式內部的區域性變數,則此函式是可重入函式
- 即前後資料一致
- 訪問全域性或靜態變數的函式是不可重入函式
- 程式片段如下:
1 #include <signal.h> 2 #include <stdio.h> 3 #include <stdlib.h> 4 #include <unistd.h> 5 6 int g_v[10]; 7 int *h_v; ///< 堆中變數 8 9 void set(int val) 10 { 11 int a_v[10]; 12 13 int i = 0; 14 for(; i < 10; i++) { 15 a_v[i] = val; 16 g_v[i] = val; 17 h_v[i] = val; 18 sleep(1); 19 } 20 21 printf("g_v:"); 22 for(i = 0; i < 10; i++){ 23 if(i != 0) { 24 printf(", %d", g_v[i]); 25 } 26 else { 27 printf(", %d", g_v[i]); 28 } 29 } 30 printf("\n"); 31 32 printf("h_v:"); 33 for(i = 0; i < 10; i++){ 34 if(i != 0) { 35 printf(", %d", h_v[i]); 36 } 37 else { 38 printf(", %d", h_v[i]); 39 } 40 } 41 42 printf("\n"); 43 printf("a_v:"); 44 for(i = 0; i < 10; i++){ 45 if(i != 0) { 46 printf(", %d", a_v[i]); 47 } 48 else { 49 printf(", %d", a_v[i]); 50 } 51 } 52 } 53 54 void sig_handler(int signo) 55 { 56 if(signo == SIGTSTP){ 57 printf("SIGTSTP occured\n"); 58 set(20); 59 printf("\nend SIGTSTP\n"); 60 } 61 } 62 int main(void) 63 { 64 if(signal(SIGTSTP, sig_handler) == SIG_ERR){ 65 perror("signal sigtstp error"); 66 } 67 68 h_v = (int *)calloc(10, sizeof(int)); 69 70 printf("begin running main\n"); 71 set(10); 72 printf("\nend running main\n"); 73 return 0; 74 }
執行結果:
全域性變數中的資料不可控,區域性變數都為 10
第一次呼叫 set 函式的時候,set(10) 中的迴圈運行了 2 次,此時 i = 2,然後中斷,再次執行set(20) 函式,此時將所有變數中的值覆蓋掉了,中斷完了之後,函式繼續從中斷的地方執行,此時中斷的地方 i = 2,則全域性變數和堆變數從此處開始執行,後面的 20 都被 10 給覆蓋。