2010-04-29 78 views
1

所以我有一个程序创建一个子进程并执行一个命令(例如ls)。父母然后将使用管道发送和从孩子获得。当我从命令行自己输入命令时,这工作正常。C IPC等待孩子

但是,当输入来自文件时,看起来孩子没有足够的时间运行,并且从管道读取时我得到NULL - 即使会有信息来自它。

使用睡眠()的缺点,有没有更好的方法来确保孩子在尝试读取它之前已经运行?

非常感谢!

回答

2

如果没有设置你管文件描述符非阻塞的,那么就应该没有问题。任何对管道的读取都会阻塞,直到孩子产生输出;如果需要响应多个文件描述符(例如,来自用户的标准输入和来自子进程的管道),请使用select()poll()

我认为它是fgets()返回NULL。这表示文件结束(意味着孩子已经关闭了管道的末端),或者是错误。您可以使用feof()ferror()来检查其中哪些为真。在错误情况下使用perror()来查看错误实际是什么。

+0

应该提到文件描述符是非阻塞的。我这样做是因为当没有新数据时,从fgets()中读取数据被阻塞,导致程序永远等待。 – Gary 2010-04-30 00:15:26

+0

@Gary:在这种情况下,使用'select()'将文件描述符添加到您正在监视的文件描述符集中 - 例如,如果您想等待来自用户或来自管道的输入,请使用'select )'来监视stdin和管道。 – caf 2010-04-30 00:20:39

+0

在这种情况下,我只想从一个文件描述符输入而不是标准输入,所以我不认为我需要选择?基本上我只是想给孩子足够的时间来运行它的命令,如果它仍然是NULL,就把它当作错误来处理。 – Gary 2010-04-30 01:47:56

1

问题是你对孩子有多少控制?如果你自己写孩子,那么你可以让孩子在共享内存中设置一个标志,它已经开始(或触摸一个文件或任何你喜欢的痒痒),然后只有当你知道孩子在线运行它。

如果您对孩子没有控制权,仍然可以通过用自己的脚本包装孩子来实现上述解决方案,该脚本首先启动孩子,然后设置共享内存,这可以采用一个shell脚本如下

#!/bin/sh 

$CHILD 
my_app_that_set_the_flag 

最后另一种选择是继续等待aslong当你从管空,显然这会导致一个无限循环,但如果你不能garuntee,你总是会得到的东西管道

1

您的父线程需要等到孩子准备好再尝试重新广告来自管道。有几种方法可以做到这一点。

首先,您可以使用条件变量。声明两个线程都可以访问的变量并将其设置为0。准备父母阅读时,您的子线程会将其设置为1。父母将等待,直到变量变为1(使用诸如while(!condvar) sleep(1);之类的东西),然后它将从管道读取并将变量重置为0,以便孩子知道父母完成。

另一种选择是使用进程间通信的一种形式,如signals。与条件变量方法类似,子线程将执行其工作,然后在完成时向父线程发送信号。父线程将等待,直到它从管道读取信号之前接收到信号,然后它可以发回一个信号给孩子,表明它已完成。

编辑:既然你产卵子进程与fork,而不是与线程,您不能使用条件变量在这里(父母和孩子都会有它独立的副本)。

相反,您可以使用signal()kill()函数在进程之间发送信号。在分叉之前,使用getpid来存储父级PID的副本(用于子进程)。同时存储返回值fork,因为它将包含孩子的pid。

发送信号到其它进程,使用类似:

kill(parent_pid, SIGUSR1); 

接收过程需要建立一个信号处理程序。例如:

int signal_received = 0; 
void signal_handler(int signal_num) { 
    if (signal_num == SIGUSR1) 
     signal_received = 1; 
} 

signal(SIGUSR1, signal_handler); 

功能signal_handler现在将被自动调用每当过程接收信号编号SIGUSR1。你的线程会等待在一个循环中,看着这个变量使用类似的改变:

while (1) { // Signal processing loop 
    // Wait here for a signal to come in 
    while (!signal_received) { sleep(1); } 

    // Wake up and do something 
    read_from_pipe(); 
    ... 
    signal_received = 0; 
} 
+0

您可以简单地使用'select()'等待管道可读。 – caf 2010-04-29 23:53:06

+0

@caf:是的,但只是告诉你有什么需要阅读的东西。 select()'告诉你,你可以并不意味着你所有的数据都在那里。许多时候,等待所有内容都准备好被读取比读取内容时更容易,然后尝试将它重新组合在一起(特别是如果数据本身不能告诉你在哪里结束是)。 – bta 2010-04-29 23:59:30

+0

这似乎是最简单的解决方案,所以我尝试了它 - 但无法让它工作。这个变量是在我做了fork之后被声明的,并且在孩子中发生了变化,但是父母似乎根本没有看到变化。这可能与我在儿童中使用exec有关吗? – Gary 2010-04-30 01:51:51