Как мне заставить tcsetpgrp() работать в C?
Я пытаюсь дать ребенку процесс (через 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
Есть идеи? Спасибо!
3 ответа
man 3 tcsetpgrp утверждает:
Если tcsetpgrp() вызывается членом группы фоновых процессов в своем сеансе, и вызывающий процесс не блокирует или не игнорирует SIGTTOU, сигнал SIGTTOU отправляется всем членам этой группы фоновых процессов.
Вам нужно вызывать tcsetpgrp() в родительском процессе, а не в child. Однако, если ваш родительский процесс запущен и перемещен в фоновый режим, он получит SIGTTOU и будет остановлен.
Догадаться. Я должен игнорировать любые сигналы SIGTTOU.
Я сделал это, добавив:
signal(SIGTTOU, SIG_IGN);
Перед tcsetpgrp()
вызов.
Родитель, а не ребенок должен вызывать 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;
}