如何让 tcsetpgrp() 在 C 中工作?

发布于 2024-10-22 20:25:25 字数 968 浏览 6 评论 0原文

我正在尝试为子进程(通过 fork())提供对终端的前台访问权限。

在我 fork() 之后,我在子进程中运行以下代码:

setpgid(0, 0);

并且:

setpgid(child, child);

在父进程中。

这为子进程提供了自己的进程组。对 setpgid() 的调用工作正常。

现在我想让孩子访问终端。

我在 setpgid() 调用之后向子进程添加了以下内容:

if (!tcsetpgrp(STDIN_FILENO, getpid())) {
    perror("tcsetpgrp failed");
}

之后,有一个 execv() 命令来生成 /usr/bin/nano< /代码>。

然而,nano 并没有出现,什么也没有发生,并且终端看起来好像在等待用户输入。

此外,在 tcsetpgrp() 调用之后似乎没有代码执行。

我在某处读到,我需要向子进程发送 SIGCONT 信号才能使其正常工作。如果该过程停止了,我该怎么办?家长必须发出信号吗?

如果这是解决方案,我该如何发送 SIGCONT 信号?

raise(SIGCONT);

另外,我不确定这是否有帮助,但是如果我运行我的程序:

exec ./program

而不是:

./program

有什么想法吗? 代码工作正常并生成 nano 。非常感谢!

I'm trying to give a child process (via fork()) foreground access to the terminal.

After I fork(), I run the following code in the child process:

setpgid(0, 0);

And:

setpgid(child, child);

In the parent process.

This gives the child its own process group. The call to setpgid() works correctly.

Now I want to give the child access to the terminal.

I added the following to the child after the setpgid() call:

if (!tcsetpgrp(STDIN_FILENO, getpid())) {
    perror("tcsetpgrp failed");
}

After that, there is an execv() command to spawn /usr/bin/nano.

However, instead of having nano come up, nothing happens, and the terminal looks as if it's expecting user input.

Further, no code seems to execute after the tcsetpgrp() call.

I read somewhere that I need to send a SIGCONT signal to the child process to get it to work. If the process is stopped, how can I do that? Does the parent have to send the signal?

How do I go about sending the SIGCONT signal if that is the solution?

raise(SIGCONT);

Also, I'm not sure if this helps, but the code works fine and spawns nano if I run my program with:

exec ./program

Instead of:

./program

Any ideas? Thanks so much!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

羁绊已千年 2024-10-29 20:25:25

想通了。我必须忽略任何 SIGTTOU 信号。

我通过添加以下内容来做到这一点:

signal(SIGTTOU, SIG_IGN);

在 tcsetpgrp() 调用之前。

Figured it out. I have to ignore any SIGTTOU signals.

I did that by adding:

signal(SIGTTOU, SIG_IGN);

Before the tcsetpgrp() call.

小红帽 2024-10-29 20:25:25

man 3 tcsetpgrp 指出:

如果 tcsetpgrp() 由后台进程组的成员在其会话中调用,并且调用进程没有阻止或忽略 SIGTTOU,则会向该后台进程组的所有成员发送 SIGTTOU 信号。

您需要在父进程而不是子进程中调用 tcsetpgrp()。但是,如果您的父进程启动并移至后台,它将收到 SIGTTOU 并将被停止。

man 3 tcsetpgrp states:

If tcsetpgrp() is called by a member of a background process group in its session, and the calling process is not blocking or ignoring SIGTTOU, a SIGTTOU signal is sent to all members of this background process group.

You need to call tcsetpgrp() in your parent process not in child. However if your parent process started and moved into background it will receive SIGTTOU and will be stopped.

我恋#小黄人 2024-10-29 20:25:25

应该由父级而不是子级调用 tcsetpgrp()。 setpgid() 调用后,子进程将成为后台进程。一个有效的情况是前台组放弃其权限,让另一个后台组成为前台,而​​自己成为后台。后台组中的进程无法获取控制终端。示例代码可能如下所示:

/* perror_act.h */
#ifndef PERROR_ACT_H
#define PERROR_ACT_H

#define PERROR_ACT(rtn, act) do { \
    perror(rtn);\
    act; \
} while (0)

#define PERROR_EXIT1(rtn) PERROR_ACT(rtn, exit(1))
#define PERROR_RETN1(rtn) PERROR_ACT(rtn, return -1)

