No SIGHUP on default Ubuntu by closing socketpair.

huangapple go评论68阅读模式
英文:

No SIGHUP on default Ubuntu by closing socketpair

问题

n=10, 'Hello Worl'  // 完美的行;但下一行缺失。
// "signal received: ...  MISSING
英文:

The following code should call the signal handler once the pipes are closed by the parent. The signal handler will be sporned by the child, cause it waits one second. This wait cycle should be interrupted by the signal handler.

Only output:

n=10, 'Hello Worl'  // perfect line ;), but the next line is missing.
// "signal received: ...  MISSING

How ever, the signal handler is not invoked. Can someone tell, why?

hint: As no Signal is processed, the child is left behind. Terminate it manually.

#include <csignal>
#include <cstdio>

#include <sys/socket.h>
#include <unistd.h>

void child(int socket) {
    while (true) {
        char buf[100];
        int n = read(socket, buf, 10);
        printf("n=%d, '%s'\n", n, buf);
        sleep(1);
    }
}

void parent(int socket) {
    char buf[] = "Hello World\n";
    write(socket, buf, sizeof(buf));

    close(socket);
}

int main() {
    int socket[2];
    socketpair(AF_LOCAL, SOCK_DGRAM, 0, socket);

    signal(SIGHUP, [](int sig) {
        printf("signal received: %d\n", sig);
    });

    switch (fork()) {
        case 0:
            close(socket[1]);
            child(socket[0]);
            break;

        default:
            close(socket[0]);
            parent(socket[1]);
            break;

        case -1:
            return 1;
    }

    return 0;
}

答案1

得分: 1

以下是翻译的内容:

the intention is, that if one side dies, the socketpair in term is destroyed and this should cause some kind of signal which should lead to a collapse of all started processes
意图是,如果一方终止,套接字对将被销毁,这应该引发某种信号,从而导致所有已启动的进程崩溃。

Well, in that case a datagram socket is just about the only thing you cannot use.
好吧,在这种情况下,数据报套接字几乎是唯一不能使用的东西。

If you want to continue using file descriptors for this and/or stay fully POSIX-compatible, a stream socket or pipe is what you can use.
如果您想要继续使用文件描述符进行此操作并且要保持完全符合 POSIX,那么您可以使用流套接字或管道。

#include <poll.h>

#include <sys/types.h>
#include <sys/socket.h>
// using socketpair

#include <unistd.h>
// using fork, sleep, close

#include <cstdio>
// using std::puts
int main()
{
  int sockets[2];
  socketpair(AF_UNIX, SOCK_STREAM, 0, sockets);
  if(fork()) { // parent
    close(sockets[1]);
    sleep(1);
    std::puts("Parent exit");
  }
  else { // child
    close(sockets[0]);
    pollfd poll_fd = { sockets[1], POLLIN, 0 };
    poll(&poll_fd, 1, 2000 /*timeout*/);
    if(poll_fd.revents & POLLIN)
      std::puts("Parent died before child");
    else
      std::puts("Child exit before parent");
  }
}
#include <poll.h>

#include <sys/types.h>
#include <sys/socket.h>
// using socketpair

#include <unistd.h>
// using fork, sleep, close

#include <cstdio>
// using std::puts
int main()
{
  int sockets[2];
  socketpair(AF_UNIX, SOCK_STREAM, 0, sockets);
  if(fork()) { // parent
    close(sockets[1]);
    sleep(1);
    std::puts("Parent exit");
  }
  else { // child
    close(sockets[0]);
    pollfd poll_fd = { sockets[1], POLLIN, 0 };
    poll(&poll_fd, 1, 2000 /*timeout*/);
    if(poll_fd.revents & POLLIN)
      std::puts("Parent died before child");
    else
      std::puts("Child exit before parent");
  }
}

Personally, if I don't have need for a socket, I just use a pipe, keeping the write-side open on the parent and the read-side in the child. When the write-side closes (or on a stream socket if either side closes), reading will unblock (indicating EOF).
就我个人而言,如果我不需要套接字,我只使用管道,在父进程上保持写端打开,子进程上保持读端打开。当写端关闭时(或者在流套接字上如果任一端关闭),读取将解除阻塞(表示已到达文件末尾)。

This approach is most useful if you have some sort of event loop since you can usually just plug it into those like I did here with the poll. If you don't and you want a signal instead, you may use SIGIO and interrupt-driven input but I find this rather tedious, especially for larger process trees.
如果您有某种事件循环,这种方法非常有用,因为通常可以像我在这里使用 poll 一样将其插入其中。如果您没有事件循环并且想要信号,您可以使用 SIGIO 和 中断驱动输入,但我认为这相当繁琐,特别是对于较大的进程树。

If you are fine with a Linux-specific solution, prctl(PR_SET_PDEATHSIG, SIGHUP) is what you want.
如果您接受特定于Linux的解决方案,那么 prctl(PR_SET_PDEATHSIG, SIGHUP) 就是您想要的。

