我正在尝试给孩子进程(通过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);
另外,我不确定这是否有帮助,但是如果我运行我的程序,代码工作正常并产生nano
:
exec ./program
而不是:
./program
有什么想法吗?非常感谢!
答案 0 :(得分:8)
想出来。我必须忽略任何SIGTTOU信号。
我通过添加:
来做到这一点signal(SIGTTOU, SIG_IGN);
tcsetpgrp()
来电之前。
答案 1 :(得分:6)
man 3 tcsetpgrp声明:
如果tcsetpgrp()在其会话中由后台进程组的成员调用,并且调用进程未阻止或忽略SIGTTOU,则会向该后台进程组的所有成员发送SIGTTOU信号。
您需要在父进程中调用tcsetpgrp()而不是在子进程中。但是,如果您的父进程已启动并移至后台,则它将收到SIGTTOU并将被停止。
答案 2 :(得分:2)
应该调用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;
}