📜  C语言信号

📅  最后修改于: 2021-05-25 21:08:09             🧑  作者: Mango


先决条件:前叉系统调用,等待系统调用
信号是由软件生成的中断,由于用户按下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将执行进程代码中的例程来处理信号。

默认信号处理程序

有几个默认的信号处理程序例程。每个信号都与这些默认处理程序例程之一相关联。不同的默认处理程序例程通常具有以下操作之一:

  • 点火:忽略信号;也就是说,什么都不做,只是返回
  • 期限:终止流程
  • 续:解除阻止已停止的进程
  • 停止:阻止进程
// CPP program to illustrate
// default Signal Handler
#include
#include
  
int main()
{
    signal(SIGINT, handle_sigint);
    while (1)
    {
        printf(“hello world\n”);
        sleep(1);
    }
    return 0;
}

输出:无限次打印问候世界。如果由于发送了SIGINT信号而用户按下ctrl-c终止了该进程,并且其默认处理程序终止了该进程。

hello world   
hello world         
hello world         
terminated          

用户定义的信号处理程序

进程可以通过其用户自己的处理程序函数替换几乎所有信号的默认信号处理程序(但不能替换SIGKILL)。
信号处理程序函数可以具有任何名称,但必须具有返回类型void和一个int参数。
示例:您可以为SIGCHLD信号(终止子进程)的信号处理程序选择名称sigchld_handler。那么声明将是:

void sigchld_handler(int sig);

执行信号处理程序时,传递给它的参数是信号的编号。程序员可以使用相同的信号处理程序函数来处理多个信号。在这种情况下,处理程序将需要检查参数以查看发送了哪个信号。另一方面,如果信号处理程序函数仅处理一个信号,则不必费心检查该参数,因为它始终是该信号编号。

// CPP program to illustrate
// User-defined Signal Handler
#include
#include
  
// 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
#include
#include
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
#include
#include
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
#include
#include
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) 
想要从精选的最佳视频中学习和练习问题,请查看《基础知识到高级C的C基础课程》。