#include <sys/types.h>
#include <unistd.h>
// using getpid, getppid, pause, sleep
#include <sys/prctl.h>

#include <csignal>
#include <cstdio>
// using std::puts. std::printf
#include <cstring>
// using std::strlen
int main()
{
  pid_t parent_id = getpid();
  if(fork()) { // parent
    sleep(1);
    std::puts("Parent exit");
  }
  else { // child
    std::signal(SIGHUP, [](int sig) {
      const char* msg = "Child received SIGHUP\n";
      // puts or printf are not signal-safe
      write(1 /*stdout*/, msg, std::strlen(msg));
      _exit(1); // std::exit is not signal-safe!
    });
    prctl(PR_SET_PDEATHSIG, SIGHUP);
    if(getppid() != parent_id) {
      std::puts("Parent died during child setup");
      return 0;
    }
    while(true)
      pause();
  }
}
#include <sys/types.h>
#include <unistd.h>
// using getpid, getppid, pause, sleep
#include <sys/prctl.h>

#include <csignal>
#include <cstdio>
// using std::puts. std.printf
#include <cstring>
// using std.strlen
int main()
{
  pid_t parent_id = getpid();
  if(fork()) { // parent
    sleep(1);
    std::puts("Parent exit");
  }
  else { // child
    std::signal(SIGHUP, [](int sig) {
      const char* msg = "Child received SIGHUP\n";
      // puts or printf are not signal-safe
      write(1 /*stdout*/, msg, std::strlen(msg));
      _exit(1); // std::exit is not signal

<details>
<summary>英文:</summary>

&gt; the intention is, that if one side dies, the socketpair in term is destroyed and this should cause some kind of signal which should lead to a collapse of all started processes

Well, in that case a datagram socket is just about the only thing you cannot use.

If you want to continue using file descriptors for this and/or stay fully POSIX-compatible, a stream socket or pipe is what you can use.

```c++
#include &lt;poll.h&gt;

#include &lt;sys/types.h&gt;
#include &lt;sys/socket.h&gt;
// using socketpair

#include &lt;unistd.h&gt;
// using fork, sleep, close

#include &lt;cstdio&gt;
// using std::puts


int main()
{
  int sockets[2];
  socketpair(AF_UNIX, SOCK_STREAM, 0, sockets);
  if(fork()) { // parent
    close(sockets[1]);
    sleep(1);
    std::puts(&quot;Parent exit&quot;);
  }
  else { // child
    close(sockets[0]);
    pollfd poll_fd = { sockets[1], POLLIN, 0 };
    poll(&amp;poll_fd, 1, 2000 /*timeout*/);
    if(poll_fd.revents &amp; POLLIN)
      std::puts(&quot;Parent died before child&quot;);
    else
      std::puts(&quot;Child exit before parent&quot;);
  }
}

Personally, if I don't have need for a socket, I just use a pipe, keeping the write-side open on the parent and the read-side in the child. When the write-side closes (or on a stream socket if either side closes), reading will unblock (indicating EOF).

This approach is most useful if you have some sort of event loop since you can usually just plug it into those like I did here with the poll. If you don't and you want a signal instead, you may use SIGIO and interrupt-driven input but I find this rather tedious, especially for larger process trees.

If you are fine with a Linux-specific solution, prctl(PR_SET_PDEATHSIG, SIGHUP) is what you want.

#include &lt;sys/types.h&gt;
#include &lt;unistd.h&gt;
// using getpid, getppid, pause, sleep
#include &lt;sys/prctl.h&gt;

#include &lt;csignal&gt;
#include &lt;cstdio&gt;
// using std::puts. std::printf
#include &lt;cstring&gt;
// using std::strlen


int main()
{
  pid_t parent_id = getpid();
  if(fork()) { // parent
    sleep(1);
    std::puts(&quot;Parent exit&quot;);
  }
  else { // child
    std::signal(SIGHUP, [](int sig) {
      const char* msg = &quot;Child received SIGHUP\n&quot;;
      // puts or printf are not signal-safe
      write(1 /*stdout*/, msg, std::strlen(msg));
      _exit(1); // std::exit is not signal-safe!
    });
    prctl(PR_SET_PDEATHSIG, SIGHUP);
    if(getppid() != parent_id) {
      std::puts(&quot;Parent died during child setup&quot;);
      return 0;
    }
    while(true)
      pause();
  }
}

Note this warning from the man-page:
> the "parent" in this case is considered to be the thread that created this process. In other words, the signal will be sent when that thread terminates (via, for example, pthread_exit(3)), rather than after all of the threads in the parent process terminate.

BTW: Your use of printf in a signal handler was erroneous. None of the C stdio functions are async-signal-safe.

huangapple
  • 本文由 发表于 2023年3月31日 16:02:11
  • 转载请务必保留本文链接:https://go.coder-hub.com/75896163.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定