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

 

posted @ 2018-12-30 21:53  游戏进行中  阅读(402)  评论(0编辑  收藏  举报