【问题标题】:why my signal handler wait no just child process?为什么我的信号处理程序不等待子进程?
【发布时间】:2018-03-19 17:00:41
【问题描述】:

我的代码是一个多进程并发服务器进程,它使用系统V消息队列与客户端进程通信,一个客户端有一个子进程。首先我想要等待不再使用的子进程.当我用 SIG_IGN 设置 SIGCHLD 处理程序时,程序可以正常工作,但当我捕获 SIGCHLD 时总是出错,错误是客户端进程在 mesrcv 系统调用中阻塞,这意味着服务器不向客户端发送消息.第二当我进入^\退出我的客户端进程时,服务器进程退出了,我把它变成了一个守护进程,让它永远在后台运行,所以我想也许 waitpid 调用本身就是等待?虽然我认为这是不可能的

//this is signal handler
void handler(int sig){
    waitpid(-1,NULL,WNOHANG);
}
//in main
//my first step is make it become a daemon(fork twice)
//my first step is using record lock to ensure only one running
//then set signal handler and process message send by client
if(signal(SIGCHLD,SIG_IGN)==SIG_ERR){
    //because its a daemon,so no tty and redirct stdin,stdout,stderr to /dev/null
    syslog(LOG_ERR|LOG_USER,"set signal handler failed");
    return errno;
}
//now process the client request ,client message contant a client sysV message queue id and a filename,server report the file whether exist
int pid;
while((rcv_size=msgrcv(srvmqid,&srvbuf,1024,0)!=-1){
    if(0==rcv_size)
        continue;
    if((pid=fork())<=0){
        clibuf.mtype=srvbuf.mtype;
        climsqid=strtol(srvbuf.filename,&filename,10);
        if((fd=open(filename,O_RDONLY)==-1)
            snprintf(clibuf.filename,"file doesn't exist\n");
        else{
            snprintf(clibuf.filename,"file exist\n");
            close(fd);
        }
        if(msgsnd(climsqid,&clibuf,1024,0)==-1)
            syslog(LOG_ERR,"send message to client pid:%d failed,srvbuf.mtype);
        if(pid==0)    //if pid<0,then no child process is created
            exit(0);
    }

}

客户端进程的核心代码如下

int main(int argc,char*argv[]){
//first ,i create the client message queue and open public serve message queue,then send struct msgbuf struct to server,the mtype is pid,the buffer behind mtype is composed by client message queue key and filename(no space between them)
while(1){
if(msgsnd(sermsqid,&sndbuf,1024,0)!=-1){
    if(msgrcv(climsqid,&rcvbuf,1024,0,0)!=-1)
        printf("type:%ld,file state:%s\n",rcvbuf.mtype,rcvbuf.filename);
    else
        printf("receive message failed\n");
}
printf("input a filename you want to search:(^e to quit)");
fgets(filename,1024,stdin);
if(filename[0]==5)//^e is 5
    break;
filename[strlen(filename)-1)='\0';
snprintf(sndbuf.filename,1024,"%d%s",climsqid,filename);
}
msgctl(climsqid,IPC_RMID,NULL);
return errno;
}

【问题讨论】:

  • snprintf(clibuf.filename,"file doesn't exist\n"); 这里缺少大小参数。 (你的编译器至少应该产生一个警告)
  • 对不起,这是我的拼写错误。我想修复,但它似乎不支持修改。我的客户端和服务器程序可以编译成功
  • 不应编译。更改编译器标志。对于 gcc,添加 -Wall -Wpedantic
  • 你误会了我的意思,在我的电脑里这个拼写是正确的,我只是在网站上拼错了。对不起,我不擅长英语,希望你能明白我的意思说

标签: c unix signals message-queue


【解决方案1】:

此代码不允许错误处理或在中断后重新启动调用:

while((rcv_size=msgrcv(srvmqid,&srvbuf,1024,0)!=-1){
    ...

您没有正确处理 msgrcv() 被中断并需要再次调用。

the POSIX msgrcv() documentation:

错误

如果出现以下情况,msgrcv() 函数将失败:

...

[EINTR] msgrcv() 函数被信号中断。

注意the Linux msgrcv() man page 状态:

如果请求类型的消息不可用且IPC_NOWAIT 不可用 在msgflg 中指定,调用进程被阻塞,直到其中一个 出现以下情况:

  • 所需类型的消息被放入队列中。

  • 消息队列已从系统中删除。在这种情况下,系统调用失败,errno 设置为 EIDRM

  • 调用进程捕获到一个信号。在这种情况下,系统调用失败,errno 设置为EINTR。 (msgrcv() 永远不会自动 在被信号处理程序中断后重新启动,无论 建立信号时SA_RESTART标志的设置 处理程序。)

这种风格的代码实现了什么:

while((rcv_size=msgrcv(srvmqid,&srvbuf,1024,0)!=-1){

所以你节省了几行代码。如果你卖的是一本包含大量代码的教科书,节省的代码行意味着每本书少几页,节省几分钱。复制足够多,就足以支付出版商的游泳池费用。

将所有内容都塞进这样的条件句中真的非常糟糕。显式使用多行代码并没有什么好处:

for ( ;; )
{
    ssize_t rcv_size;
    do
    {
        errno = 0;
        rcv_size = msgrcv(...);
    }
    while ( ( -1 == rcv_size ) && ( EINTR == errno ) );

    if ( -1 == rcv_size )
    {
        perror( "msgrcv()" );
        break;
    }
    else if ( 0 == rcv_size )
    {
        continue;
    }
    ...
}

【讨论】:

  • 非常感谢。现在它工作正常了。我真的忽略了msgrcv被SIGCHLD中断的情况。我会记住你的建议。真诚感谢
猜你喜欢
  • 1970-01-01
  • 2019-07-31
  • 1970-01-01
  • 2014-12-09
  • 2012-01-29
  • 1970-01-01
  • 2019-08-04
  • 1970-01-01
相关资源
最近更新 更多