How to Set Process Group of a Shell Script

Process group in bash

In the second example, the for loop is executed in the current, interactive shell, which has job control enabled. Job control requires each job to be in its own process group.

But when you pipe from ls, the for loop is executed in a subshell. Because it's not an interactive shell, there's no job control, so there's no need to put each pipeline in its own process group. So that entire subshell is just a single process group.

How to start a process in its own process group?

What do you mean "start a process in its own process group"? The shell launches processes in their own process groups, that's how it does job control (by having a process group for processes in the foreground, and several process groups for every pipeline launched on the background).

To see that the shell launches a new process group for every pipeline, you can do this:

ps fax -o pid,pgid,cmd | less

which will show something like:

11816 11816  |   \_ /bin/bash
4759 4759 | \_ ps fax -o pid,pgid,cmd
4760 4759 | \_ less

Note that the shell has created a new process group for the pipeline, and every process in the pipeline shares the process group.

Edit:

I think I know what you are getting at. You are calling system from Perl. Apparently, sh -c doesn't create new process groups, since it's a shell without job control.

What I would do would be to fork, then on the child:

setpgrp;
system("ps fax -o pid,pgid,cmd");

and wait on the parent.

What's the best way to send a signal to all members of a process group?

You don't say if the tree you want to kill is a single process group. (This is often the case if the tree is the result of forking from a server start or a shell command line.) You can discover process groups using GNU ps as follows:

 ps x -o  "%p %r %y %x %c "

If it is a process group you want to kill, just use the kill(1) command but instead of giving it a process number, give it the negation of the group number. For example to kill every process in group 5112, use kill -TERM -- -5112.

Bash script to list all processes in the foreground process group of a terminal

To find the pids of all processes in the foreground process group of pts/29, you can do (on linux):

ps ao stat=,pid=,tty=  | awk '$1 ~ /\+/ && $3 ~ /pts\/29/{ print $2}'

ps is often different, and I am uncertain of the portability of that solution.

Using setpgid in a mini-shell breaks interactive commands

The solution is to relinquish control of the tty to the other process group using tcsetpgrp, and when the child is done, take back the control of the tty using tcsetpgrp again. Note that tcsetpgrp sends SIGTTOU to its caller if the calling process belongs to a background process group, so SIGTTOU and SIGTTIN must be blocked.

// error handling is omitted for brevity
// these two signal functions can be anywhere before tcsetpgrp
// alternatively, they can be masked during tcsetpgrp
signal(SIGTTOU, SIG_IGN);
signal(SIGTTIN, SIG_IGN);

if (!builtin_command(argv)) {
if ((pid = Fork()) == 0) {
if (execve(argv[0], argv, environ) < 0) {
execvp(argv[0], argv);
printf("%s: Command not found.\n", argv[0]);
exit(0);
}
}
if (!bg) {
setpgid(pid, 0);
tcsetpgrp(STDERR_FILENO, pid);
int status;
if (waitpid(pid, &status, 0) < 0) {
unix_error("waitfg: waitpid error");
}
tcsetpgrp(STDERR_FILENO, getpgrp());
} else {
printf("%d %s", pid, cmdline);
}
}

This is a rather crude implementation. Consult Craig's comments for this question for where to find bash's implementation.



Related Topics



Leave a reply



Submit