2011-01-17 25 views
13

Tôi có một kịch bản Perl mà dĩa.Điều gì xảy ra với SIGINT (^ C) khi được gửi đến một kịch bản perl có chứa trẻ em?

Mỗi ngã ba chạy một chương trình bên ngoài, phân tích đầu ra và chuyển đổi đầu ra thành tệp Storable.

Các tệp đáng tin cậy sau đó được đọc bởi phụ huynh và tổng dữ liệu từ mỗi trẻ được phân tích trước khi tiếp tục lặp lại của ngã ba trước đó hoặc người khác dừng lại.

Điều gì xảy ra chính xác khi tôi phát hành^C trong khi một số trẻ vẫn đang chạy chương trình bên ngoài? Các kịch bản mẹ perl được gọi là ở phía trước và, tôi đoán, vẫn ở phía trước mặc dù forking.

SIGINT có được truyền cho tất cả trẻ em, đó là cha mẹ, con của cha mẹ và chương trình bên ngoài được gọi bởi trẻ em không ??

CẬP NHẬT:

tôi nên thêm, dường như khi tôi hành SIGINIT, các chương trình bên ngoài gọi bằng con cái kịch bản của tôi dường như thừa nhận tín hiệu và chấm dứt. Nhưng trẻ em, hoặc có lẽ là chương trình phụ huynh, tiếp tục. Điều này là tất cả không rõ ràng với tôi.

UPDATE 2:

đối với bình luận tchrist hề có, sự chương trình bên ngoài được gọi với lệnh Perl của system().

Trong thực tế, nhận xét của tchrist cũng dường như chứa lời giải thích mà tôi đang tìm kiếm. Sau khi một số gỡ lỗi nhiều hơn, dựa trên hành vi của chương trình của tôi, có vẻ như, SIGINT đang được chuyển từ cha mẹ cho tất cả trẻ em và từ tất cả trẻ em cho tất cả con cái của họ (chương trình bên ngoài).

Vì vậy, những gì dường như đang xảy ra, dựa trên nhận xét của tchrist, là CTRL-C đang giết chết chương trình bên ngoài khiến trẻ di chuyển ra khỏi lệnh system() - và không có gì khác.

Mặc dù tôi có con tôi kiểm tra trạng thái thoát của những gì được gọi trong system(), tôi giả định rằng CTRL-C sẽ giết mọi thứ từ cấp độ gốc, thay vì dẫn đến việc tạo nhiều vòng xử lý hơn chuyện gì đã xảy ra!!!

SOLUTION (cho vấn đề của tôi):

tôi cần phải chỉ cần tạo một handler tín hiệu cho SIGINT trong các phụ huynh. Sau đó, trình xử lý tín hiệu sẽ gửi SIGTERM đến từng đứa trẻ (mà tôi cho là sẽ gửi một SIGTERM tới các trẻ em), và sau đó khiến cha mẹ thoát khỏi một cách duyên dáng. Mặc dù giải pháp hơi rõ ràng này có khả năng sẽ có những thứ cố định, tôi muốn hiểu quan niệm sai lầm của tôi về hành vi của SIGINT liên quan đến việc làm giả trong Perl.

+2

Bạn chưa biết bạn có đang tung phiên bản 'system()' của riêng mình hay một đường ống mở hoặc backticks hay cho dù bạn đang sử dụng Perl hay không. Nếu bạn đang sử dụng Perl, thì cha/mẹ - người gọi là 'hệ thống' không phải là người được gọi bởi nó - sẽ loại bỏ bất kỳ SIGINT và SIGQUIT nào trong khi các trẻ đang chạy. Nếu bạn đã tự mình cuộn, bạn phải tự mình nghĩ về những vấn đề này. Hãy xem xét điều gì sẽ xảy ra khi bạn sử dụng 'system (" vi somefile ")' và nhấn^C trong một tìm kiếm dài trong 'vi': chỉ' vi' có một SIGINT (nonfatal); cha mẹ bỏ qua nó. Đây là hành vi đúng. – tchrist

+0

