1. 程式人生 > >三十三、Linux 程序與訊號——中斷系統呼叫和函式可重入性

三十三、Linux 程序與訊號——中斷系統呼叫和函式可重入性

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 給覆蓋。