TE
科技回声
首页24小时热榜最新最佳问答展示工作
GitHubTwitter
首页

科技回声

基于 Next.js 构建的科技新闻平台,提供全球科技新闻和讨论内容。

GitHubTwitter

首页

首页最新最佳问答展示工作

资源链接

HackerNews API原版 HackerNewsNext.js

© 2025 科技回声. 版权所有。

Way too many ways to wait on a child process with a timeout

129 点作者 broken_broken_6 个月前

14 条评论

machine_coffee6 个月前
Lol, author&#x27;s thought process mirrored mine as I read the article, as I was reading I was thinking, &#x27;doesn&#x27;t kqueue support that?... and then a section on kqueue. Then I was thinking to myself, so how does the Linux implementation do it then?... was just about to start trawling the source code when &#x27;A parenthesis..&#x27;<p>Great article. Sorry to say though, Windows does manage all this in a more consistent way - but I guess they had the benefit of a clean slate.
评论 #42109647 未加载
评论 #42131470 未加载
adrianmonk6 个月前
Tenth Approach: fork() two processes.<p>Child 1 exec()s the command.<p>Child 2 does this:<p><pre><code> signal(SIGALRM, alarm_handler); alarm(timeout_length); pause(); exit(0); </code></pre> Start both children, then call wait(), which blocks until <i>any</i> child exits and returns the pid of the child that exited. If it&#x27;s the command child, then your command finished. If it&#x27;s the other child, then the timeout expired.<p>Now that one child has exited, kill() the other child with SIGTERM and reap it by calling wait() again.<p>All of this assumes you&#x27;ll only have these two children going, but if you&#x27;re writing a small exponential backoff command retry utility, that should be OK.
评论 #42147726 未加载
AnotherGoodName6 个月前
In the early days of android i had an app that had to do video transcoding yet often hit oom on startup (reported via telemetry) even when the phone should have enough memory. This was before android had any video transcoding built in (2.3 days).<p>The solution was to spawn a child process, use memory in a loop, catch the sigkill in the parent, yield to the os as it killed other processes to free memory in the device as a whole and then on return from sleep in the parent process after killing the child start the video transcoding.<p>Hopefully this hack is not needed but if you want android to proactively run its process killing job so your app starts with maximum free memory the above worked!
评论 #42134460 未加载
greggyb6 个月前
Not so much about timeouts, but related in that it is based around managing children processes:<p>The lineage of tools descending from daemontools for service management is worth exploring:<p>daemontools: <a href="http:&#x2F;&#x2F;cr.yp.to&#x2F;daemontools.html" rel="nofollow">http:&#x2F;&#x2F;cr.yp.to&#x2F;daemontools.html</a><p>runit: <a href="https:&#x2F;&#x2F;smarden.org&#x2F;runit&#x2F;" rel="nofollow">https:&#x2F;&#x2F;smarden.org&#x2F;runit&#x2F;</a><p>s6: <a href="https:&#x2F;&#x2F;skarnet.org&#x2F;software&#x2F;s6&#x2F;" rel="nofollow">https:&#x2F;&#x2F;skarnet.org&#x2F;software&#x2F;s6&#x2F;</a><p>dinit: <a href="https:&#x2F;&#x2F;davmac.org&#x2F;projects&#x2F;dinit&#x2F;" rel="nofollow">https:&#x2F;&#x2F;davmac.org&#x2F;projects&#x2F;dinit&#x2F;</a>
nf36 个月前
FWIW io_uring does have support for waitid.<p><a href="https:&#x2F;&#x2F;www.man7.org&#x2F;linux&#x2F;man-pages&#x2F;man3&#x2F;io_uring_prep_waitid.3.html" rel="nofollow">https:&#x2F;&#x2F;www.man7.org&#x2F;linux&#x2F;man-pages&#x2F;man3&#x2F;io_uring_prep_wait...</a>
评论 #42106025 未加载
评论 #42130260 未加载
nasretdinov6 个月前
So many ways and no-one mentioned threads..?<p>Edit: by threads I mean creating a new thread to wait for the process, and then kill the process after a certain timeout if the process hasn&#x27;t terminated. I guess I&#x27;m spoiled by Go...
评论 #42112294 未加载
eduction6 个月前
He mentions Bryan Cantrill in there and I can’t resist posting his famous epoll&#x2F;kqueue rant:<p><a href="https:&#x2F;&#x2F;youtu.be&#x2F;l6XQUciI-Sc?t=3643" rel="nofollow">https:&#x2F;&#x2F;youtu.be&#x2F;l6XQUciI-Sc?t=3643</a><p>I know this is related but maybe someone smarter than me can explain how closely it relates (or doesn’t) to this issue which seems more general (iirc Cantrill was talking about fs events not child processes generally)
xchip6 个月前
Thanks for this great article, it is going to be very useful for my project. I am currently developing an open source Android native app that invokes rsync when a file gets closed (ie: you take a picture)<p><a href="https:&#x2F;&#x2F;github.com&#x2F;aguaviva&#x2F;Syncy">https:&#x2F;&#x2F;github.com&#x2F;aguaviva&#x2F;Syncy</a>
akira25016 个月前
&gt; I would prefer extending poll to support things other than file descriptors, instead of converting everything a file descriptor to be able to use poll.<p>Why? The ability to block on these descriptors as a one off rather than wrapping into a poll makes them extremely useful and avoids the race issues that exist with signal handlers and other non-blocking mechanisms.<p>signalfd, timerfd, eventfd, userfaultfd, pidfd are all great applications of this strategy.
评论 #42131300 未加载
cdaringe6 个月前
I wrote a crate <a href="https:&#x2F;&#x2F;crates.io&#x2F;crates&#x2F;swaperooni" rel="nofollow">https:&#x2F;&#x2F;crates.io&#x2F;crates&#x2F;swaperooni</a> for similar use cases some time ago. I only gave the article a cursory scan, and can clearly see much deeper thought given here. Can&#x27;t wait to dig in after work and learn a little bit.<p>Dunking on my crate is welcomed :)
moron1236 个月前
Parenting 101
JackSlateur6 个月前
What is the meaning of this code ?<p><pre><code> void on_sigchld(int sig) { (void)sig; }</code></pre>
评论 #42112569 未加载
o11c6 个月前
&gt; Because the Linux kernel coalesces SIGCHLD (and other signals), the only way to reliably determine if a monitored process has exited, is to loop through all PIDs registered by any kqueue when we receive a SIGCHLD. This involves many calls to waitid(2) and may have a negative performance impact.<p>This is somewhat wrong. To speed things up in the happy case (where we are the only part of the program that is spawning children), you can just do a `WNOHANG` wait for <i>any</i> child first, and check if it&#x27;s one of the children we care about. Only if it&#x27;s an unknown child do you have to do the full loop (of course, if you only have a couple of children the loop may be better).
tlsalmin6 个月前
First nitpick:<p><pre><code> static int pipe_fd[2] = {0}; </code></pre> 0 is valid fd, so I recommend initializing fds to -1.<p>signalfd was just off-hand mentioned, but for writing anything larger, like lets say a daemon process, it keeps things close to all the other events being reacted to. E.g.<p><pre><code> #include &lt;signal.h&gt; #include &lt;unistd.h&gt; #include &lt;stdio.h&gt; #include &lt;stdlib.h&gt; #include &lt;sys&#x2F;timerfd.h&gt; #include &lt;sys&#x2F;signalfd.h&gt; #include &lt;sys&#x2F;epoll.h&gt; static int signalfd_init(void) { sigset_t sigs, oldsigs; int sfd = -1; sigemptyset(&amp;sigs); sigemptyset(&amp;oldsigs); sigaddset(&amp;sigs, SIGCHLD); if (!sigprocmask(SIG_BLOCK, &amp;sigs, &amp;oldsigs)) { sfd = signalfd(-1, &amp;sigs, SFD_CLOEXEC | SFD_NONBLOCK); if (sfd != -1) { &#x2F;&#x2F; Success return sfd; } else { perror(&quot;signalfd&quot;); } sigprocmask(SIG_SETMASK, &amp;oldsigs, NULL); } else { perror(&quot;sigprocmask&quot;); } return -1; } static int timerfd_init(void) { int tfd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC); if (tfd != -1) { struct itimerspec tv = { .it_value = { .tv_sec = 5 } }; if (!timerfd_settime(tfd, 0, &amp;tv, NULL)) { return tfd; } else { perror(&quot;timerfd_settime&quot;); } close(tfd); } else { perror(&quot;timerfd_create&quot;); } return -1; } static int epoll_init(int sfd, int tfd) { int efd; if (!sfd || !tfd) { return -1; } efd = epoll_create1(EPOLL_CLOEXEC); if (efd != -1) { struct epoll_event ev[2] = { { .events = EPOLLIN, .data = { .fd = sfd, } }, { .events = EPOLLIN, .data = { .fd = tfd } } }; if (!epoll_ctl(efd, EPOLL_CTL_ADD, sfd, &amp;ev[0]) &amp;&amp; !epoll_ctl(efd, EPOLL_CTL_ADD, tfd, &amp;ev[1])) { return efd; } else { perror(&quot;epoll_ctl&quot;); } close(efd); } else { perror(&quot;epoll_create1&quot;); } return -1; } int main(int argc, char *argv[]) { int exit_value = EXIT_FAILURE; int sfd = signalfd_init(), tfd = timerfd_init(), efd = epoll_init(sfd, tfd); if (sfd != -1 &amp;&amp; tfd != -1 &amp;&amp; efd != -1) { int child_pid = fork(); if (child_pid != -1) { if (!child_pid) { argv += 1; if (-1 == execvp(argv[0], argv)) { exit(EXIT_FAILURE); } __builtin_unreachable(); } else { int err; struct epoll_event ev; while ((err = epoll_wait(efd, &amp;ev, 1, -1)) &gt; 0) { if (ev.data.fd == tfd) { &#x2F;&#x2F; Read the signalfd for the possible SIGCHLD and exit_value = EXIT_SUCCESS; } else if (ev.data.fd == tfd) { &#x2F;&#x2F; Timer triggered, kill the child process. } } if (err == -1) { perror(&quot;epoll_wait&quot;); } } } else { perror(&quot;fork&quot;); } } close(sfd); close(tfd); close(efd); exit(exit_value); }</code></pre>