Cảm ơn, tchrist. Đặt câu trả lời này thành câu trả lời để tôi có thể đánh dấu câu trả lời là đã được chấp nhận. – EMiller

Trả lời

12

Hàm dựng sẵn của Perl system hoạt động giống như chức năng C hệ thống (3) từ thư viện C chuẩn khi có liên quan đến tín hiệu. Nếu bạn đang sử dụng phiên bản system() hoặc đường ống mở của Perl, thì phụ huynh - người gọi số system thay vì được gọi bởi số đó - sẽ BỎ L anyI bất kỳ SIGINT và SIGQUIT nào trong khi trẻ đang chạy. Nếu bạn đã tự cuộn của mình bằng cách sử dụng một số biến thể của bộ ba fork?wait:exec, thì bạn phải tự mình nghĩ về những vấn đề này.

Hãy xem xét điều gì sẽ xảy ra khi bạn sử dụng system("vi somefile") và nhấn^C trong một tìm kiếm dài trong vi: chỉ vi mất SIGINT (nonfatal); cha mẹ bỏ qua nó. Đây là hành vi đúng. Đó là lý do tại sao C làm việc theo cách này, và đó là lý do tại sao Perl hoạt động theo cách này. Điều bạn cần nhớ là chỉ vì^C gửi SIGINT đến tất cả các quy trình trong nhóm tiến trình nền trước (ngay cả những quy trình khác nhau có hiệu quả UID hoặc GID), điều đó có nghĩa là không phải là có nghĩa là nó gây ra tất cả quy trình để thoát. A^C chỉ là một SIGINT, có nghĩa là để ngắt một quy trình, không phải là SIGKILL, có nghĩa là chấm dứt mà không có câu hỏi nào.

Có nhiều loại chương trình sẽ sai khi chỉ cần tắt mà không có cảnh báo; một biên tập viên chỉ là một ví dụ như vậy. Một bưu phẩm có thể khác. Hãy hết sức thận trọng về điều này.

Nhiều loại chương trình có chọn lọc bỏ qua, bẫy hoặc chặn (có nghĩa là gửi chậm trễ) các loại tín hiệu khác nhau. Chỉ có hành vi mặc định của SIGINT là làm cho quá trình thoát ra.Bạn có thể tìm hiểu xem điều này xảy ra, và trong thực tế mà tín hiệu gây ra nó xảy ra (trong số những thứ khác), với loại mã này trên hệ điều hành truyền thống:

if ($wait_status = system("whatever")) { 
    $sig_killed = $wait_status & 127; 
    $did_coredump = $wait_status & 128; 
    $exit_status = $wait_status >> 8; 
    # now do something based on that... 
} 

Lưu ý cẩn thận rằng a^C'd vi, ví dụ: không phải có từ trạng thái chờ cho biết từ đó đã chết từ SIGINT chưa được tháo dỡ, vì không có từ nào: nó đã bắt được nó.

Thỉnh thoảng các con của bạn sẽ đi và có con của riêng bạn sau lưng bạn. Lộn xộn nhưng đúng. Vì vậy, tôi đã được biết, nhân dịp, để diệt chủng tất cả các thế hệ con cháu biết và chưa biết theo cách này:

# scope to temporize (save+restore) any previous value of $SIG{HUP} 
{ 
    local $SIG{HUP} = "IGNORE"; 
    kill HUP => -$$; # the killpg(getpid(), SIGHUP) syscall 
} 

Điều đó tất nhiên không làm việc với SIGKILL hoặc SIGSTOP, mà không tuân theo bị bỏ qua như thế.

Một vấn đề khác mà bạn có thể muốn cẩn thận là trước bản phát hành 5.8, xử lý tín hiệu trong Perl trước đây không phải là hoạt động an toàn đáng tin cậy. Bây giờ là bây giờ, nhưng đây là vấn đề phụ thuộc vào phiên bản. Nếu bạn chưa làm như vậy, thì bạn chắc chắn nên đọc trên deferred signals in the perlipc manpage và có lẽ cũng trên PERL_SIGNALS envariable in the perlrun manpage.

