【问题标题】:process synchronization with signals deadlock case进程同步与信号死锁情况
【发布时间】:2018-04-05 09:12:29
【问题描述】:

我需要同步父进程和子进程以在无限循环中按顺序工作,但一段时间后我遇到了死锁。

我不允许使用睡眠,等待,我必须实现与信号的同步。经过搜索,我发现了一个使用暂停方法的类似问题,在答案中建议使用 sigsuspend 而不是暂停。所以我不确定我的实现是否有问题,或者我需要使用另一种方式来避免死锁

这是我的代码

#include <signal.h>
#include <unistd.h>
#include <stdio.h>
#include <time.h>

void catcher( int sig ) {
    printf( "inside catcher() function\n" );
}

void timestamp( char *str ) {

    time_t t;

    time( &t );
    printf( "%s the time is %s\n", str, ctime(&t) );
}


int main( int argc, char *argv[] ) {

    struct sigaction sigact;
    sigset_t block_set1,block_set2;

    sigfillset( &block_set1 );
    sigfillset( &block_set2 );
    sigdelset( &block_set1, SIGUSR1 );
    sigdelset( &block_set2, SIGUSR2 );

    sigemptyset( &sigact.sa_mask );
    sigact.sa_flags = 0;
    sigact.sa_handler = catcher;
    sigaction( SIGUSR1, &sigact, NULL );
    sigaction( SIGUSR2, &sigact, NULL );

    pid_t child_id;
    child_id = fork ();
    if (child_id == 0){

    while(1){
        printf("child send signal\n");
        kill (getppid (), SIGUSR1);

        printf("child wait signal\n");
        sigsuspend( &block_set2 );
        printf("child is going to die\n");
    }


    }else{
        while(1){
            timestamp( "before sigsuspend()" );
            sigsuspend( &block_set1 );
            timestamp( "after sigsuspend()" );

            kill (child_id, SIGUSR2);

            printf("parent is going to die\n");
        }
    }




    return( 0 );
}

【问题讨论】:

  • 家庭作业?阅读条件变量。
  • printf() 在信号处理函数中是导致死锁的好方法。在基于 POSIX 的系统上,您只能从信号处理程序中安全地调用异步信号安全函数。每the C standard:“行为未定义如果信号处理程序...调用标准库中的任何函数除了中止函数、_Exit 函数、quick_exit函数,或第一个参数等于导致调用处理程序的信号对应的信号编号的信号函数。"

标签: c synchronization signals deadlock


【解决方案1】:

这个程序有一个问题,考虑这样一个场景,孩子/父母甚至在他们使用 sigsuspend() 暂停执行之前就向父母/孩子发送信号。在这种情况下,将执行处理程序,稍后将不会执行任何操作 他们中断 sigsupend() 可能会出现无限挂起状态。

引用手册页

int sigsuspend(const sigset_t *mask);

sigsuspend() 临时替换调用的信号掩码 使用由 mask 给出的掩码进行处理,然后暂停该进程 直到传递一个信号,其动作是调用信号处理程序 或终止进程。

我建议改用 sigwaitinfo(),

int sigwaitinfo(const sigset_t *set, siginfo_t *info);

sigwaitinfo() 暂停调用线程的执行,直到其中之一 集合中的信号未决(如果集合中的信号之一已经 等待调用线程,sigwaitinfo() 将返回 立即。)

请找到相同的示例程序。

#include <signal.h>
#include <unistd.h>
#include <stdio.h>
#include <time.h>

void timestamp( char *str ) {

        time_t t;

        time( &t );
        printf( "%s the time is %s\n", str, ctime(&t) );
}


int main( int argc, char *argv[] ) {

        sigset_t parent_set, child_set;
        sigset_t set;
        siginfo_t info;

        /*Mask SIGUSR2 and SIGUSR1 signal, you can choose this mask
         * according to your requirement*/
        sigemptyset(&set);
        sigaddset(&set, SIGUSR1);
        sigaddset(&set, SIGUSR2);
        sigprocmask(SIG_SETMASK, &set, NULL);

        sigemptyset(&child_set);
        sigaddset(&child_set, SIGUSR2);

        sigemptyset(&parent_set);
        sigaddset(&parent_set, SIGUSR1);

        pid_t child_id;
        child_id = fork ();
        if (child_id == 0){

                while(1){
                        printf("child send signal\n");
                        kill (getppid (), SIGUSR1);

                        printf("child wait signal\n");
                        /* Child will suspend its execution if parent did not notify it
                         * using signal(SIGUSR2), if parent has already notified sigwaitinfo()
                         * will return immediately
                         **/
                        sigwaitinfo(&child_set, &info );
                        printf("child is going to die\n");
                }


        }else{
                while(1){
                        timestamp( "before sigwaitinfo()" );
                        /* Parent will suspend its execution if child did not notify it
                         * using signal(SIGUSR1), if child has already notified sigwaitinfo()
                         * will return immediately
                         **/
                        sigwaitinfo(&parent_set, &info );
                        timestamp( "after sigwaitinfo()" );

                        kill (child_id, SIGUSR2);

                        printf("parent is going to die\n");
                }
        }
}

希望对你有帮助。

【讨论】:

    【解决方案2】:

    问题,正如@monc noted,是一个进程在另一个进程没有准备好时向另一个进程发出信号。

    因此,在调用sigsuspend() 之前传递信号(即调用sigaction() 处理程序),因此传递不能中断sigsuspend()。会发生这样的事情:

    while(1){
        printf("child send signal\n");
        kill (getppid (), SIGUSR1);
                                        // SIGUSR2 delivered here
        printf("child wait signal\n");  //  or here
                                        //  or here
        sigsuspend( &block_set2 );      // wait forever...
        ...
    }
    

    解决方案是从一开始就阻止(屏蔽)父级中的 SIGUSR1 和子级中的 SIGUSR2。阻止信号将阻止传递,使它们保持等待状态,直到sigsuspend() 原子地换出信号掩码以允许传递(和中断)。为了安全起见,在调用fork()之前应该屏蔽信号,并且子进程可以从父进程继承信号掩码:

    sigemptyset(&ss);
    sigaddset(&ss, SIGUSR1);
    sigaddset(&ss, SIGUSR2);
    sigprocmask(SIG_BLOCK, &ss, NULL);
    
    ... fork();
    
    // child
    while (1) {
      sigset_t wakemask;
      sigemptyset(&wakemask);
      ...
      sigsuspend(&wakemask);
    }
    
    // similarly for parent ...
    

    请注意,在上述情况下,父母和孩子都阻止了这两个信号,包括它们相互发送的信号。这没有必要——您可以在fork() 之后立即取消阻止——但也无害。

    此外,您还可以使用sigwait 系列函数接受而不是传递信号。在这种情况下,您不需要使用sigaction 设置处理程序,但您仍然需要阻止信号。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-03-20
      • 2012-03-20
      • 2014-07-04
      • 1970-01-01
      相关资源
      最近更新 更多