进程同步信号死锁情况

时间:2018-04-05 09:12:30

标签: c synchronization signals deadlock

我需要将父进程和子进程同步以无限循环的顺序工作,但过了一段时间我就陷入僵局。

我不允许使用睡眠,等待,我必须实现与信号的同步。搜索后我找到了一个类似的问题,使用了暂停方法,在答案中建议使用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 );
}

2 个答案:

答案 0 :(得分:0)

这个程序有一个问题,考虑一下子/父在使用sigsuspend()暂停执行之前向父/子发送信号的情况。在这种情况下,处理程序将被执行,之后将不会有人 他们打断sigsupend()可能会导致无限暂停状态。

引用手册页

  

int sigsuspend(const sigset_t * mask);

     

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

我建议改用sigwaitinfo(),

  

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

     

sigwaitinfo()暂停执行调用线程,直到其中一个   set中的信号处于待处理状态(如果已设置其中一个信号   等待调用线程,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");
                }
        }
}

我希望这会对你有所帮助。

答案 1 :(得分:0)

问题,就像@monc noted一样,当另一个过程尚未准备好时,一个过程会向另一个过程发出信号。

因此,在调用sigaction()之前传递信号(即调用sigsuspend()处理程序),因此传递不能中断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设置处理程序,但仍需要阻止信号。