Có biến thể UNIX nào mà quá trình con chết với cha mẹ của nó không?


41

Tôi đã nghiên cứu hành vi nhân Linux khá lâu rồi và tôi luôn thấy rõ rằng:

Khi một quá trình chết, tất cả các con của nó được đưa trở lại initquy trình (PID 1) cho đến khi cuối cùng chúng chết.

Tuy nhiên, gần đây, một người có nhiều kinh nghiệm hơn tôi với kernel đã nói với tôi rằng:

Khi một quá trình thoát ra, tất cả các con của nó cũng chết (trừ khi bạn sử dụng NOHUPtrong trường hợp chúng quay lại init).

Bây giờ, mặc dù tôi không tin điều này, tôi vẫn viết một chương trình đơn giản để đảm bảo về nó. Tôi biết tôi không nên dựa vào thời gian ( sleep) cho các bài kiểm tra vì tất cả phụ thuộc vào lập lịch xử lý, nhưng đối với trường hợp đơn giản này, tôi nghĩ rằng điều đó là khá đủ.

int main(void){
    printf("Father process spawned (%d).\n", getpid());
    sleep(5);

    if(fork() == 0){
        printf("Child process spawned (%d => %d).\n", getppid(), getpid());
        sleep(15);
        printf("Child process exiting (%d => %d).\n", getppid(), getpid());
        exit(0);
    }

    sleep(5);
    printf(stdout, "Father process exiting (%d).\n", getpid());
    return EXIT_SUCCESS;
}

Đây là đầu ra của chương trình, với kết psquả được liên kết mỗi lần printfnói chuyện:

$ ./test &
Father process spawned (435).

$ ps -ef | grep test
myuser    435    392   tty1    ./test

Child process spawned (435 => 436).

$ ps -ef | grep test
myuser    435    392   tty1    ./test
myuser    436    435   tty1    ./test

Father process exiting (435).

$ ps -ef | grep test
myuser    436    1     tty1    ./test

Child process exiting (436).

Bây giờ, như bạn có thể thấy, điều này hoạt động hoàn toàn như tôi mong đợi. Quá trình mồ côi (436) được đưa trở lại init(1) cho đến khi nó chết.

Tuy nhiên, có bất kỳ hệ thống dựa trên UNIX nào mà hành vi này không áp dụng theo mặc định không? Có hệ thống nào mà cái chết của một quá trình ngay lập tức kích hoạt cái chết của tất cả trẻ em của nó không?

Câu trả lời:


68

Khi một quá trình thoát ra, tất cả các con của nó cũng chết (trừ khi bạn sử dụng NOHUP trong trường hợp chúng quay lại init).

Cái này sai. Hoàn toàn sai. Người nói đó là nhầm lẫn, hoặc nhầm lẫn một tình huống cụ thể với trường hợp chung.

Có hai cách mà cái chết của một quá trình có thể gián tiếp gây ra cái chết của những đứa trẻ của nó. Chúng có liên quan đến những gì xảy ra khi một thiết bị đầu cuối được đóng lại. Khi một thiết bị đầu cuối biến mất (theo lịch sử vì đường nối tiếp bị cắt do treo modem, ngày nay thường là do người dùng đóng cửa sổ mô phỏng thiết bị đầu cuối), tín hiệu SIGHUP được gửi đến quá trình điều khiển đang chạy trong thiết bị đầu cuối đó - thông thường, lớp vỏ ban đầu bắt đầu trong thiết bị đầu cuối đó. Vỏ thường phản ứng với điều này bằng cách thoát ra. Trước khi thoát, các shell dự định sử dụng tương tác sẽ gửi HUP đến từng công việc mà chúng bắt đầu.

Bắt đầu một công việc từ một vỏ với sự nohupphá vỡ nguồn tín hiệu HUP thứ hai bởi vì công việc sau đó sẽ bỏ qua tín hiệu và do đó không được thông báo là chết khi thiết bị đầu cuối biến mất. Các cách khác để phá vỡ sự truyền tín hiệu HUP từ vỏ sang các công việc bao gồm sử dụng hàm disowndựng sẵn của vỏ nếu nó có (công việc được xóa khỏi danh sách công việc của vỏ) và tách đôi (vỏ khởi động một đứa trẻ sẽ khởi chạy một đứa trẻ của riêng nó và thoát ra ngay lập tức, vỏ không có kiến ​​thức về cháu của nó).

Một lần nữa, các công việc bắt đầu trong thiết bị đầu cuối chết không phải vì quá trình cha mẹ của chúng (vỏ) mà vì quy trình cha mẹ của chúng quyết định giết chúng khi được yêu cầu giết chúng. Và lớp vỏ ban đầu trong thiết bị đầu cuối chết không phải vì quá trình mẹ của nó chết, mà bởi vì thiết bị đầu cuối của nó biến mất (điều này có thể hoặc không phải ngẫu nhiên là do thiết bị đầu cuối được cung cấp bởi trình giả lập thiết bị đầu cuối là quy trình mẹ của trình bao).


1
Có một cách thứ ba, một cái gì đó với các nhóm kiểm soát. Tất cả những gì tôi biết là systemd sử dụng nó để thực thi các lệnh giết sạch.
o11c

