《TCP/IP网络编程》第 10 章 多进程服务器端 笔记( 五 )


#include #include #include void timeout(int sig) //信号处理器{if (sig == SIGALRM)puts("Time out!");alarm(2); //为了每隔 2 秒重复产生 SIGALRM 信号,在信号处理器中调用 alarm 函数}void keycontrol(int sig) //信号处理器{if (sig == SIGINT)puts("CTRL+C pressed");}int main(int argc, char *argv[]){int i;signal(SIGALRM, timeout); //注册信号及相应处理器signal(SIGINT, keycontrol);alarm(2); //预约 2 秒候发生 SIGALRM 信号for (i = 0; i < 3; i++){puts("wait...");sleep(100);}return 0;}
编译运行:
gcc signal.c -o signal./signal
结果:

《TCP/IP网络编程》第 10 章 多进程服务器端 笔记

文章插图
上述结果是没有任何输入的运行结果 。当输入 ctrl+c 时:
《TCP/IP网络编程》第 10 章 多进程服务器端 笔记

文章插图
就可以看到 CTRL+C的字符串 。
发生信号时将唤醒由于调用 sleep 函数而进入阻塞状态的进程 。
调用函数的主题的确是操作系统,但是进程处于睡眠状态时无法调用函数,因此,产生信号时,为了调用信号处理器,将唤醒由于调用 sleep 函数而进入阻塞状态的进程 。而且,进程一旦被唤醒,就不会再进入睡眠状态 。即使还未到 sleep 中规定的时间也是如此 。所以上述示例运行不到 10 秒后就会结束,连续输入 CTRL+C 可能连一秒都不到 。
简言之,就是本来系统要睡眠100秒,但是到了 alarm(2) 规定的两秒之后,就会唤醒睡眠的进程,进程被唤醒了就不会再进入睡眠状态了,所以就不用等待100秒 。如果把 () 函数中的 alarm(2) 注释掉,就会先输出wait...,然后再输出Time out! (这时已经跳过了第一次的 sleep(100) 秒),然后就真的会睡眠100秒,因为没有再发出 alarm(2) 的信号 。
10.3.3 利用函数进行信号处理
《TCP/IP网络编程》第 10 章 多进程服务器端 笔记

文章插图
前面所学的内容可以防止僵尸进程,还有一个函数,叫做函数,他类似于函数,而且可以完全代替后者,也更稳定 。之所以稳定,是因为:
函数在 Unix 系列的不同操作系统可能存在区别,但函数完全相同
实际上现在很少用函数编写程序,他只是为了保持对旧程序的兼容,下面介绍函数,只讲解可以替换函数的功能 。
#include int sigaction(int signo, const struct sigaction *act, struct sigaction *oldact);/*成功时返回 0 ,失败时返回 -1act: 对于第一个参数的信号处理函数(信号处理器)信息 。oldact: 通过此参数获取之前注册的信号处理函数指针,若不需要则传递 0*/
声明并初始化结构体变量以调用上述函数,该结构体定义如下:
struct sigaction{void (*sa_handler)(int);sigset_t sa_mask;int sa_flags;};
此结构体的成员保存信号处理的函数指针值(地址值) 。和的所有位初始化 0 即可 。这 2 个成员用于指定信号相关的选项和特性,而我们的目的主要是防止产生僵尸进程,故省略 。
下面的示例是关于函数的使用方法 。
#include #include #include void timeout(int sig){if (sig == SIGALRM)puts("Time out!");alarm(2);}int main(int argc, char *argv[]){int i;struct sigaction act;act.sa_handler = timeout;//保存函数指针sigemptyset(&act.sa_mask);//将 sa_mask 函数的所有位初始化成0act.sa_flags = 0;//sa_flags 同样初始化成 0sigaction(SIGALRM, &act, 0); //注册 SIGALRM 信号的处理器 。alarm(2); //2 秒后发生 SIGALRM 信号for (int i = 0; i < 3; i++){puts("wait...");sleep(100);}return 0;}