setpgid
Ví dụ tối thiểu nhóm quy trình POSIX C
Có thể dễ hiểu hơn với một ví dụ có thể chạy tối thiểu của API cơ bản.
Điều này minh họa cách tín hiệu được gửi đến trẻ, nếu trẻ không thay đổi nhóm quy trình setpgid
.
C chính
#define _XOPEN_SOURCE 700
#include <assert.h>
#include <signal.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
volatile sig_atomic_t is_child = 0;
void signal_handler(int sig) {
char parent_str[] = "sigint parent\n";
char child_str[] = "sigint child\n";
signal(sig, signal_handler);
if (sig == SIGINT) {
if (is_child) {
write(STDOUT_FILENO, child_str, sizeof(child_str) - 1);
} else {
write(STDOUT_FILENO, parent_str, sizeof(parent_str) - 1);
}
}
}
int main(int argc, char **argv) {
pid_t pid, pgid;
(void)argv;
signal(SIGINT, signal_handler);
signal(SIGUSR1, signal_handler);
pid = fork();
assert(pid != -1);
if (pid == 0) {
is_child = 1;
if (argc > 1) {
/* Change the pgid.
* The new one is guaranteed to be different than the previous, which was equal to the parent's,
* because `man setpgid` says:
* > the child has its own unique process ID, and this PID does not match
* > the ID of any existing process group (setpgid(2)) or session.
*/
setpgid(0, 0);
}
printf("child pid, pgid = %ju, %ju\n", (uintmax_t)getpid(), (uintmax_t)getpgid(0));
assert(kill(getppid(), SIGUSR1) == 0);
while (1);
exit(EXIT_SUCCESS);
}
/* Wait until the child sends a SIGUSR1. */
pause();
pgid = getpgid(0);
printf("parent pid, pgid = %ju, %ju\n", (uintmax_t)getpid(), (uintmax_t)pgid);
/* man kill explains that negative first argument means to send a signal to a process group. */
kill(-pgid, SIGINT);
while (1);
}
GitHub ngược dòng .
Biên dịch với:
gcc -ggdb3 -O0 -std=c99 -Wall -Wextra -Wpedantic -o setpgid setpgid.c
Chạy mà không có setpgid
Nếu không có bất kỳ đối số CLI nào, setpgid
sẽ không được thực hiện:
./setpgid
Kết quả có thể xảy ra:
child pid, pgid = 28250, 28249
parent pid, pgid = 28249, 28249
sigint parent
sigint child
và chương trình bị treo.
Như chúng ta có thể thấy, pgid của cả hai quá trình là như nhau, vì nó được kế thừa qua fork
.
Sau đó, bất cứ khi nào bạn nhấn:
Ctrl + C
Nó xuất ra một lần nữa:
sigint parent
sigint child
Điều này cho thấy làm thế nào:
- để gửi tín hiệu đến toàn bộ nhóm quy trình với
kill(-pgid, SIGINT)
- Ctrl + C trên thiết bị đầu cuối gửi lệnh giết cho toàn bộ nhóm quy trình theo mặc định
Thoát khỏi chương trình bằng cách gửi tín hiệu khác nhau cho cả hai quá trình, ví dụ SIGQUIT với Ctrl + \
.
Chạy với setpgid
Nếu bạn chạy với một đối số, ví dụ:
./setpgid 1
sau đó đứa trẻ thay đổi pgid của nó, và bây giờ chỉ có một sigint duy nhất được in mỗi lần từ cha mẹ:
child pid, pgid = 16470, 16470
parent pid, pgid = 16469, 16469
sigint parent
Và bây giờ, bất cứ khi nào bạn nhấn:
Ctrl + C
chỉ có cha mẹ nhận được tín hiệu là tốt:
sigint parent
Bạn vẫn có thể giết cha mẹ như trước với SIGQUIT:
Ctrl + \
tuy nhiên hiện tại trẻ có PGID khác và không nhận được tín hiệu đó! Điều này có thể thấy từ:
ps aux | grep setpgid
Bạn sẽ phải giết nó một cách rõ ràng với:
kill -9 16470
Điều này cho thấy rõ lý do tại sao các nhóm tín hiệu tồn tại: nếu không chúng ta sẽ có một loạt các quy trình còn sót lại để được làm sạch thủ công mọi lúc.
Đã thử nghiệm trên Ubuntu 18.04.