三十三、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 给覆盖。