简体   繁体   English

线程和派生:从popen()-pipe读取时,fgetc()会阻塞

[英]Threads and fork: fgetc() blocks when reading from popen()-pipe

In a multithreaded program (running on ARM) I have 在多线程程序(在ARM上运行)中,我有

a main thread which, among other things, periodically checks with popen( "pidof -s prog" ) whether another program is running. 一个主线程 ,除其他外,该线程定期使用popen( "pidof -s prog" )是否正在运行另一个程序。 I use the O_CLOEXEC flag for the file descriptor and check whether fgetc() receives anything from the pipe. 我使用O_CLOEXEC标志作为文件描述符,并检查fgetc()是否从管道接收到任何东西。 Setting the file descriptor "non-blocking" results in nothing being read and won't help . 将文件描述符设置为“非阻塞”将导致不读取任何内容,也无济于事 The same pidof command from the shell command is performing fine. 与shell命令相同的pidof命令执行得很好。

In another thread , fork() with an immediate execl() in the child process is used to start an rsync operation whenever a specific event occurs. 在另一个线程中 ,每当发生特定事件时,使用子进程中具有直接execl() fork()即可启动rsync操作。 The parent uses a signal handler to observe the child status, and has the option to kill the child at another specific event. 父母使用信号处理程序来观察孩子的状态,并可以选择在另一个特定事件中杀死孩子。 It doesn't matter whether I invoke exec() with rsync or sleep - the result is the same. 我是否使用rsyncsleep调用exec()都没有关系-结果是相同的。

The problem is that the fgetc() in the main thread blocks until the child process terminates. 问题在于,主线程中的fgetc()阻塞,直到子进程终止。

I'll try to solve this problem by fork() ing early (at some point where the application is single-threaded, as supposed in another post which I started). 我将尽早通过fork()解决这个问题(在我开始撰写的另一篇文章中 ,应用程序是单线程的)。

But anyway: 但无论如何:

I'd like to understand what's causing the fgetc() to block when reading from the pipe. 我想了解是什么导致fgetc()从管道读取时阻塞。

A few things I've tried so far: 到目前为止,我已经尝试了一些方法:

  • I tried to reproduce the problem with a small example application that does what I've described above and hoped it would show the same erroneous behaviour, but unfortunately it works fine, which is why I do not provide any code here yet. 我试图通过一个小型示例应用程序来重现该问题,该示例应用程序执行了上述操作,并希望它能够显示相同的错误行为,但不幸的是,它可以正常工作,这就是为什么我现在未提供任何代码的原因。 Maybe I'm missing the relevant point. 也许我错过了相关要点。
  • Using the same rsync invocation via system() doesn't cause any issues 通过system()使用相同的rsync调用不会引起任何问题
  • I've had a look at a system() implementation and can see that the signals are manipulated before fork() ing: 我看过一个system() 实现 ,可以看到在fork()之前对信号进行了处理:

    • SIGCHLD is blocked SIGCHLD被阻止
    • SIGINT and SIGQUIT are ignored SIGINT和SIGQUIT被忽略

    I need the signal handler for SIGCHLD, but out of curiousity I tried to do the same as in the code from above (I replaced sigprocmask() with pthread_sigmask() ) - without any success, the behaviour stays the same. 我需要SIGCHLD的信号处理程序,但是出于好奇,我试图与上面的代码相同(我将sigprocmask()替换为pthread_sigmask() )-没有成功,行为保持不变。

    I couldn't find any implementation of system() in the sources provided with my BSP. 我的BSP随附的源代码中找不到system()任何实现。

The program opens other files via fstream - and without O_CLOEXEC (will be a bit cumbersome to change that ) 该程序通过fstream打开其他文件-并且没有O_CLOEXEC( 更改它会有点麻烦

Bugfix and explanation of unexpected behaviour 错误修正和意外行为的解释

Indeed I've missed the relevant point. 确实,我已经错过了相关的要点。 After adapting the sample program more to the original code example I've seen that the signal handler (which worked in a test program) was the issue. 在使示例程序更适合原始代码示例之后,我看到了信号处理程序(在测试程序中工作)的问题。 Excerpt: 摘抄:

void MyClass::sig_handler(int sig) {
    if( m_pid < 1 ) // not the child we're waiting for
        return;

    pid_t pid;
    int wstatus;

    while ((pid = waitpid( -1, &wstatus, WNOHANG )) != -1 ) {
        // error: this returns 0 as long as any children are alive
        // -> check for "> 0" to ignore active child processes
        if( pid != m_pid )
            return;
        // handle stuff here...
    }
}

I had to replace the following line 我不得不替换以下行

while ((pid = waitpid( -1, &wstatus, WNOHANG )) != -1 )

with

while ((pid = waitpid( -1, &wstatus, WNOHANG )) > 0 )

because the program's other threads fork() children (eg with popen() ). 因为该程序的其他线程fork()子级(例如,带有popen() )。 If those terminate, the signal handler (a static class function) is invoked, too. 如果这些终止,则也会调用信号处理程序(静态类函数)。

As I understand: 我认为:

In the thread where I invoke fork() , I use a member m_pid with default and reset value -1 . 在调用fork()的线程中,我使用具有默认值和重置值-1的成员m_pid It takes the pid from fork() . 它从fork()获取pid。 The sig handler immediately returns if m_pid is -1. 如果m_pid为-1,则sig处理程序立即返回。

The program blocked at popen() which fork() s (could be any other call that fork() s). 挡在程序popen()其中fork() S(可以是任何其它调用fork()多个)。 Thus the signal handler for SIGCHLD is entered when popen() returns. 因此,当popen()返回时,将输入SIGCHLD的信号处理程序。 The check for m_pid is passed as m_pid = fork() has been invoked. m_pid的检查在调用m_pid = fork()通过。 waitpid() does not return -1 but the pid of the popen() child, and then keeps checking with return value = 0 until all children have terminated - the one I'm waiting for is still alive! waitpid()不会返回-1,而是popen()子代的pid,然后继续检查返回值= 0,直到所有子代都终止为止-我正在等待的子代仍然存在! Only then waitpid() returns with -1 and the main thread can continue reading with fgetc() . 只有waitpid()返回-1 ,主线程才能继续使用fgetc()读取。

Man page from waitpid : waitpid页:

if WNOHANG was specified and one or more child(ren) specified by pid exist, but have not yet changed state, then 0 is returned. 如果指定了WNOHANG,并且存在由pid指定的一个或多个孩子,但尚未更改状态,则返回0。 On error, -1 is returned 错误时,返回-1

Because the sig handler checks for m_pid != -1 , the problem only occurred when I used fork() in MyClass to set the m_pid . 因为sig处理程序检查m_pid != -1 ,所以仅当我在MyClass中使用fork()设置m_pid时才出现问题。

That's why using system() did not cause the problem. 这就是为什么使用system()不会引起问题的原因。 The m_pid is not set to a value != -1, thus the sig handler immediately returns if eg a child is popen() ed in the main thread. m_pid未设置为一个值!= -1,如果如一个孩子因此SIG处理程序后立即返回popen()版在主线程。

The imitation of the system() invocation failed because I've set the m_pid at fork() , thus the sig handler did not return immediately. 模仿 system()调用失败,因为我将m_pid设置为fork() ,因此sig处理程序没有立即返回。

I guess since the sig handler is a static member function , the handler blocks the very thread that fork() ed a child process. 我猜因为sig处理程序是一个static member function ,因此该处理程序会阻塞fork()创建子进程的那个线程。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM