主进程select,子进程退出会产生EINTR信号么?

arrow0815 2010-10-05 01:35:27
while(1)
{
/*提示连接哪种服务*/
...
if((chpid = fork()) == 0)
{
close(pfd[0]);
dup2(pfd[1], 5);
close(pfd[1]);
execlp("xterm", "xterm", "-e", "./echo_cli", addr, (char *) 0);

} else {
FD_ZERO(&rset);
while(1)
{
maxfdp1 = max(pfd[0], fileno(stdin)) + 1;
FD_SET(pfd[0], &rset);
FD_SET(fileno(stdin), &rset);
select(maxfdp1, &rset, NULL, NULL, NULL);
if(FD_ISSET(pfd[0], &rset))
{
if((nread = read(pfd[0], buff, MAXLINE)) > 0)
{
buff[nread] = 0;
fputs(buff, stdout);
} else break;
}
if(FD_ISSET(fileno(stdin), &rset))
{
if((nread = read(fileno(stdin), buff, MAXLINE)) > 0)
fputs("Do not type in the parent window!\n", stdout);

}
else
{
if(errno == EINTR)
break;
}
}
}

void
sig_chld(int signo)
{
pid_t pid;
int stat;
char buff[MAXLINE];
while((pid = waitpid(-1, &stat, WNOHANG)) > 0)
{
sprintf(buff, "---Disconnected from server...---\n");
fputs(buff, stdout);
}
return;
}

我写了一个socket连接的代码,进入子进程后会产生一个xterm窗口与服务器进行通信,我希望在开启xterm窗口后,
1创建一个管道与夫进程通信,传输一些连接信息
2如果在parent继续输入,则会提示Do not type in the parent window!
3xterm通过ctrl D 或 ctrl C退出后,主进程回到外层的while循环继续询问要连接哪种服务


为了做到这三点,定义了信号处理函数来处理SIG_CHLD,一旦子进程退出就会打印---Disconnected from server...并
并在主进程里用select监听管道的输出端pfd[0]和stdin,哪个描述符产生了可读信息就打印


可是现在一旦退出xterm(子进程),父进程窗口就会停在---Disconnected from server...---处,不会返回外层的while(1)循环,我认为此时父进程应该还阻塞在为select写的内层while(1)里,可是如何通知主进程子进程已经退出呢?
可以通过判断errno==EINTR么?我在内层while(1)里加了判断errno的代码,但是没有什么用,请高手赐教
...全文
662 16 打赏 收藏 转发到动态 举报
写回复
用AI写文章
16 条回复
切换为时间正序
请发表友善的回复…
发表回复
arrow0815 2010-10-10
  • 打赏
  • 举报
回复
不管怎样谢谢大家的回答,现在这个作业已经交了,在看第二次的作业,以后有时间再来研究这个问题吧
快乐田伯光 2010-10-08
  • 打赏
  • 举报
回复
关于被信号中断的系统调用,是不是我理解错了?难道select和read在这种情况下不会因为子进程退出被中断?或者说被中断了它们不是返回的负值?

会被中断,会返回返值
快乐田伯光 2010-10-08
  • 打赏
  • 举报
回复
主进程select,子进程退出会产生EINTR信号么?

justkk 2010-10-08
  • 打赏
  • 举报
回复
[Quote=引用 11 楼 arrow0815 的回复:]我放在select里打印的“mlgb”也从来没有被打印过...[/Quote]
lz用了脏词..
arrow0815 2010-10-08
  • 打赏
  • 举报
回复
我试了各位提出的各种方法,justkk的方法很给力阿,我没有关掉父进程的管道入口,在之前加了一句close(pfd[1])就可以了正常退出了,我在想是不是因为进程阻塞在管道入口输入上了,所以退出后管道入口一直在等待输入。

可是这个并没有解决我的疑问,现在这个程序能够正常工作的原因是,我在if(FD_ISSET(pfd[0], &rset))中检查了read函数的返回值,read是从管道出口读取信息用的,一旦子进程ctrl c强行退出或者ctrl d都会触发socket发送一个fin到服务端,此时服务端读了fin则read返回值肯定是0,所以程序之所以能break,和sigchld没有关系,完全是因为读到了长度为0的数据才break的,我尝试测试select和read返回负数(即我理解的它们在阻塞的时候被sigchld中断了)的时候退出,但是从来不会发生这种情况,我放在select里打印的“mlgb”也从来没有被打印过...

..根据之前几个朋友的回复,我尝试了在各个select和read函数检查eintr错误,可是都似乎都没有检查到,我的理解是这样的,出现这个错误的原因是因为系统阻塞在慢系统调用上时,产生了sigchld信号。比如现在我阻塞在select()上,一个子进程退出,出现了sigcld信号,则select()被中断,返回一个负数,此时的errno被置为EINTR

关于被信号中断的系统调用,是不是我理解错了?难道select和read在这种情况下不会因为子进程退出被中断?或者说被中断了它们不是返回的负值?

还有EINTR这个值被定义在那个文件里面阿?我现在用的是ubuntu,是不是系统里没有这个定义?




close(pfd[1]);//差了这一句
FD_ZERO(&rset);
while(1)
{
/*
if(errno == EINTR)
break;
*/
maxfdp1 = max(pfd[0], fileno(stdin)) + 1;
FD_SET(pfd[0], &rset);
FD_SET(fileno(stdin), &rset);
if(select(maxfdp1, &rset, NULL, NULL, NULL) < 0) {
printf("mlgb\n");
/*
if(errno == EINTR) {
break;
}
*/

}
if(FD_ISSET(pfd[0], &rset))
{
if((nread = read(pfd[0], buff, MAXLINE)) > 0)
{
buff[nread] = 0;
fputs(buff, stdout);
}
else if(nread == 0)
break;
/*
else if(nread < 0)
break;
可是检查返回负数无法break,永远不会返回负数?(即永远不会因为阻塞而被sigchld中断?)
*/
}
if(FD_ISSET(fileno(stdin), &rset))
{
if((nread = read(fileno(stdin), buff, MAXLINE)) > 0)
fputs("Do not type in the parent window!\n", stdout);

}
}
justkk 2010-10-08
  • 打赏
  • 举报
回复
也许你的主进程退出太快,信号还没来得及触发
可以在退出之前sleep(1)看看效果..
gohome520 2010-10-05
  • 打赏
  • 举报
回复
vividonly说的是对的。
楼主原来的代码中,在select之后并未马上进行出错判断,而是先进行FD_ISSET(),这个时候,FD_ISSET()返回值是真,所以走到第一个if(FD_ISSET())分支中了,然后阻塞在分支中的read()上了。

但有一点疑问:
如果在main()函数中执行信号捕捉函数,则结果如上面所说的,阻塞在read()上。
如果在main()函数中去掉信号捕捉函数,则会阻塞在select()上。

搞不明白了。

[Quote=引用 7 楼 vividonly 的回复:]

根据5楼的描述,建议楼主在select处检测一个EINTR错误
while((retval = select(maxfdp1, &rset, NULL, NULL, NULL)) == -1 && errno == EINTR);

同样也对read做同样处理
while((nread = read(pfd[0], buff, MAXLINE) == -1 &am……
[/Quote]
justkk 2010-10-05
  • 打赏
  • 举报
回复
} else {
close(pfd[1]);
FD_ZERO(&rset);
while(1)
...
justkk 2010-10-05
  • 打赏
  • 举报
回复
刚才试验了一下
lz试试在父进程中首先调用close(pfd[1]);
...
} else {
close(pfd[1]);
FD_ZERO(&rset);
while(1)
vivi 2010-10-05
  • 打赏
  • 举报
回复
根据5楼的描述,建议楼主在select处检测一个EINTR错误
while((retval = select(maxfdp1, &rset, NULL, NULL, NULL)) == -1 && errno == EINTR);

同样也对read做同样处理
while((nread = read(pfd[0], buff, MAXLINE) == -1 && errno == EINTR);
vivi 2010-10-05
  • 打赏
  • 举报
回复
摘自UNP P115:
适用于慢系统调用的基本规则是:当阻塞于某一个慢系统调用的一个进程捕获某个信号且有相应信号处理函数返回时,该系统调用可能返回一个EINTR错误。

加了中断处理函数的情况下,应该对所有慢系统调用(accpet,select,read等)都检测一下EINTR错误,如果是的话,有些内核能够帮你自动重启这些函数,有的则不能。为了安全起见,我们应该手动重启这些函数(再循环中用continue而不是break)。
gohome520 2010-10-05
  • 打赏
  • 举报
回复
按楼主所给的代码片断以及所说的要求,我简单完整了一下代码。
用strace跟踪后,发现主进程是阻塞在select()函数这,用kill -17(SIGCHLD),发现程序也没有从select中跳出来。


#include <stdio.h>
#include <errno.h>
#include <signal.h>
#include <stdlib.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>

#define MAXLINE 1024
#define MAX(a, b) (a>b?a:b)

#define debug(fmt...) \
do {\
fprintf(stdout, "%s[%d] ", __FILE__, __LINE__);\
fprintf(stdout, fmt);\
}while(0)

void proc()
{
fd_set rset;
int pfd[2];
int maxfdp1;
int nread;
char buff[MAXLINE+1];
pid_t chpid;

pfd[0] = open("p1", O_RDWR);

//while(1)
{
if((chpid = fork()) == 0)
{
close(pfd[0]);
dup2(pfd[1], 5);
close(pfd[1]);
execlp("xterm", "xterm", "-e", "cat", ">", "p1", (char *)0);
}
else
{
FD_ZERO(&rset);
while(1)
{
maxfdp1 = MAX(pfd[0], fileno(stdin)) + 1;

FD_SET(pfd[0], &rset);
FD_SET(fileno(stdin), &rset);
select(maxfdp1, &rset, NULL, NULL, NULL);
if(FD_ISSET(pfd[0], &rset))
{
if((nread = read(pfd[0], buff, MAXLINE)) > 0)
{
buff[nread] = 0;
fputs(buff, stdout);
}
else
break;
}
if(FD_ISSET(fileno(stdin), &rset))
{
if((nread = read(fileno(stdin), buff, MAXLINE)) > 0)
fputs("Do not type in the parent window!\n", stdout);
}
else
{
if(errno == EINTR){
debug("===== 200 =====\n");
break;
}
}
}
}
}

close(pfd[0]);
return;
}
void sig_chld(int signo)
{
pid_t pid;
int stat;
char buff[MAXLINE];
while((pid = waitpid(-1, &stat, WNOHANG)) > 0)
{
debug("===== 100 =====\n");
sprintf(buff, "---Disconnected from server...---\n");
fputs(buff, stdout);
}
return;
}

int main(int argc, char *argv[])
{
//signal(SIGCHLD, sig_chld);

proc();

return 0;
}

justkk 2010-10-05
  • 打赏
  • 举报
回复
跟踪一下,看主进程停留在哪里..
guoxuqu 2010-10-05
  • 打赏
  • 举报
回复
while((pid = waitpid(-1, &stat, WNOHANG)) > 0) //
{
sprintf(buff, "---Disconnected from server...---\n");
fputs(buff, stdout);
}

waitpid 第一个参数不能是-1,不然会一直循环到所有子进程退出。
gohome520 2010-10-05
  • 打赏
  • 举报
回复
强烈建议贴代码用"插入代码",而不直接沾贴。

23,125

社区成员

发帖
与我相关
我的任务
社区描述
Linux/Unix社区 应用程序开发区
社区管理员
  • 应用程序开发区社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