LINUX.ORG.RU

как присвоить ребенку новую группу процесса?

 , ,


0

1

Добрый вечер, не могу разобраться как пользоваться setpgid мне нужно присвоить новому процессу группу отличную от родителя, чтобы при нажатии cntl+Z вставал на паузу только процесс вызванный с помощью execve а мой псевдо bash продолжал работу. Помогите пожалуйста разобраться

void	ft_infinit_pipe2(t_exectoken *head, t_memory *q)
{
	int			p[2];
	pid_t		pid;
	int			fd_in;
	char		*rt;
	int			status;

	fd_in = 0;
	ft_file_create(head);
	rt = NULL;
	while (head)
	{
		if (pipe(p) == -1 || (pid = fork()) == -1)
		{
			ft_putstr_fd("ERROR pipe or fork", 2);
			exit(1);
		}
		else if (pid == 0)
		{
			if (head->left != NULL)
			{
				dup2(p[1], 1);
				close(p[1]);
			}
			if (head->file_opt)
				ft_fd_flag(head->file_opt, &fd_in);
			dup2(fd_in, 0);
			close(p[0]);
			if (ft_whatis2(head, q) == 0)
			{
				exit(0);
			}
			else
				rt = hash_get(head->file_args[0], 0);
			if (rt != NULL)
				ft_start_exe(rt, head->file_args, pid);
			exit(0);
		}
		else
		{
			g_pid = pid;
//			setpgid(g_pid, getpgid(g_pid) + 100);
			signal(SIGINT, ft_fork_signal);
//			signal(SIGSTOP, SIG_IGN);/////?????
//			signal(SIGTSTP, ft_fork_signal);////cntrl+Z
//			printf("%spid: %d%s\n", RED, pid, RESET);
			if (g_pid != -1)
				waitpid(pid, &status, 0);
//			printf("%sstatus: %d%s\n", RED, status, RESET);
			if (WIFEXITED(status))
			{
				g_exit_code = WEXITSTATUS(status);
				if (rt == NULL)
					g_exit_code = 127;
//				printf("%sExit status of the child was %d%s\n", YEL, g_exit_code, RESET);
			}
			close(p[1]);
			fd_in = p[0];
			head = (head)->left;
		}
	}
}

Ответ на: комментарий от diosio

Если это поведение отличается от предыдущего, то, видимо, сработало (хотя надо на код возврата смотреть).

Но вообще это не то, что нужно для работы pipe. setpgid(0, 0) создаст группу на процесс, а она должна быть общая для всех процессов в pipeline. Надо делать setpgid(0, PIPEID).

getpgid(g_pid) + 100

Я не уверен, что значение может быть произвольным, а не должно равняться PID одного из процессов в группе. Иначе же лидера не будет.

xaizek ★★★★★ ()
Ответ на: комментарий от xaizek

