10.4 不可靠的信号
- 信号的不可靠 指的是signal的行为是不可靠。
- 比如有如下例子:按书上的意思是第二次点(ctrl+c)会不调用int_hanlder(),会调用默认函数。但实测是:每次的(ctrl+c)都会调用int_handler()函数。。
#include <signal.h> void int_handler(int a) { write(1, "7", 1); } int main() { int i; signal( SIGINT, int_handler); //函数的调用是内核布置的。 //信号的不可靠指的是信号的行为不可靠:当收到SIGINT,然后内核调用int_handler函数,当内核第二次收到同样的信号,第二次的调用会恢复为默认调用 for (int i = 0; i < 10; i++) { write(1, "*", 1); sleep(1); } }
10.6 可重入函数:
第一次调用还没结束,又发生了第二次调用,不会出错的函数叫可重入的函数。
系统调用都是可重入的,一部分库函数也是可重入的:比如memcpy.. 尤其要注意返回值为指针的函数,如果是malloc出来的内存,重入可能就会有问题。
- 信号的响应过程:
10.9 函数kill和raise
- 函数kill
man 2 kill: kill是发送信号给一个进程
- kill的参数
pid > 0 : 杀死pid 进程。
pid = 0 :杀死调用kill所在进程组的所有进程。
pid = -1: 向所有除了init的进程发送信号,比如init()进程杀死所有进程。
pid < -1:会把进程组id为pid的进程组,全部发送这个信号。
- 函数raise(signo): 给调用raise()的线程发送signo
相当于 kill(getpid(), signo);
10.10 函数alarm和pause
- 函数alarm(sec); 在sec秒后会产生SIGALRM信号
alarm默认杀死进程:
int main() { alarm(5); // 5s后默认杀死进程 while (1) { } exit(0); }
当一个进程又多个alarm,会以最后,最短的时间发出SIGALRM信号
// 5s后杀死进程 int main() { alarm(5); // 默认杀死进程 // alarm(2); //超过一个alarm(),程序会在设定的alarm() 最后,时间最早的alarm被杀死 while (1) { } exit(0); }
慎重使用sleep(): 如果sleep通过alarm + pause来封装,如果程序中有alarm信号,就会产生异常。
- 定时5s的程序:用time()
// 5s的定时程序:用time()函数 int main() { time_t end; int64_t count = 0; end = time(NULL) + 5; while (time(NULL) <= end) { count++; } printf("%lld\n", count); } // time ./a.out 可以看到time调用的时间是不准的 // lbw@HP-ZHAN-66-Pro-15-G3:~/lbw/gitNote/chap10$ time ./a.out // 1761106842 // real 0m5.770s // user 0m5.769s // sys 0m0.000s
- 定时5s的程序:用alarm+signal
// 5s的定时程序:用alarm() + signal() // 同一个程序用gcc main.c -O1优化编译,如果不加volatile,会让程序进入死循环 // 而volatile的用处就体现出来了:让编译器对这个变量不在进行编译优化,这个时候在用gcc main.c -O1优化编译也不会出问题 volatile int loop = 1; void alarm_handler(int s) { loop = 0; } int main() { alarm(5); signal(SIGALRM, alarm_handler); int count = 0; while (loop) { count++; } printf("%lld\n", count); exit(0); } //打印结果如下 :可以看到时间精度很高 // lbw@HP-ZHAN-66-Pro-15-G3:~/lbw/gitNote/chap10$ time ./a.out // 2474397847 // real 0m5.002s // user 0m5.000s // sys 0m0.000s // lbw@HP-ZHAN-66-Pro-15-G3:~/lbw/gitNote/chap10$
注意:如果用优化编译,需要注意变量的定义要加上volatile
- 流控之漏桶
//漏桶: 流控:控制打印速度 alarm + signal volatile int loop = 0; void alarm_handler(int s) { alarm(1); loop = 1; } int main(int argc, char **argv) { signal(SIGALRM, alarm_handler); alarm(1); while (1) { while (!loop) pause(); loop = 0; printf("hello,hello\n"); } }
- 流控之令牌桶
//令牌桶:有权限一秒打印一次hello, //如果程序被阻塞了3s,分配给程序的资源:3s打印3次hello,那么解除阻塞之后,程序就会拥有打印3次hello的权限,于时可以打印3次。 // 相比于漏桶,不会白白浪费可使用资源。 volatile int token = 0; #define MAX 100 void alarm_handler(int s) { alarm(1); token++; //增加令牌 if (token > MAX) { token = MAX; } } int main(int argc, char **argv) { signal(SIGALRM, alarm_handler); alarm(1); while (1) { while (token <= 0) pause(); token--; //消耗令牌 // 模拟操作 printf("hello\n"); } }