5
@JanHudec Tôi nghĩ rằng bạn đang bối rối nohupvới disown. disownlà một phần tử dựng sẵn trong một số shell loại bỏ một công việc đang chạy khỏi bảng công việc (lấy một đối số như thế %1) và tác dụng phụ hữu ích chính của nó là shell sẽ không gửi SIGHUP đến quy trình con. nohuplà một tiện ích bên ngoài bỏ qua SIGHUP (và thực hiện một số thứ khác) sau đó bắt đầu lệnh được truyền trên dòng lệnh của nó.
Gilles 'SO- ngừng trở nên xấu xa'

@Gilles: Không, tôi đã không, nhưng nhìn vào strace nohuptrong thực tế không bỏ qua tín hiệu trước khi nó execlà lệnh.
Jan Hudec

Cảm ơn bạn vì câu trả lời! Tôi đặc biệt thích một chút về bối cảnh lịch sử liên quan đến việc treo modem. Tôi đã bắt đầu lo lắng rằng tôi đã hiểu nhầm hạt nhân suốt thời gian này ...
John WH Smith

3
Cũng cần lưu ý rằng một chương trình thoát khi nhận được SIGHUP vì trình xử lý tín hiệu mặc định cho SIGHUP là để thoát. Nhưng bất kỳ chương trình nào cũng có thể thực hiện trình xử lý SIGHUP của riêng nó, điều này có thể khiến nó không thoát khi trình vỏ mẹ gửi SIGHUP.
slebetman

12

Khi một quá trình thoát ra, tất cả các con của nó cũng chết (trừ khi bạn sử dụng NOHUP trong trường hợp chúng quay lại init).

Điều này là chính xác nếu quá trình là một nhà lãnh đạo phiên. Khi một nhà lãnh đạo phiên chết, SIGHUP được gửi đến tất cả các thành viên của phiên đó. Trong thực tế có nghĩa là con cái của nó và con cháu của họ.

Một quá trình làm cho chính nó dẫn đầu phiên bằng cách gọi setsid. Vỏ sử dụng cái này.


Tôi vừa mới chạy thử nghiệm về vấn đề này, nhưng tôi không thể tái tạo hành vi mà bạn mô tả ... Tôi đã tạo ra một quy trình, đưa ra một phiên mới ( fork, giết quy trình phụ huynh setsid) và đưa một đứa trẻ khác vào phiên mới này. Tuy nhiên, khi quá trình cha mẹ (người lãnh đạo phiên mới) qua đời, đứa trẻ không nhận được SIGHUP và gắn bó cho initđến khi nó thoát ra.
John WH Smith

Từ setpgid(2)trang chủ: Nếu phiên có thiết bị đầu cuối kiểm soát và cờ CLOCAL cho thiết bị đầu cuối đó không được đặt và tình trạng treo thiết bị đầu cuối xảy ra, thì người lãnh đạo phiên được gửi SIGHUP. Nếu người lãnh đạo phiên thoát, thì tín hiệu SIGHUP cũng sẽ được gửi đến từng quy trình trong nhóm quy trình tiền cảnh của thiết bị đầu cuối điều khiển.
ninjalj

1
@ninjalj Sau đó, tôi đoán câu trả lời này là hợp lệ khi và chỉ khi người lãnh đạo phiên cũng là quá trình kiểm soát của thiết bị đầu cuối. Một người lãnh đạo phiên chuẩn, độc lập với bất kỳ thiết bị đầu cuối nào, sẽ không giết chết con của nó. Nó có đúng không?
John WH Smith

-1

Vì vậy, những gì các áp phích trên đang nói là, những đứa trẻ không chết, cha mẹ giết chúng (hoặc gửi cho chúng một tín hiệu mà chúng chấm dứt). Vì vậy, bạn có thể có những gì bạn yêu cầu, nếu bạn lập trình cho Phụ huynh (1) ghi lại tất cả các con của nó và (2) gửi tín hiệu cho tất cả các con của nó.

Đây là những gì Shell làm, và nó phải là những gì quá trình cha mẹ của bạn làm. Có thể cần phải bắt tín hiệu HUP ở phụ huynh để bạn vẫn có đủ quyền kiểm soát để giết chết trẻ em.


Nhiều nguyên nhân có thể làm cho cha mẹ chết. Chẳng có cách nào để ngăn chặn sự sống còn của con cái nếu cha mẹ bị SIGKILLed chẳng hạn.
Emil Jeřábek hỗ trợ Monica

-1

Tôi thiếu một chút trong các câu trả lời hơi liên quan đến cha mẹ sắp chết: khi một quá trình viết trên một đường ống mà không có quá trình đọc nữa, nó nhận được SIGPIPE. Hành động tiêu chuẩn cho SIGPIPE là chấm dứt.

Điều này thực sự có thể gây ra quá trình chết. Trong thực tế, đó là cách tiêu chuẩn mà chương trình yeschết.

Nếu tôi thực thi

(yes;echo $? >&2)|head -10

trên hệ thống của tôi, câu trả lời là

y
y
y
y
y
y
y
y
y
y
141

và 141 thực sự là 128 + SIGPIPE:

   SIGPIPE      13       Term    Broken pipe: write to pipe with no
                                 readers

từ man 7 signal.


2
Nhưng quá trình đọc từ đường ống không nhất thiết (hoặc thậm chí thường là) cha mẹ của nó. Vì vậy, đây thực sự là một trường hợp rất khác với những gì câu hỏi đang hỏi về.
Barmar
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.