Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/69.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何让tcsetpgrp()在C中工作?_C_Process_Signals_Jobs_Foreground - Fatal编程技术网

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

如何让tcsetpgrp()在C中工作?,c,process,signals,jobs,foreground,C,Process,Signals,Jobs,Foreground,我试图给子进程(通过fork())前台访问终端的权限 在Ifork()之后,我在子进程中运行以下代码: setpgid(0, 0); 以及: 在父进程中 这将为子进程提供自己的进程组。对setpgid()的调用工作正常 现在我想让孩子访问终端 在调用setpgid()之后,我向子级添加了以下内容: if (!tcsetpgrp(STDIN_FILENO, getpid())) { perror("tcsetpgrp failed"); } 之后,有一个execv()命令来生成/usr

我试图给子进程(通过
fork()
)前台访问终端的权限

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

setpgid(0, 0);
以及:

在父进程中

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

现在我想让孩子访问终端

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

if (!tcsetpgrp(STDIN_FILENO, getpid())) {
    perror("tcsetpgrp failed");
}
之后,有一个
execv()
命令来生成
/usr/bin/nano

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

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

我在某个地方读到,我需要向子进程发送一个
SIGCONT
信号以使其工作。如果进程停止,我该如何做?家长必须发出信号吗

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

raise(SIGCONT);
此外,我不确定这是否有帮助,但如果我使用以下工具运行程序,代码工作正常并生成
nano

exec ./program
而不是:

./program

有什么想法吗?非常感谢

找到了答案。我必须忽略任何信号

为此,我补充说:

signal(SIGTTOU, SIG_IGN);

在调用
tcsetpgrp()
之前。

man 3 tcsetpgrp声明:

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


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

应该调用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;
}
/*perror_act.h*/
#ifndef PERROR_ACT_H
#定义行为
#定义PERROR_ACT(rtn,ACT)do{\
perror(rtn)\
行动\
}而(0)
#定义PERROR_EXIT1(rtn)PERROR_ACT(rtn,exit(1))
#定义PERROR_RETN1(rtn)PERROR_ACT(rtn,return-1)
#恩迪夫
/*invnano.c*/
#包括
#包括
#包括
#包括
#包括“perror_act.h”
无效信号信道(内部信道)
{
出口(0);
}
内部主(空)
{
pid_t儿童;
int p2c[2];
结构sigaction sa={.sa_handler=sig_chld};
if(sigaction(SIGCHLD,&sa,NULL))
PERROR_EXIT1(“sigaction”);
if(管道(p2c))
PERROR_EXIT1(“管道”);
如果((child=fork())<0)
PERROR_EXIT1(“fork”);
如果(子项==0){
半焦;
尺寸(单位:nread);
如果(关闭(p2c[1])/*我们必须确保此fd已关闭。原因在以下注释中解释*/
PERROR_EXIT1(“关闭”);
如果((nread=read(p2c[0],&buff,1))<0)/*只是为了接收来自父级的消息,表明其工作已完成。内容并不重要*/
PERROR_EXIT1(“读取”);
如果(nread==0)/*当管道的所有写入端都关闭时,此管道的读取端的read()将获得0的返回值。我们已关闭子管道的写入端,因此如果返回0,则可以确保父管道已因错误退出*/
出口(1);
关闭(p2c[0]);
execlp(“nano”,“nano”,“char*)0);
PERROR_EXIT1(“execlp”);
}否则{
如果(关闭(p2c[0]))
PERROR_EXIT1(“关闭”);
if(setpgid(child,child))
PERROR_EXIT1(“setpgid”);
if(tcsetpgrp(标准文件号,子文件))
PERROR_EXIT1(“tcsetpgrp”);
如果(write(p2c[1],&child,1)!=1)/*如果管道的所有读取端都很近,则对此管道的写入端执行write()操作将允许调用进程接收一个SIGPIPE,其默认存储将终止*/
PERROR_EXIT1(“写入”);
而(1)/*如果父节点退出此处,则登录shell将看到新闻并抓取控制终端*/
暂停();
}
返回0;
}

会话负责人(读:shell)应该调用tcsetpgrp(),为什么需要忽略sigttoo?这是否与失去或共享终端控制有关?不确定。。。我决定,比忽略它更好的是阻止它,调用tcsetpgrp(),然后取消阻止它。你能解释为什么需要在父级而不是子级中调用tcsetpgrp()吗?我读到了“当您键入CTRL-C时,您的终端会向前台进程组中的每个进程发送一个信号。您可以使用“tcsetpgrp(int fd,pid_t pgrp)”更改终端前台的进程组。现在,我们是在子进程(fork()==0)还是在父进程中执行此操作?如何将父进程添加回前台进程组?在前台进程组中添加子进程后,父进程组将成为后台进程并
execlp()
将永远不会返回。那么我应该从哪里调用
tcsetpgrp()
以再次将父进程添加到前台进程组中?我已经得到了答案。如果父进程完成子进程,我可以忽略SIGTTOU信号,然后调用
tcsetpgrp()
在前台进程组中添加父进程。这已在@John Kurlak solution中得到回答。
/* 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;
}