Теперь setpgid -1 возвращает:(

void	ft_infinit_pipe2(t_exectoken *head, t_memory *q)
{
	int			p[2];
	pid_t		pid;
	int			fd_in;
	char		*rt;
	int			status;
	int			gr_pid;

	fd_in = 0;
	ft_file_create(head);
	rt = NULL;
	gr_pid = getpgrp() + 100;
	while (head)
	{
		if (pipe(p) == -1 || (pid = fork()) == -1)
		{
			ft_putstr_fd("ERROR pipe or fork", 2);
			exit(1);
		}
		else if (pid == 0)
		{
			dprintf(2, "FF: %d\n", setpgid(0, gr_pid));
			if (head->left != NULL)
			{
				dup2(p[1], 1);
				close(p[1]);
			}
			if (head->file_opt)
				ft_fd_flag(head->file_opt, &fd_in);
			dup2(fd_in, 0);
			close(p[0]);
			if (ft_whatis2(head, q) == 0)
			{
				exit(0);
			}
			else
				rt = hash_get(head->file_args[0], 0);
			if (rt != NULL)
				ft_fun_fork(rt, head->file_args, pid);
			exit(0);
		}
		else
		{
			g_pid = pid;
//			setpgid(g_pid, getpgid(g_pid) + 100);
			signal(SIGINT, ft_fork_signal);
//			signal(SIGSTOP, SIG_IGN);/////?????
//			signal(SIGTSTP, ft_fork_signal);////cntrl+Z
//			printf("%spid: %d%s\n", RED, pid, RESET);
			if (g_pid != -1)
				waitpid(pid, &status, 0);
//			printf("%sstatus: %d%s\n", RED, status, RESET);
			if (WIFEXITED(status))
			{
				g_exit_code = WEXITSTATUS(status);
				if (rt == NULL)
					g_exit_code = 127;
				printf("%sExit status of the child was %d%s\n", YEL, g_exit_code, RESET);
			}
			close(p[1]);
			fd_in = p[0];
			head = (head)->left;
		}
	}
}

diosio ()
Ответ на: комментарий от diosio

Теперь setpgid -1 возвращает:(

Не удивлюсь, если из-за

gr_pid = getpgrp() + 100;

gr_pid надо инициализировать нулём, а потом писать туда PID первого потомка, он будет лидером, а остальные дочерние процессы в его группу будут добавляться.

И в случае ошибок с вызовами, надо выводить strerror(errno), чтобы понять, в чём именно дело.

xaizek ★★★★★ ()
Последнее исправление: xaizek (всего исправлений: 1)
Ответ на: комментарий от diosio

Я же, вроде, всё расписал словами. Вот кодом:

gr_pid = 0;
while (head)
{
    if (pipe(p) == -1 || (pid = fork()) == -1)
    {
        // ...
    }
    else if (pid == 0)
    {
        dprintf(2, "FF: %d\n", setpgid(0, gr_pid));
        // ...
    }
    else
    {
        if (gr_pid == 0)
            gr_pid = pid;
xaizek ★★★★★ ()
Ответ на: комментарий от diosio

setpgid(0, gr_pid) в первом потомке это setpgid(0, 0), а этот вызов создаёт новую группу с идентификатором равным идентификатору вызвавшего процесса.

getpgid(pid) нельзя, может быть гонка, так как дочерний процесс ещё мог даже не запуститься.

UPDATE: хотя первый ребёнок может ещё не запуститься до второго, возможно здесь будет ошибка.

xaizek ★★★★★ ()
Последнее исправление: xaizek (всего исправлений: 1)
Ответ на: комментарий от xaizek

Все равно не понимаю что я н так делаю, теперь cntrl+Z и cntrl+C не работают:(

else if (pid == 0)
		{
			//dprintf(2, "FF: %d\n", setpgid(0, gr_pid));
			if (head->left != NULL)
			{
				dup2(p[1], 1);
				close(p[1]);
			}
			if (head->file_opt)
				ft_fd_flag(head->file_opt, &fd_in);
			dup2(fd_in, 0);
			close(p[0]);
			if (ft_whatis2(head, q) == 0)
				exit(0);
			if (rt != NULL)
				ft_fun_fork(rt, head->file_args, pid);
			exit(0);
		}
		else
		{
			if (gr_pid == 0)
			{
				gr_pid = pid;
				setpgid(0, 0);
			}
			else
				setpgid(pid, gr_pid);

diosio ()
Ответ на: комментарий от diosio

Если делать так, то код в else должен быть:

if (gr_pid == 0)
{
    gr_pid = pid;
}
setpgid(pid, gr_pid);

Т.е. мы запоминаем PID первого потомка как идентификатор группы и используем его для всех потомков (включая первого).

xaizek ★★★★★ ()
Ответ на: комментарий от diosio

Или работает и это такой эффект? Надо смотреть коды возврата или ещё как-то проверять. Код тривиальный и не понятно, что здесь не так.

Можно глянуть в исходники bash, чтобы наверняка. Я подозреваю, что там есть дополнительный fork оболочки, которая создаёт группу процессов себе, а дети её просто наследуют автоматически (это должно быть без гонок в принципе).

xaizek ★★★★★ ()
Ответ на: комментарий от xaizek

[qmartina@mi-h3.21-school.ru]$> ls

good setpgid

+#+ Makefile cmake-build-debug inc script.sh 21sh README.md exit3.c libft src 21sh.dSYM a.out hash_table maincc.c tests CMakeLists.txt author history objs

Exit status of the child was 0

[qmartina@mi-h3.21-school.ru]$> ls | cat -en | wc

good setpgid

Exit status of the child was 0

error setpgid

Exit status of the child was 0

error setpgid

  19      38     307

Exit status of the child was 0

вот тут есть коды возврата, setpgid не работает второй раз, а если просто cat вызвать то сразу все дохнет

diosio ()
Ответ на: комментарий от xaizek
void	ft_infinit_pipe2(t_exectoken *head, t_memory *q)
{
	int			p[2];
	pid_t		pid;
	int			fd_in;
	char		*rt;
	int			status;
	int			gr_pid;

	fd_in = 0;
	ft_file_create(head);
	rt = NULL;
	gr_pid = 0;
//	dprintf(2, "getpgrp: %d\n", getpgrp());
	while (head)
	{
		rt = hash_get(head->file_args[0], 0);
		if (pipe(p) == -1 || (pid = fork()) == -1)
		{
			ft_putstr_fd("ERROR pipe or fork", 2);
			exit(1);
		}
		else if (pid == 0)
		{
			//dprintf(2, "FF: %d\n", setpgid(0, gr_pid));
			if (head->left != NULL)
			{
				dup2(p[1], 1);
				close(p[1]);
			}
			if (head->file_opt)
				ft_fd_flag(head->file_opt, &fd_in);
			dup2(fd_in, 0);
			close(p[0]);
			if (ft_whatis2(head, q) == 0)
				exit(0);
			if (rt != NULL)
				ft_fun_fork(rt, head->file_args, pid);
			exit(0);
		}
		else
		{
			if (gr_pid == 0)
			{
				gr_pid = pid;
				//if (setpgid(0, 0) == -1)
				//	dprintf(2, "error if\n");
			}
			if (setpgid(pid, gr_pid) == -1)
				dprintf(2, "error setpgid\n");
			else
				dprintf(2, "good setpgid\n");
			signal(SIGINT, ft_fork_signal);
//			signal(SIGSTOP, SIG_IGN);/////?????
//			signal(SIGTSTP, ft_fork_signal);////cntrl+Z
//			printf("%spid: %d%s\n", RED, pid, RESET);
			waitpid(pid, &status, 0);
//			printf("%sstatus: %d%s\n", RED, status, RESET);
			if (WIFEXITED(status))
			{
				g_exit_code = WEXITSTATUS(status);
//				if (rt == NULL)
//					g_exit_code = 127;
				printf("%sExit status of the child was %d%s\n", YEL, g_exit_code, RESET);
			}
			close(p[1]);
			fd_in = p[0];
			head = (head)->left;
		}
	}
}
diosio ()
Ответ на: комментарий от diosio

Сразу нужно было выводить детали ошибки:

if (setpgid(pid, gr_pid) == -1)
    dprintf(2, "error setpgid: %s\n", strerror(errno));
else
...

Хотя и так похоже, что первый ребёнок завершается очень рано или уже сделал exec().

Кстати, код bash напомнил о tcsetpgrp(), т.е. терминалу нужно сменить текущую активную группу процессов, чтобы они могли с ним работать. Это делает ребёнок, а не родитель.

Вообще, код в bash похож (jobs.c), но он вызывает setpgid() и в родителе и в каждом из потомков, чтобы обойти возможную гонку.

xaizek ★★★★★ ()
pid = fork()
if (pid=0) {
  // это потомок
  // создаем для него новую группу
  // pid потомка будет совпрадать с gpid потомка
  setpgid(0,0);
  // если posix, то просто
  //setpgrp();
  ...
}
if (pid > 0) {
  // это родитель
  // pid потомка будет совпрадать с gpid потомка, см. выше

  // не надо заниматься закатом солнца вручую,
  // назначением своих выдуманных gpid

  // просто запомни pid потомка как gpid
  ...
}

Сигналы STOP и KILL нельзя перехватить, поэтому signal(SIGSTOP, SIG_IGN) - это глупость.

anonymous ()
Ответ на: комментарий от diosio

ioctl и какой fd для него брать

Не знаю за ioctl, разве что на нём можно tcsetpgrp реализовать. fd тот, на котором терминал (0, 1, 2 обычно).

tcsetpgrp это man 3 насколько я понимаю а мне только функциями из man 2 можно пользоваться. :DDDD

Но это же, вроде, сообщает терминалу кому слать Ctrl+Z, иначе его оболочка получит.

Operation not permitted это ошибка

EPERM, видимо (может EACCES), в man setpgid:

An attempt was made to move a process into a process group in a different
session, or to change the process group ID of one of the children of the
calling process and the child was in a different  session, or to change the
process group ID of a session leader (setpgid(), setpgrp()).
xaizek ★★★★★ ()
Ответ на: комментарий от diosio

Я тебе не могу помочь, потому что я не знаю что ты хочешь получить. Я примерно понимаю, что хочешь получить, но я в этом я не разбираюсь.

Итак. У тебя дочерние процессы должны делиться на основной foreground-процесс и фоновые background-процессы. Основной процесс должен взаимодействовать с терминалом, если это интерактивная сессия, в частности должен реагировать ctrl-z. Также тебе надо изучить как работать с (терминальными) сессиями (можешь начать копать с man 2 setsid). Не спрашиай - я не знаю.

anonymous ()