2

Nếu bạn giết quá trình cha mẹ, trẻ em (và các chương trình bên ngoài đang chạy) sẽ vẫn chạy cho đến khi chúng kết thúc bằng cách này hay cách khác. Điều này có thể tránh được nếu cha mẹ có một trình xử lý tín hiệu bắt SIGINT, và sau đó giết chết nhóm tiến trình (thường là pid của cha mẹ). Bằng cách đó, một khi cha mẹ nhận được SIGINT, nó sẽ giết chết tất cả những đứa trẻ của nó.

Vì vậy, để trả lời câu hỏi của bạn, tất cả phụ thuộc vào việc triển khai trình xử lý tín hiệu.Dựa trên cập nhật của bạn, có vẻ như cha mẹ thực sự giết chết con của nó và sau đó thay vì chấm dứt chính nó, nó quay trở lại để làm một cái gì đó khác (nghĩ về nó như là một thiết lập lại thay vì một kết thúc tắt/bắt đầu hoàn thành).

+0

Trong hành vi tiêu chuẩn này? Tôi đã không đưa bất kỳ trình xử lý tín hiệu nào vào chương trình của tôi. Tôi nghĩ rằng một điều khiển C sẽ giết tất cả mọi thứ và tôi bị sốc rằng đây không phải là trường hợp. – EMiller

+0

Hành vi mặc định cho SIGINT là chấm dứt. Nhưng kể từ khi cha mẹ của bạn không làm điều đó, và bạn không có bất kỳ xử lý hoặc đã tự thay đổi ID của, sau đó cái gì khác đang xảy ra. Nó có thể được rằng các chương trình bên ngoài cũng đang forking? – Milan

8

Khi bạn nhấn^C trong cửa sổ đầu cuối (hoặc bất kỳ đầu cuối nào, cho vấn đề đó), nó sẽ gửi SIGINT đến tiến trình nền trước GROUP trong thiết bị đầu cuối đó. Bây giờ khi bạn bắt đầu một chương trình từ dòng lệnh, nó thường nằm trong nhóm tiến trình riêng của nó và trở thành nhóm tiến trình tiền cảnh. Theo mặc định, khi bạn nĩa một đứa trẻ, nó sẽ nằm trong cùng một nhóm tiến trình với tư cách cha mẹ, do đó, theo mặc định, cha mẹ (chương trình cấp cao nhất được gọi từ dòng lệnh), tất cả các con của nó, con cái, vv, cũng như bất kỳ chương trình bên ngoài nào được gọi bởi bất kỳ chương trình nào trong số này (tất cả chỉ là trẻ em) đều nằm trong cùng một nhóm tiến trình đó, vì vậy tất cả sẽ nhận được tín hiệu SIGINT. Tuy nhiên, nếu bất kỳ trẻ em hoặc chương trình nào gọi setpgrp hoặc setpgid hoặc setsid hoặc bất kỳ cuộc gọi nào khác khiến quá trình trở thành một nhóm tiến trình mới, thì các quy trình đó (và mọi trẻ em chúng bắt đầu sau khi rời khỏi quá trình nền trước). nhóm) sẽ KHÔNG nhận được SIGINT.

Ngoài ra, khi một quá trình nhận được SIGINT, nó có thể không chấm dứt - nó có thể bỏ qua tín hiệu, hoặc nó có thể bắt nó và làm điều gì đó hoàn toàn khác. Trình xử lý tín hiệu mặc định cho SIGINT chấm dứt quá trình, nhưng đó chỉ là mặc định có thể được ghi đè.

chỉnh sửa

Từ cập nhật của bạn, có vẻ như mọi thứ đang còn lại trong nhóm quá trình tương tự, vì vậy các tín hiệu được phân phối tới tất cả mọi người, như cháu (các chương trình bên ngoài) đều được thoát khỏi, nhưng những đứa trẻ đang bắt và bỏ qua SIGINT. Theo nhận xét của tchrist, có vẻ như đây là hành vi mặc định cho perl.