#endif

/* invnano.c */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include "perror_act.h"

void sig_chld(int chld)
{
    exit(0);
}

int main(void)
{
    pid_t child;
    int p2c[2];
    struct sigaction sa = {.sa_handler = sig_chld};

    if (sigaction(SIGCHLD, &sa, NULL))
        PERROR_EXIT1("sigaction");
    if (pipe(p2c))
        PERROR_EXIT1("pipe");
    if ((child = fork()) < 0)
        PERROR_EXIT1("fork");
    if (child == 0) {
        char buff;
        size_t nread;
        if (close(p2c[1])) /* We must make sure this fd is closed. The reason is explained in following comments. */
                        PERROR_EXIT1("close");
        if ((nread = read(p2c[0], &buff, 1)) < 0) /* Just to receive a message from parent indicating its work is done. Content is not important.  */
            PERROR_EXIT1("read");
        if (nread == 0) /* When all the write ends of a pipe are closed, a read() to the read end of this pipe will get a return value of 0. We've closed the child's write end so if 0 as returned, we can sure the parent have exited because of error. */
            exit(1);
        close(p2c[0]);
        execlp("nano", "nano", (char *) 0);
        PERROR_EXIT1("execlp");
    } else {
        if (close(p2c[0]))
            PERROR_EXIT1("close");
        if (setpgid(child, child))
            PERROR_EXIT1("setpgid");
        if (tcsetpgrp(STDIN_FILENO, child))
            PERROR_EXIT1("tcsetpgrp");
        if (write(p2c[1], &child, 1) != 1) /* If all the read ends of a pipe are close, a write() to the write end of this pipe will let the calling process receive a SIGPIPE whose default deposition is to terminate. */
            PERROR_EXIT1("write");
        while (1) /* If parent exit here, login shell will see the news and grab the controlling terminal */
            pause();
    }
    return 0;
}

It's the parent rather than child who should invoke tcsetpgrp(). After setpgid() call, the child becomes a background process. A valid case is the foreground group gives up its permission, let another background group become foreground and itself background. A process in background group can't grab controlling terminal. Example code maybe look like:

/* perror_act.h */
#ifndef PERROR_ACT_H
#define PERROR_ACT_H

#define PERROR_ACT(rtn, act) do { \
    perror(rtn);\
    act; \
} while (0)

#define PERROR_EXIT1(rtn) PERROR_ACT(rtn, exit(1))
#define PERROR_RETN1(rtn) PERROR_ACT(rtn, return -1)

#endif

/* invnano.c */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include "perror_act.h"

void sig_chld(int chld)
{
    exit(0);
}

int main(void)
{
    pid_t child;
    int p2c[2];
    struct sigaction sa = {.sa_handler = sig_chld};

    if (sigaction(SIGCHLD, &sa, NULL))
        PERROR_EXIT1("sigaction");
    if (pipe(p2c))
        PERROR_EXIT1("pipe");
    if ((child = fork()) < 0)
        PERROR_EXIT1("fork");
    if (child == 0) {
        char buff;
        size_t nread;
        if (close(p2c[1])) /* We must make sure this fd is closed. The reason is explained in following comments. */
                        PERROR_EXIT1("close");
        if ((nread = read(p2c[0], &buff, 1)) < 0) /* Just to receive a message from parent indicating its work is done. Content is not important.  */
            PERROR_EXIT1("read");
        if (nread == 0) /* When all the write ends of a pipe are closed, a read() to the read end of this pipe will get a return value of 0. We've closed the child's write end so if 0 as returned, we can sure the parent have exited because of error. */
            exit(1);
        close(p2c[0]);
        execlp("nano", "nano", (char *) 0);
        PERROR_EXIT1("execlp");
    } else {
        if (close(p2c[0]))
            PERROR_EXIT1("close");
        if (setpgid(child, child))
            PERROR_EXIT1("setpgid");
        if (tcsetpgrp(STDIN_FILENO, child))
            PERROR_EXIT1("tcsetpgrp");
        if (write(p2c[1], &child, 1) != 1) /* If all the read ends of a pipe are close, a write() to the write end of this pipe will let the calling process receive a SIGPIPE whose default deposition is to terminate. */
            PERROR_EXIT1("write");
        while (1) /* If parent exit here, login shell will see the news and grab the controlling terminal */
            pause();
    }
    return 0;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文