二、不可靠信号安装和发送函数。
1.
名称:: |
signal |
功能: |
信号安装(设置信号关联动作) |
头文件: |
#include <signal.h> |
函数原形: |
typedef void (*sighandler_t)(int); sighandler_t signal(int signum,sighandler_t handler); |
参数: |
signum 信号名 handler 操作方式 |
返回值: |
成功则为以前的信号处理配置,若出错则为SIG_ERR |
signum参数是信号名,handler的值是:1。常数SIG_IGN:表示忽略此信号。2。SIG_DFL:表示接到此信号后的动作是系统默认动作。3。函数地址:表示我们捕捉此信号,并且调用用户设置的信号处理程序。当调用signal设置信号处理程序时,第二个参数是指向该函数的指针。
/*10_1.c*/ #include <stdio.h> #include <unistd.h> #include <signal.h>
main(){ signal(SIGINT,SIG_IGN); printf("hello!n"); sleep(10); printf("hellon"); } |
上面的代码忽略了SININT信号.
此程序执行会在屏幕上先打印一个“hello!”,然后睡眠10分钟。在此期间用户按ctrl+c没有任何反应,因为signal函数已将SIGINT信号(按ctrl+c会产生)设为忽略。
然后看下面的程序:
/*10_2.c*/ #include <stdio.h> #include <unistd.h> #include <signal.h>
void catch(int sig);
main(){ signal(SIGINT,catch); printf("hello!n"); sleep(10); printf("hello!n"); }
void catch(int sig){ printf("catch signaln"); }
|
当用户按下ctrl+c时,进程被中断,catch()被执行.中断处理函数处理完毕后,转回断点执行下面的指令.
当编写自己的中断处理函数时,注意下面两点:
1.信号不能打断系统调用.
2.信号不能打断信号处理函数.
2.
名称:: |
pause |
功能: |
等待信号 |
头文件: |
#include <unistd.h> |
函数原形: |
int pause(void); |
参数: |
无 |
返回值: |
-1,errno设置为EINTR |
pause函数使调用进程挂起直至捕捉到一个信号,pause才返回。在这种情况下,pause返回-1,errno设置为EINTR..
下面是一个例子:
/*10_3.c*/ #include <signal.h>
static void sig_usr(int signo);
int main() { if(signal(SIGUSR1,sig_usr)==SIG_ERR) perror(SIGUSR1); if(signal(SIGUSR2,sig_usr)==SIG_ERR) perror(SIGUSR2); while(1) pause(); }
static void sig_usr(int signo) { if(signo==SIGUSR1) printf(“received SIGUSR1\n”); else if(signo==SIGUSR2) printf(“received SIGUSR2\n”); else printf(“received signal %d\n”,signo); } |
pause();函数使调用进程挂起,直至捕捉到一个信号。
下面我们来运行一下:
#./10_1 & 在后台运行进程
[3] 18864
#.kill -USR1 18864 向该进程发送SIGUSR1
received SIGUSR1
# kill –USR2 18864 向该进程发送SIGUSR1
received SIGUSR2
# kill 18864 向该进程发送SIGTERM
[3]+ Terminated ./signal
可以看到当用户kill -USR1 18864的时候产生了SIGUSR1信号,signal 定义了处理此信号要调用sig_usr函数,所以就在屏幕上打印出received SIGUSR1。
shell自动将后台进程对中断和退出信号的处理方式设置为忽略。于是当按中断键时就不会影响到后台进程。如果没有执行这样的处理,那么当按中断键时,它不但会终止前台进程,还会终止所以的后台进程。
我们还应注意的是,我们不能在信号处理程序中调用某些函数,这些函数被称为不可重入函数,例如malloc,getpwnam..那是因为当发生中断的时候系统有可能正在执行这些函数,在中断中调用这些函数可能会覆盖原来的信息,因而产生错误。
3.
名称:: |
kill/raise |
功能: |
信号发送函数 |
头文件: |
#include <signal.h> |
函数原形: |
int kill(pid_t pid,int signo); int raise(int signo); |
参数: |
pid 进程id signo 信号 |
返回值: |
若成功返回0,若出错返回-1 |
kill函数将信号发送给进程或进程组。raise函数则允许进程向自己发送信号。
raise(signo)等价于kill(getpid(),signo);
kill的pid函数有4种不同的情况:
pid>0 将信号发送给进程ID为pid的进程。
pid==0将信号发送给与发送进程同一组的所有进程。
pid<0 将该信号发送给其进程组id等于pid绝对值。
pid==-1将信号发送给进程有权向它发送信号的系统上的所有进程。
进程将信号发送给其他进程需要许可权。超级用户可将信号发送另一个进程。对于非超级用户,其基本规则是发送者的实际或有效用户ID必须等于接收者的实际或有效用户ID。
/*10_4.c*/ #include <signal.h> #include <stdio.h>
void sig_usr(int);
main() { if(signal(SIGINT,sig_usr)==SIG_ERR) perror(“error”); while(1); }
void sig_usr(int signo) { if(signo==SIGINT) printf(“received SIGINT\n”); kill(getpid(),SIGKILL); } |
程序运行后,当用户按ctrl+c后,程序调用信号处理函数输出received SIGINT,然后调用kill函数中止进程。此程序的kill(getpid(),SIGKILL);也可以写成raise(SIGKILL);.