У меня такой вопрос:
есть процесс, который создаёт терминал,
есть несколько дочерних процессов, которые пишут в стандартный вывод (stdout)
собственно вопрос: как мне из материнского процесса сделать так, чтобы вывод дочерних попадал в мой терминал?
Здравствуйте, GarikTot, Вы писали:
GT>Добрый вечер, уважаемые!
GT>У меня такой вопрос: GT>есть процесс, который создаёт терминал, GT>есть несколько дочерних процессов, которые пишут в стандартный вывод (stdout) GT>собственно вопрос: как мне из материнского процесса сделать так, чтобы вывод дочерних попадал в мой терминал?
Можно сначала форкануться, в сыне форка переставить свой
стандартный вывод, а затем родить дочерний процесс. Дочка
наследует стандартный ввод/вывод.
GT>Заранее благодарен
GT>ps. GT>не пинайте меня сильно, если вопрос дурацкий
Serge.
Hасколько проще была бы жизнь, если бы она была в исходниках.
Никакой эт не дурачкий вопрос, а тема, описанная
в каждой книге про юних. Самый простой пример —
запускаем два терминала и делаем вот так:
$ ps
PID TTY TIME CMD
1085 pts/2 00:00:00 bash
1151 pts/2 00:00:00 ps
$ echo'sending text to pts3' > /dev/pts/3
$ ps
PID TTY TIME CMD
1113 pts/3 00:00:00 bash
1152 pts/3 00:00:00 ps
$
$ sending text to pts3
те каталог /dev/ содержит ссылки на устройства, к коим
есесьно, относится и твой терминал. Просто пиши в твой
материнский терминал (устройство).
Re[2]: перенаправление стандартного вывода
От:
Аноним
Дата:
03.07.03 13:14
Оценка:
Здравствуйте, a1EXa, Вы писали:
EX>Никакой эт не дурачкий вопрос, а тема, описанная EX>в каждой книге про юних. Самый простой пример - EX>запускаем два терминала и делаем вот так: EX>
EX>$ ps
EX> PID TTY TIME CMD
EX> 1085 pts/2 00:00:00 bash
EX> 1151 pts/2 00:00:00 ps
EX>$ echo'sending text to pts3' > /dev/pts/3
EX>
EX>
EX>$ ps
EX> PID TTY TIME CMD
EX> 1113 pts/3 00:00:00 bash
EX> 1152 pts/3 00:00:00 ps
EX>$
EX>$ sending text to pts3
EX>
EX>те каталог /dev/ содержит ссылки на устройства, к коим EX>есесьно, относится и твой терминал. Просто пиши в твой EX>материнский терминал (устройство).
Дело в том, что дочерние процессы не должны ничего знать о материнском
они пишут в стандартный вывод и изменить здесь ничего нельзя
можно ли как-то изменить параметры наследования, чтобы "сыны" унаследовали вывод матери?
Здравствуйте, Sergeem, Вы писали:
S>Можно сначала форкануться, в сыне форка переставить свой S>стандартный вывод, а затем родить дочерний процесс. Дочка S>наследует стандартный ввод/вывод.
Сейчас попробую... А почему нельзя сразу рожать? без форка?
Здравствуйте, GarikTot, Вы писали:
GT>Здравствуйте, Sergeem, Вы писали:
S>>Можно сначала форкануться, в сыне форка переставить свой S>>стандартный вывод, а затем родить дочерний процесс. Дочка S>>наследует стандартный ввод/вывод.
GT>Сейчас попробую... А почему нельзя сразу рожать? без форка?
чтобы не портить stdout основного процесса.
Serge.
Hасколько проще была бы жизнь, если бы она была в исходниках.
Что-то у меня не получается
Можно простенький примерчик?
Может я что неправильно делаю?
есть несколько дочерних процессов, которые делают что-то и при этом пишут в стандартный вывод
они ничего не должны знать о "матери"
материнский процесс создаёт терминал и перенаправляет туда вывод
после этого запускает дочерние
однако их вывод идёт на консоль, а не в терминал
пробовал сделать как ты советовал в предыдущем посте, т.е. заменить вызов spawn комбинацией forc+exec
при этом получается то же самое
Вопрос довольно сложный и ответ на него сильно зависит от системы.
Если терминал чужой, то как уже написали, в него писать можно всегда.
Если терминал свой, то можно попробовать извратиться как-нибудь так:
#include <stdio.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
int main (int argc, char *argv[]) {
pid_t oldpgrp, mypid;
pid_t p = fork ();
if (p < 0) { // AP: fork failed somehow
return -1;
}
if (p == 0) { // AP: child process
int fd;
mypid = getpid ();
fclose (stdin); fclose (stdout); fclose (stderr);
fd = open ("/dev/tty", O_RDWR); // AP: there should be some correct way of doing that ...
if (fd == -1) {
// AP: we may fail here because of some reasons like
// a) there was no controlling terminal
// b) we're called from the chroot'ed environment
// c) etc
return -1;
}
dup2 (fd, 0); // AP: we are able to use dup(fd) here at least in Linux
dup2 (fd, 1); // AP: we are able to use dup(fd) here at least in Linux
dup2 (fd, 2); // AP: we are able to use dup(fd) here at least in Linux
if (setpgrp () == -1) {
// AP: looks like this can never happen, because we're no leader of the session
return -1;
}
(void)ioctl (0, TIOCGPGRP, &oldpgrp); // AP: i'm not sure if we really have to try to save old pgrp, it just doesn't hurt!
signal (SIGTTOU, SIG_IGN); // AP: mysterious things, but Linux expects this in tty_check_change
if (ioctl (0, TIOCSPGRP, &mypid) == -1) {
// AP: failed to acquire terminal
return -1;
}
stdin = fdopen (0, "r"); stdout = fdopen (1, "w"); stderr = fdopen (2, "w"); // AP: does ISO requires std* to be public writable vars?
{
printf ("Hello, world!\n");
}
ioctl (0, TIOCSPGRP, &oldpgrp); // AP: i believe that this never works, but why not just try?
}
}
Тут единственная проблема в том, что set process group несколько странно работает в Linux и сменить группу процессов на чужую, похоже, нельзя (или надо подумать как).
Вроде доковырял пример до работоспособного состояния (по крайней мере в Linux):
/**********************************************************************
Terminal refetching sample.
Copyright (C) Murr. 2003.
Feel free to use the code in any educational or commercial purposes.
***********************************************************************/
/**********************************************************************
The process changes the foreground process group of its controlling
terminal onto itself by freezing the whole foreground group and
switching pgrp and then making some I/O on it (output+input+output).
The process can be run in the context of whatever pgrp and/or
any with whatever redirected stdin/stdout/stderr.
***********************************************************************/
#include <stdio.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <termios.h>
#include <errno.h>
int main (int argc, char *argv[]) {
int status, have_to_lock_term = 1;
struct termios t_old, t_new;
pid_t oldpgrp, mypid, realpgrp;
pid_t p = fork (); //AP: no real need for forking here, it's just to show forked redirection
char str[11];
if (p < 0) { // AP: fork failed somehow
return -1;
}
if (p == 0) { // AP: child process
int fd;
mypid = getpid ();
fclose (stdin); fclose (stdout); fclose (stderr);
fd = open ("/dev/tty", O_RDWR); // AP: there should be some correct way of doing that ...
if (fd == -1) {
// AP: we may fail here because of some reasons like
// a) there was no controlling terminal
// b) we're called from the chroot'ed environment (hence /dev is unreachable)
// c) etc
// Is anyone aware of how controlling tty may be opened in most common case?
return -1;
}
dup2 (fd, 0); // AP: we are able to use dup(fd) here at least in Linux
dup2 (fd, 1); // AP: we are able to use dup(fd) here at least in Linux
dup2 (fd, 2); // AP: we are able to use dup(fd) here at least in Linux
(void)ioctl (0, TIOCGPGRP, &oldpgrp); // AP: saving foreground group id to restore it later
if (oldpgrp == getpgrp ())
have_to_lock_term = 0;
if (have_to_lock_term) {
killpg (oldpgrp, SIGSTOP); // AP: freeze foreground group
if (setpgrp () == -1) {
// AP: looks like this can never happen, because we're never a leader of a session
return -1;
}
signal (SIGTTOU, SIG_IGN); // AP: mysterious things, but Linux expects this in tty_check_change
// so this should always be done ...
if (ioctl (0, TIOCSPGRP, &mypid) != 0) {
// AP: failed to acquire terminal
return -1;
}
}
tcgetattr (0, &t_old); // AP: i used to change ECHO mode with tcgetattr, while ioctl is fine ;)
t_new.c_iflag = ICRNL | IXON;
t_new.c_oflag = OPOST | ONLCR;
t_new.c_cflag = B38400 | CS8 | CREAD | HUPCL;
t_new.c_lflag = (ISIG|ICANON|ECHO|ECHOE|ECHOK|ECHOCTL|ECHOKE|IEXTEN);
tcsetattr (0, TCSANOW, &t_new); // AP: set the default mode
stdin = fdopen (0, "r"); stdout = fdopen (1, "w"); stderr = fdopen (2, "w"); // AP: does ANSI/ISO requires std* to be public writable vars?
printf ("Hello, stranger! Please, enter something:\n"); // AP: output demo
fgets (str, 10, stdin); // AP: input demo
printf ("You entered %s\n (error code = %lu)\n", str, errno); // AP: output demo
tcsetattr (0, TCSANOW, &t_old); // AP: restoring old terminal settings
if (have_to_lock_term) {
ioctl (0, TIOCSPGRP, &oldpgrp); // AP: switching back the foreground process group
killpg (oldpgrp, SIGCONT); // AP: unfreeze the foreground group
}
return 0;
}
wait (&status); // AP: do not poll the status - that really doesn't matter
return 0;
}
hell:~> ./ttytest
Hello, stranger! Please, enter something:
test123456
You entered test12345
(error code = 29)
hell:~> ./ttytest &
[1] 26395
hell:~> Hello, stranger! Please, enter something:
test987654321
You entered test98765
(error code = 29)
[1]+ Done ./ttytest
hell:~>