信号是由软件生成的中断, 由于用户按下ctrl-c或其他进程向该进程告知某些信息, 因此OS会将其发送给进程。
有可以发送到进程的固定信号集。信号由整数标识。
信号编号具有符号名称。例如
SIGCHLD
是子级终止时发送到父级进程的信号编号。
例子:
#define SIGHUP 1 /* Hangup the process */
#define SIGINT 2 /* Interrupt the process */
#define SIGQUIT 3 /* Quit the process */
#define SIGILL 4 /* Illegal instruction. */
#define SIGTRAP 5 /* Trace trap. */
#define SIGABRT 6 /* Abort. */
信号的OS结构
- 对于每个过程, 操作系统都维护2个整数, 并且每个比特对应于一个信号编号。
- 这两个整数跟踪:待处理信号和阻塞信号
- 使用32位整数, 最多可以表示32个不同的信号。
范例:
在下面的示例中, SIGINT(= 2)信号被阻止, 并且没有待处理的信号。
将信号发送到进程, 将进程的未决信号整数中的相应位设置为1。每次OS选择要在处理器上运行的进程时, 都会检查未决和阻止的整数。如果没有信号待处理, 则该过程将正常重启, 并在下一条指令处继续执行。
如果有1个或多个信号待处理, 但每个信号都被阻止, 则该过程也会正常重启, 但信号仍标记为待处理。如果有1个或多个信号待处理且未被阻止, 则OS将执行进程代码中的例程来处理信号。
默认信号处理程序
有几个默认的信号处理程序例程。每个信号都与这些默认处理程序例程之一相关联。不同的默认处理程序例程通常具有以下操作之一:
- Ign:忽略信号;也就是说, 什么也不做, 只是返回
- Term:终止流程
- Cont:解除阻止已停止的进程
- Stop:阻止进程
// CPP program to illustrate
// default Signal Handler
#include<stdio.h>
#include<signal.h>
int main()
{
signal (SIGINT, handle_sigint);
while (1)
{
printf ("hello world\n");
sleep(1);
}
return 0;
}
输出:无限次打印问候世界。如果用户由于以下原因按ctrl-c终止进程, SIGINT发送的信号及其默认处理程序以终止该过程。
hello world
hello world
hello world
terminated
用户定义的信号处理器
进程可以通过其用户自己的处理程序功能替换几乎所有信号的默认信号处理程序(但不能替换SIGKILL)。
信号处理程序函数可以具有任何名称, 但必须具有返回类型void和一个int参数。
例子:
你可以为信号处理程序选择名称sigchld_handler
SIGCHLD
信号(终止子进程)。那么声明将是:
void sigchld_handler(int sig);
执行信号处理程序时, 传递给它的参数是信号编号。程序员可以使用相同的信号处理函数来处理多个信号。在这种情况下, 处理程序将需要检查参数以查看发送了哪个信号。另一方面, 如果信号处理程序功能仅处理一个信号, 则不必费心检查该参数, 因为它始终是该信号号。
// CPP program to illustrate
// User-defined Signal Handler
#include<stdio.h>
#include<signal.h>
// Handler for SIGINT, caused by
// Ctrl-C at keyboard
void handle_sigint( int sig)
{
printf ( "Caught signal %d\n" , sig);
}
int main()
{
signal (SIGINT, handle_sigint);
while (1) ;
return 0;
}
输出如下:
^CCaught signal 2 // when user presses ctrl-c
^CCaught signal 2
通过kill()发送信号
我们可以使用kill()向进程发送信号。
int kill(pid_t pid, int signal);
pid: id of destination process
signal: the type of signal to send
Return value: 0 if signal was sent successfully
例子:
pid_t iPid = getpid(); /* Process gets its id.*/
kill(iPid, SIGINT); /* Process sends itself a SIGINT signal
(commits suicide?)(because of SIGINT
signal default handler is terminate the process) */
问题
1.以下程序的输出是什么?
#include<stdio.h>
#include<wait.h>
#include<signal.h>
int main()
{
int stat;
pid_t pid;
if ((pid = fork()) == 0)
while (1) ;
else
{
kill(pid, SIGINT);
wait(&stat);
if (WIFSIGNALED(stat))
psignal(WTERMSIG(stat), "Child term due to" );
}
}
输出如下:
Child term due to: Interrupt
2.以下程序的输出是什么?
#include<stdio.h>
#include<signal.h>
#include<wait.h>
int val = 10;
void handler( int sig)
{
val += 5;
}
int main()
{
pid_t pid;
signal (SIGCHLD, handler);
if ((pid = fork()) == 0)
{
val -= 3;
exit (0);
}
waitpid(pid, NULL, 0);
printf ( "val = %d\n" , val);
exit (0);
}
输出如下:
val = 15
3.考虑以下代码。输出是什么?
#include<stdio.h>
#include<wait.h>
#include<signal.h>
pid_t pid;
int counter = 0;
void handler1( int sig)
{
counter++;
printf ( "counter = %d\n" , counter);
/* Flushes the printed string to stdout */
fflush (stdout);
kill(pid, SIGUSR1);
}
void handler2( int sig)
{
counter += 3;
printf ( "counter = %d\n" , counter);
exit (0);
}
int main()
{
pid_t p;
int status;
signal (SIGUSR1, handler1);
if ((pid = fork()) == 0)
{
signal (SIGUSR1, handler2);
kill(getppid(), SIGUSR1);
while (1) ;
}
if ((p = wait(&status)) > 0)
{
counter += 4;
printf ( "counter = %d\n" , counter);
}
}
输出如下
counter = 1 //(parent’s handler)
counter = 3 //(child’s handler)
counter = 5 //(parent’s main)
如果发现任何不正确的地方, 或者想分享有关上述主题的更多信息, 请写评论。