2011-03-17 72 views
11

我正試圖給子進程(通過fork())前臺訪問終端。如何讓tcsetpgrp()在C中工作?

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 

有任何想法嗎?非常感謝!

+2

會議領導(讀:殼)應該調用tcsetpgrp() – blaze 2011-03-17 16:59:09

回答

6

人3 tcsetpgrp狀態:

如果tcsetpgrp()由在其會話後臺進程組的成員調用,調用進程不會阻止或忽略SIGTTOU,一個SIGTTOU信號被髮送到此後臺進程組的所有成員。

您需要在父進程中不要在子進程中調用tcsetpgrp()。但是,如果您的父進程啓動並移入後臺,它將收到SIGTTOU並將停止。

+0

你可以expalin爲什麼tcsetpgrp()需要在父級調用,而不是孩子? – 2016-02-17 06:05:38

+1

我讀到:「當你輸入CTRL-C時,你的終端 向前臺進程組內的每個進程發送一個信號,你可以用」tcsetpgrp(int fd,pid_t pgrp)來改變終端前臺的哪個進程組 )」。現在,我們在子(fork()== 0)還是父類中執行此操作? – 2016-02-17 06:07:31

6

想通了。我必須忽略任何SIGTTOU信號。

我這樣做,通過添加:

signal(SIGTTOU, SIG_IGN); 

tcsetpgrp()電話之前。

+0

爲什麼你需要忽略SIGTTOU?這與丟失或共享終端控制有關嗎? – tyree731 2011-03-17 21:27:46

+0

不確定...比忽略它更好,我決定阻止它,調用tcsetpgrp(),然後解除阻止它。 – 2011-03-19 07:02:56

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; 
}