所以我有一个创建子进程并执行命令的程序(例如,ls)。然后,父母将使用管道发送给孩子并从孩子那里取得。当我从命令行输入命令时,这很好用。
但是,当输入来自文件时,似乎孩子没有足够的时间来运行,并且从管道读取时我得到NULL - 即使会有来自它的信息。
如果没有使用sleep(),是否有更好的方法可以确保孩子在尝试阅读之前已经运行了?
非常感谢!
答案 0 :(得分:2)
如果你没有将管道文件描述符设置为非阻塞,那么应该没有问题。管道上的任何读数都会阻塞,直到孩子产生输出;如果您需要响应多个文件描述符(例如,来自用户的标准输入和来自子进程的管道),请使用select()
或poll()
。
我假设fgets()
返回NULL。这表示文件结束(意味着孩子已关闭管道的末尾)或错误。您可以使用feof()
或ferror()
来检查哪些是真的。在错误情况下使用perror()
来查看错误实际是什么。
答案 1 :(得分:1)
问题是你对孩子有多少控制?如果您自己编写孩子,那么您可以让孩子在已经开始的共享内存中设置一个标志(或触摸一个文件或任何你想要的痒),然后只有在你知道孩子在线运行它时才会这样做。
如果您无法控制孩子,仍然可以通过使用您自己的脚本包装孩子来实现上述解决方案,该脚本首先启动子项然后设置共享内存。这可以是一种形式shell脚本如下
#!/bin/sh
$CHILD
my_app_that_set_the_flag
最后另一种选择是继续等待从管道中获取NULL,显然这会导致无限循环,但如果你不能指望你总会在管道上得到一些东西
答案 2 :(得分: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;
}