如何让 tcsetpgrp() 在 C 中工作?
Posted
技术标签:
【中文标题】如何让 tcsetpgrp() 在 C 中工作?【英文标题】:How do I get tcsetpgrp() to work in C? 【发布时间】:2011-07-17 12:10:30 【问题描述】:我正在尝试为子进程(通过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
有什么想法吗?非常感谢!
【问题讨论】:
会话负责人(阅读:shell)应该调用 tcsetpgrp() 【参考方案1】:应该调用 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;
【讨论】:
如何将父进程添加回前台进程组?在前台进程组中添加子进程后,父进程将成为后台进程,execlp()
将永远不会返回。那么我应该从哪里调用tcsetpgrp()
再次在前台进程组中添加父进程?
我得到了答案。我在完成子进程后的父进程的情况下,我可以忽略 SIGTTOU 信号,然后调用tcsetpgrp()
在前台进程组中添加父进程。 @John Kurlak 解决方案已经回答了这个问题。【参考方案2】:
man 3 tcsetpgrp 状态:
如果 tcsetpgrp() 由其会话中的后台进程组的成员调用,并且调用进程没有阻塞或忽略 SIGTTOU,则会向该后台进程组的所有成员发送 SIGTTOU 信号。
你需要在你的父进程而不是子进程中调用 tcsetpgrp()。但是,如果您的父进程启动并移至后台,它将收到 SIGTTOU 并停止。
【讨论】:
你能解释一下为什么需要在父进程而不是子进程中调用 tcsetpgrp() 吗? 我读到“当您键入 CTRL-C 时,您的终端会向前台进程组中的每个进程发送一个信号。您可以使用“tcsetpgrp( int fd,pid_t pgrp)"。现在,我们是在子级(fork() ==0)还是父级中执行此操作?【参考方案3】:想通了。我必须忽略任何 SIGTTOU 信号。
我通过添加:
signal(SIGTTOU, SIG_IGN);
在tcsetpgrp()
调用之前。
【讨论】:
为什么需要忽略 SIGTTOU?这是否与终端控制丢失或共享有关? 不确定...总比忽略它好,我决定阻止它,调用 tcsetpgrp(),然后取消阻止它。以上是关于如何让 tcsetpgrp() 在 C 中工作?的主要内容,如果未能解决你的问题,请参考以下文章
如何让 C 语言中的 ANTLR3.5 生成的解析器在 MVS EBCDIC 环境中工作?
如何让一个方法等到另一个方法完成它在Objective C中工作?