11

我有这段代码,我必须让程序块反复等待信号。我的老师希望我们使用 sigsuspend 和 mask 而不是暂停或睡眠。我不熟悉 sigsuspend 或掩码,我知道 sigsuspend() 用掩码给出的掩码临时替换调用进程的信号掩码,然后暂停进程,直到传递一个信号,其动作是调用信号处理程序或终止进程。但是我该如何实现它。

#include <stdlib.h>
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>

unsigned Conta = 0;

void mypause(int sign)
{
    switch (sign)
    {
        case SIGINT:
            printf("You've pressed ctrl-C\n");
            printf("I'm running waiting for a signal...\n");
            Conta++;
            break;

        case SIGQUIT:
            printf("You've pressd ctrl-\\n");
            printf("Number of times you've pressed CTRL-C: %d", Conta); 
            exit(0);
            break;
    }
}

int main()
{
    alarm(3);
    printf("I'm Alive\n");

    signal(SIGINT, mypause);
    signal(SIGQUIT, mypause);
    printf("I'm running, waiting for a signal...\n");
    while (1)
    {
    }

    return (0);
}
4

3 回答 3

12

开始的简单方法是sigsuspend使用空掩码调用(即任何信号都可以唤醒程序):

sigset_t myset;
(void) sigemptyset(&myset);
while (1) {
    (void) printf("I'm running, waiting for a signal...\n");
    (void) sigsuspend(&myset);
}

下一步是sigprocmask用来禁止两个已处理信号 (SIGINTSIGQUIT) 的发生,而不是在sigsuspend. 然后,您将在挂起时使用从中获得的“旧掩码”sigprocmask而不是空掩码。

于 2013-04-16T16:14:43.487 回答
9

GNU libc 手册的描述似乎清晰透彻:

功能: int sigsuspend (const sigset_t *set)

该函数用 set 替换进程的信号掩码,然后暂停进程,直到传递一个信号,该信号的动作是终止进程或调用信号处理函数。换句话说,程序被有效地挂起,直到不是集合成员的信号之一到达。

如果通过传递调用处理函数的信号唤醒进程,并且处理函数返回,则 sigsuspend 也返回。

仅当 sigsuspend 处于等待状态时,掩码才会保持设置。函数 sigsuspend 总是在返回时恢复之前的信号掩码。

返回值和错误条件与暂停相同。

使用 sigsuspend,您可以用完全可靠的东西替换上一节中的暂停或睡眠循环:

 sigset_t mask, oldmask;

 ...

 /* Set up the mask of signals to temporarily block. */
 sigemptyset (&mask);
 sigaddset (&mask, SIGUSR1);

 ...

 /* Wait for a signal to arrive. */
 sigprocmask (SIG_BLOCK, &mask, &oldmask);
 while (!usr_interrupt)
   sigsuspend (&oldmask);
 sigprocmask (SIG_UNBLOCK, &mask, NULL);

最后一段代码有点棘手。这里要记住的关键点是,当 sigsuspend 返回时,它将进程的信号掩码重置为原始值,即调用 sigsuspend 之前的值——在这种情况下,SIGUSR1 信号再次被阻塞。对 sigprocmask 的第二次调用是显式解除阻塞此信号所必需的。

还有一点:您可能想知道为什么需要 while 循环,因为程序显然只等待一个 SIGUSR1 信号。答案是传递给 sigsuspend 的掩码允许通过传递其他类型的信号(例如作业控制信号)唤醒进程。如果进程被未设置 usr_interrupt 的信号唤醒,它只会再次挂起自己,直到“正确”类型的信号最终到达。

这种技术需要多做几行准备,但对于您要使用的每种等待标准,只需要一次。实际等待的代码只有四行。

于 2013-04-16T16:11:03.097 回答
1

-通过使用 sigsuspend() API,进程可以暂停其执行,直到从一组信号中传递出任何一个信号。

  • 等待信号的干净可靠的方法是阻止它,然后使用 sigsuspend。

  • 通过在循环中使用 sigsuspend,可以等待某种信号,同时允许其他类型的信号由其处理程序处理。

  • 它允许进程暂停其执行,直到接收到一个不存在于掩码指向的集合中的信号。

  • 语法:int sigsuspend(sigset_t *mask);

  • 它暂时用新的掩码替换进程当前掩码,并在信号处理程序返回后恢复。

  • 由于 sigsuspend() 无限期地暂停线程执行,因此没有成功完成返回值,失败时返回 -1。

该程序确保程序在临界区执行时阻塞 SIGKILL 和 SIGTERM 信号,并在发出 SIGINT 信号并执行其相应的信号处理程序后恢复执行 */

     #include <stdio.h>
     #include <string.h>
     #include <errno.h>
     #include <signal.h>


     int cs = 0;
     int value = 5;

     /* This is the function called on receipt of signal(cntl+c) */
     void sig_handler (int sig)
     {
        printf("\n This is signal handler for signal %d\n", sig);
        cs =1;
     }

    int main()
    {
        struct sigaction act;
        act.sa_handler = sig_handler;
        sigemptyset (&act.sa_mask);
        sigaddset (&act.sa_mask, SIGPIPE);
        act.sa_flags = 0;

        sigset_t sig_term_kill_mask;
        sigaddset (&sig_term_kill_mask, SIGTERM);
        sigaddset (&sig_term_kill_mask, SIGKILL);

        sigaction (SIGINT, &act, NULL);

        /* Critical section */
        value++;
        printf ("\n Value is %d\n", value);
        while (cs == 0)
            sigsuspend (&sig_term_kill_mask);

        printf("\n Return from suspended successful\n");
        int ret = sigismember (&act.sa_mask, SIGPIPE);
        if (ret)
            printf("\n Old Mask that is SIGPIPE is restored after return from sigsuspend\n");
        else
            printf("\n Old Mask that is SIGPIPE failed to restored after return from sigsuspend\n");

         return 0; }

输出:

gcc sigsuspend.c

./a.out

值为 6 ^C 这是信号 2 的信号处理程序

从暂停成功返回

从 sigsuspend 返回后恢复作为 SIGPIPE 的旧掩码

于 2021-04-26T20:25:21.733 回答