Chương 20 · Tín hiệu — khái niệm nền tảng Chapter 20 · Signals: Fundamental Concepts
Tín hiệu là gì, và một tiến trình phản ứng với nó ra sao Tín hiệu là gì, và máy làm gì khi nhận được một tín hiệu What a signal is, and how a process responds to one
Kerrisk định nghĩa tín hiệu (signal) là một thông báo gửi tới tiến trình rằng một sự kiện đã xảy ra — đôi khi được gọi là "ngắt phần mềm" (software interrupt), vì nó cắt ngang luồng chạy bình thường và thường không thể đoán trước thời điểm đến.12 Chương đi qua: khái niệm và vòng đời của tín hiệu; các loại tín hiệu chuẩn (1–31) cùng hành động mặc định của chúng; cách đổi disposition bằng signal() và sigaction() (được ưu tiên); cách gửi tín hiệu với kill(), raise(), killpg(); cách kiểm tra một tiến trình còn tồn tại; quyền cần có để gửi; cách mô tả tín hiệu với strsignal()/psignal(); tập tín hiệu, mặt nạ tín hiệu và việc chặn (sigprocmask); tín hiệu đang chờ (sigpending); việc tín hiệu chuẩn không xếp hàng; và chờ tín hiệu bằng pause().1
Hãy tưởng tượng bạn đang làm việc thì có người gõ cửa. Tiếng gõ ấy là một tín hiệu: nó báo "có chuyện xảy ra" và cắt ngang việc bạn đang làm. Tiến trình trong máy cũng vậy — nó có thể bị một tín hiệu gõ cửa bất cứ lúc nào, và nó phải quyết định: lờ đi, làm theo mặc định, hay chạy một việc riêng để đáp lại. Chương này dạy cách nhận biết, gửi đi, chặn lại và chờ đợi những tiếng gõ cửa ấy. Nhớ nhé: tín hiệu là một tiếng gõ cửa báo "có chuyện" cho tiến trình.
Kerrisk defines a signal as a notification to a process that an event has occurred — sometimes called a "software interrupt," because it cuts across a program's normal flow and usually cannot be predicted in advance.12 The chapter walks through: the concept and lifecycle of signals; the standard signal types (1–31) and their default actions; changing a signal's disposition with signal() and the preferred sigaction(); sending signals with kill(), raise(), killpg(); checking whether a process still exists; the permissions required to send; describing signals with strsignal()/psignal(); signal sets, the signal mask and blocking (sigprocmask); pending signals (sigpending); the fact that standard signals are not queued; and waiting for a signal with pause().1
Góc nhìn của nhân: một tín hiệu đi qua một vòng đời rõ rệt. Nó được sinh ra (generated) khi một sự kiện xảy ra — do nhân, do một tiến trình khác, hay do phần cứng; rồi nó được phát (delivered) tới tiến trình đích, lúc đó tiến trình thực hiện hành động tương ứng với disposition hiện thời của tín hiệu. Khoảng giữa sinh và phát, tín hiệu ở trạng thái đang chờ (pending); và nếu tín hiệu đó đang bị chặn (blocked) bởi mặt nạ tín hiệu của tiến trình, nó cứ nằm chờ cho tới khi được bỏ chặn.12The kernel's view: a signal passes through a distinct lifecycle. It is generated when an event occurs — by the kernel, by another process, or by hardware; then it is delivered to the target process, at which point the process performs the action dictated by the signal's current disposition. Between generation and delivery the signal is pending; and if that signal is currently blocked by the process's signal mask, it remains pending until it is unblocked.12
Luận đề của chương: một tín hiệu là một cơ chế thông báo bất đồng bộ do nhân điều phối; làm chủ được nó nghĩa là làm chủ bốn việc — phân loại các tín hiệu và biết hành động mặc định của chúng, đổi cách tiến trình phản ứng (disposition) một cách khả chuyển qua sigaction(), gửi tín hiệu đúng quyền với kill(), và trì hoãn việc phát bằng mặt nạ tín hiệu — tất cả trong khi nhớ rằng tín hiệu chuẩn không xếp hàng.1
Ý chính: tín hiệu là tiếng gõ cửa bất ngờ từ bên ngoài luồng chạy của chương trình. Hiểu một tín hiệu là biết: nó mặc định làm gì, làm sao đổi cách đáp lại, làm sao tự gửi, và làm sao bảo "khoan đã" để nó chờ. Nhớ nhé: tín hiệu là tiếng gõ cửa — bạn chọn cách mở, cách gửi, và cách bảo nó đợi.
The chapter's thesis: a signal is an asynchronous notification mechanism orchestrated by the kernel; mastering it means mastering four things — classifying signals and knowing their default actions, changing how a process reacts (its disposition) portably via sigaction(), sending signals with the right permissions via kill(), and deferring delivery with the signal mask — all while remembering that standard signals are not queued.1
Cụm khái niệm 01 Concept cluster 01
Một tín hiệu đi qua vòng đời sinh–chờ–phát — và mỗi loại có một hành động mặc định Tín hiệu sinh ra, chờ, rồi được trao — và mỗi loại có cách xử mặc định A signal has a generate–pending–deliver lifecycle — and each type has a default action
Mỗi tín hiệu là một số nguyên nhỏ, định danh duy nhất, bắt đầu tuần tự từ 1, với tên biểu tượng dạng SIGxxxx định nghĩa trong <signal.h>.1213 Vì con số thực tế khác nhau giữa các hệ, mã nên dùng tên chứ không dùng số. Trên Linux, các tín hiệu chuẩn (standard) được đánh số từ 1 đến 31; còn một tập khác là tín hiệu thời gian thực (realtime) — chương này mô tả tín hiệu chuẩn.1 Khi một tín hiệu đến mà tiến trình chưa thiết lập cách xử riêng, nhân thực thi một trong các hành động mặc định: kết thúc tiến trình (term), kết thúc kèm dump lõi (core), bỏ qua (ign), dừng (stop), hoặc tiếp tục chạy (cont).12
Mỗi loại tín hiệu có một con số và một cái tên, ví dụ SIGINT là tín hiệu ngắt khi bạn bấm Ctrl-C. Có khoảng ba mươi loại tín hiệu "cổ điển" (số 1 đến 31). Khi một tín hiệu tới mà bạn chưa dặn gì, máy làm theo "nếp mặc định" của loại đó: có loại khiến chương trình tắt ngay, có loại bị lờ đi, có loại khiến chương trình tạm dừng rồi chạy tiếp. Nhớ nhé: mỗi tín hiệu có tên, có số, và có một nếp xử mặc định nếu bạn không dặn gì khác.
Each signal is a unique small integer, numbered sequentially from 1, with a symbolic name of the form SIGxxxx defined in <signal.h>.1213 Because the actual numbers vary across implementations, code should use the names, not the numbers. On Linux the standard signals are numbered 1 to 31; a separate set are the realtime signals — this chapter covers the standard ones.1 When a signal arrives and the process has set no handling of its own, the kernel performs one of the default actions: terminate the process (term), terminate with a core dump (core), ignore (ign), stop (stop), or resume (cont).12
Định nghĩa. Disposition của một tín hiệu là cái mà tiến trình quy định sẽ xảy ra khi tín hiệu đó được phát: để nguyên hành động mặc định, bỏ qua tín hiệu (SIG_IGN), hoặc chạy một trình xử lý tín hiệu (signal handler) do lập trình viên viết (SIG_DFL là khôi phục về mặc định).13
Nói đơn giản. "Disposition" là lời dặn trước: khi tín hiệu này tới thì làm gì — cứ làm mặc định, lờ nó đi, hay chạy một đoạn việc riêng của tôi. Nhớ nhé: disposition là lời dặn "khi gõ cửa thì làm thế này".
Definition. A signal's disposition is what the process specifies should happen when that signal is delivered: leave the default action, ignore the signal (SIG_IGN), or run a programmer-written signal handler (SIG_DFL restores the default).13
sự kiện xảy ra
↓ generated
[ đang chờ · pending ] ← bị chặn? (mask)
↓ delivered
hành động = term | core | ign | stop | cont
hoặc handler / SIG_IGN
có chuyện xảy ra
→ sinh tín hiệu
→ (có thể phải chờ)
→ được trao → làm gì đó
(tắt / lờ / chạy việc riêng)
event occurs
↓ generated
[ pending ] ← blocked? (mask)
↓ delivered
action = term | core | ign | stop | cont
or handler / SIG_IGN
-
software interrupt
Tín hiệu là bất đồng bộ — không đoán được lúc đến Tín hiệu đến bất chợt, không báo trước Signals are asynchronous — you can't predict their arrival
Tín hiệu giống ngắt phần cứng ở chỗ nó cắt ngang luồng chạy bình thường; trong hầu hết trường hợp không thể biết trước chính xác khi nào một tín hiệu sẽ tới.1 Các nguồn sinh tín hiệu điển hình gồm: một ngoại lệ phần cứng (ví dụ chia cho 0 hay tham chiếu địa chỉ sai), người dùng gõ một ký tự đặc biệt trên terminal (như Ctrl-C sinh
SIGINT), hay một tiến trình khác chủ động gửi.12Tín hiệu cắt ngang việc chương trình đang làm, giống như chuông điện thoại reo giữa bữa ăn. Nó có thể do một lỗi phần cứng (chia cho 0), do bạn bấm một phím đặc biệt (Ctrl-C), hay do một chương trình khác gửi sang. Nhớ nhé: tín hiệu đến bất chợt, giữa chừng việc đang làm.
A signal resembles a hardware interrupt in that it interrupts normal flow; in most cases you cannot know in advance exactly when one will arrive.1 Typical sources are: a hardware exception (such as dividing by zero or a bad memory reference), the user typing a terminal special character (like Ctrl-C generating
SIGINT), or another process deliberately sending one.12Bạn gặp nó ở đâuWhere you meet it Mỗi khi bạn bấm Ctrl-C để dừng một lệnh đang chạy ở terminal — đó làSIGINTđược sinh và phát tới tiến trình tiền cảnh.2 Khi bấm Ctrl-C để dừng một lệnh — đó chính là một tín hiệu đang được gửi đi. Every time you press Ctrl-C to stop a running command in the terminal — that isSIGINTbeing generated and delivered to the foreground process.2Chuẩn và thời gian thựcStandard vs realtime
Tín hiệu rơi vào hai nhóm: nhóm chuẩn (số 1–31 trên Linux), dùng để nhân báo các sự kiện truyền thống; và nhóm thời gian thực được thêm sau, có nhiều điểm khác — đáng chú ý nhất là tín hiệu thời gian thực được xếp hàng, còn tín hiệu chuẩn thì không.12Signals fall into two groups: the standard set (numbered 1–31 on Linux), used by the kernel for traditional events; and the later realtime set, which differs in several ways — most notably, realtime signals are queued, whereas standard signals are not.12
Dùng tên, đừng dùng sốUse names, not numbers
Vì số hiệu của mỗi tín hiệu khác nhau giữa các hiện thực UNIX, chương trình khả chuyển luôn tham chiếu tín hiệu bằng tên biểu tượng (
SIGINT,SIGTERM…) chứ không bằng số thô.1Because each signal's number varies across UNIX implementations, portable programs always refer to signals by their symbolic names (SIGINT,SIGTERM…) rather than raw numbers.1 -
default actions
Năm hành động mặc định: term, core, ign, stop, cont Năm nếp xử mặc định khi không dặn gì Five default actions: term, core, ign, stop, cont
Mỗi tín hiệu chuẩn có một hành động mặc định ghi trong bảng của sách (Table 20-1): term — kết thúc tiến trình; core — kết thúc và ghi một tệp core dump để gỡ lỗi; ign — tín hiệu bị bỏ qua; stop — tạm dừng tiến trình; cont — cho tiến trình đang dừng chạy tiếp.12 Phần lớn tín hiệu trên các bản UNIX đời đầu có mặc định là kết thúc tiến trình.1
Khi một tín hiệu tới mà bạn chưa dặn gì, máy làm theo một trong năm nếp: tắt chương trình; tắt kèm ghi "hộp đen" (core dump) để soi lỗi; lờ đi; tạm dừng; hoặc cho chạy tiếp. Mỗi loại tín hiệu có sẵn một nếp riêng. Nhớ nhé: không dặn gì thì máy theo nếp mặc định của loại tín hiệu đó.
Each standard signal has a default action listed in the book's table (Table 20-1): term — terminate the process; core — terminate and write a core dump file for debugging; ign — the signal is ignored; stop — suspend the process; cont — resume a stopped process.12 On early UNIX systems most signals defaulted to terminating the process.1
Bạn gặp nó ở đâuWhere you meet it Khi một chương trình "chết" và để lại tệpcore— đó là một tín hiệu có mặc định core (nhưSIGSEGV,SIGABRT) vừa kết thúc nó.2 Khi chương trình sập và để lại tệp core — đó là một tín hiệu loại "tắt kèm hộp đen". When a program dies and leaves acorefile — a signal whose default is core (likeSIGSEGV,SIGABRT) just terminated it.2 -
SIGKILL / SIGSTOP
Hai tín hiệu không thể bắt, chặn hay bỏ qua Hai tín hiệu không ai cưỡng lại được Two signals that cannot be caught, blocked, or ignored
SIGKILL(kết thúc tức thì) vàSIGSTOP(dừng tức thì) là hai ngoại lệ đặc biệt: disposition của chúng không thể bị thay đổi — không thể bắt bằng handler, không thể bỏ qua, và cũng không thể chặn.12 Đó là lý dokill -9(gửiSIGKILL) luôn hạ được một tiến trình bướng bỉnh — nó bỏ qua mọi handler. Vì vậy Kerrisk khuyên: hãy thửSIGTERMtrước (tiến trình có thể dọn dẹp rồi thoát gọn), chỉ dùngSIGKILLnhư biện pháp cuối cùng.1Hầu hết tín hiệu, chương trình có quyền "chặn cửa" hoặc tự xử theo ý mình. Nhưng hai tín hiệu đặc biệt — "giết" (
SIGKILL) và "dừng" (SIGSTOP) — thì không ai cưỡng lại được: không chặn được, không lờ được, không tự xử được. Nênkill -9luôn hạ được chương trình. Nhưng hãy lịch sự thửSIGTERMtrước để nó kịp dọn dẹp. Nhớ nhé: SIGKILL và SIGSTOP là tuyệt đối — dùng SIGKILL là lựa chọn cuối.SIGKILL(instant termination) andSIGSTOP(instant stop) are special exceptions: their disposition cannot be changed — they cannot be caught by a handler, ignored, or blocked.12 That is whykill -9(sendingSIGKILL) always brings down a stubborn process — it bypasses every handler. Hence Kerrisk's advice: trySIGTERMfirst (the process can clean up and exit gracefully), and reserveSIGKILLas a last resort.1Bạn gặp nó ở đâuWhere you meet it Khi một tiến trình "treo" không chịu thoát dù đã gửiSIGTERM—kill -KILLlà cứu cánh, vì nó không thể bị handler nào cản.1 Khi một chương trình treo cứng không chịu tắt —kill -9luôn dứt điểm được. When a hung process refuses to exit even afterSIGTERM—kill -KILLis the fallback, since no handler can block it.1Vì sao phải cứng rắn như vậyWhy they must be absolute
Nếu một tiến trình có thể bắt hay bỏ qua
SIGKILL, nó sẽ có thể tự biến mình thành bất tử — không cách nào hạ được. GiữSIGKILLvàSIGSTOPngoài tầm với của tiến trình đảm bảo hệ điều hành luôn còn một cách dứt khoát để kết thúc hay tạm dừng bất kỳ tiến trình nào.12If a process could catch or ignoreSIGKILL, it could make itself immortal — unkillable. KeepingSIGKILLandSIGSTOPout of a process's reach guarantees the operating system always retains a definitive way to terminate or suspend any process.12SIGTERM trước, SIGKILL sauSIGTERM first, SIGKILL after
SIGTERMlà tín hiệu "xin hãy kết thúc" tiêu chuẩn mà công cụ nhưkillgửi mặc định; tiến trình có thể bắt nó để lưu dữ liệu và đóng tài nguyên. Nhảy thẳng sangSIGKILLbỏ qua bước dọn dẹp ấy, nên dễ để lại trạng thái dở dang.1SIGTERMis the standard "please terminate" signal that tools likekillsend by default; a process can catch it to save data and close resources. Jumping straight toSIGKILLskips that cleanup, so it can leave state half-finished.1
Cụm khái niệm 02 Concept cluster 02
Đổi disposition: signal() là bản gốc, sigaction() là bản được ưu tiên Hai cách dặn máy phản ứng với tín hiệu — và cách nên dùng Changing disposition: signal() is the original, sigaction() the preferred one
UNIX cho hai cách đổi disposition của một tín hiệu: signal() và sigaction().1 signal() là API gốc, giao diện đơn giản hơn, nhưng hành vi của nó khác nhau giữa các hệ UNIX — một nguồn lỗi khả chuyển kinh điển. Vì vậy Kerrisk khuyến nghị (rất mạnh) dùng sigaction() để thiết lập trình xử lý tín hiệu trong chương trình khả chuyển.134 Thú vị là trên glibc, bản thân signal() được hiện thực như một hàm thư viện đặt chồng lên sigaction().1
Có hai cách dặn máy "khi tín hiệu này tới thì làm thế này". Cách cũ tên signal() ngắn gọn nhưng trớ trêu: mỗi hệ UNIX hiểu nó một kiểu, nên dễ gây lỗi khi chuyển máy. Cách mới tên sigaction() dài hơn chút nhưng rõ ràng và giống nhau ở mọi nơi. Nhớ nhé: để dặn tín hiệu cho chắc và chuyển máy an toàn, hãy dùng sigaction().
UNIX provides two ways to change a signal's disposition: signal() and sigaction().1 signal() is the original API with a simpler interface, but its behavior varies across UNIX systems — a classic portability pitfall. So Kerrisk (strongly) recommends sigaction() for establishing signal handlers in portable programs.134 Interestingly, on glibc signal() itself is implemented as a library function layered on top of sigaction().1
Định nghĩa. signal(sig, handler) đặt disposition cho tín hiệu sig; đối số handler là địa chỉ hàm xử lý, hoặc một trong hai hằng đặc biệt: SIG_DFL (khôi phục hành động mặc định) và SIG_IGN (bỏ qua tín hiệu). Hàm trả về disposition trước đó khi thành công, hoặc SIG_ERR khi lỗi.13
Nói đơn giản. signal(tín_hiệu, việc_cần_làm): bạn nói "khi tín hiệu này tới, chạy đoạn việc này", hoặc đưa SIG_DFL để trả về mặc định, SIG_IGN để lờ đi. Nhớ nhé: SIG_DFL là về mặc định, SIG_IGN là lờ đi.
Definition. signal(sig, handler) sets the disposition for signal sig; the handler argument is a handler function's address, or one of two special constants: SIG_DFL (restore the default action) and SIG_IGN (ignore the signal). It returns the previous disposition on success, or SIG_ERR on error.13
#include <signal.h>
void (*signal(int sig,
void (*handler)(int)))(int);
/* trả về disposition cũ, hoặc SIG_ERR */
signal(SIGINT, SIG_IGN); /* lờ Ctrl-C */
signal(SIGINT, SIG_IGN);
--> lờ Ctrl-C
signal(SIGINT, SIG_DFL);
--> về lại mặc định
#include <signal.h>
void (*signal(int sig,
void (*handler)(int)))(int);
/* returns old disposition, or SIG_ERR */
signal(SIGINT, SIG_IGN); /* ignore Ctrl-C */
-
sigaction()
Vì sao sigaction() được ưu tiên tuyệt đối Vì sao nên chọn sigaction() thay vì signal() Why sigaction() is strongly preferred
sigaction()cung cấp chức năng màsignal()không có, và — quan trọng hơn — cho một hành vi nhất quán, xác định rõ khi thiết lập handler, không phụ thuộc hệ.14 Nó nhận một cấu trúcstruct sigactionmô tả handler cùng các cờ (flags) và một tập tín hiệu sẽ được chặn trong lúc handler chạy. Vì những khác biệt khả chuyển củasignal(), sách dùngsigaction()xuyên suốt các chương về sau.1sigaction()làm mọi việcsignal()làm, nhưng rõ ràng và giống nhau ở mọi hệ máy, lại cho thêm nhiều lựa chọn tinh chỉnh. Vì thế đây là cách sách dùng từ đây về sau. Nhớ nhé: sigaction() là lựa chọn mặc định đúng đắn để bắt tín hiệu.sigaction()provides functionality thatsignal()lacks and — more importantly — gives consistent, well-defined behavior when establishing a handler, independent of the system.14 It takes astruct sigactiondescribing the handler, plus flags and a set of signals to block while the handler runs. Because ofsignal()'s portability differences, the book usessigaction()throughout the later chapters.1Bạn gặp nó ở đâuWhere you meet it Trong gần như mọi chương trình nghiêm túc có bắt tín hiệu — và trong toàn bộ Chương 21 về thiết kế trình xử lý tín hiệu.4 Trong hầu hết chương trình thực tế có xử lý tín hiệu — và suốt Chương 21 kế tiếp. In nearly every serious program that catches signals — and throughout Chapter 21 on designing signal handlers.4signal() đặt trên sigaction()signal() layered on sigaction()
Dù
signal()được ghi trong mục 2 của man page (system call), trên glibc nó thực chất là một hàm thư viện gọi tớisigaction(). Chính vì vậy hành vi khả chuyển củasignal()phụ thuộc vào cách thư viện C ánh xạ nó — một lý do nữa để gọi thẳngsigaction().13Althoughsignal()is documented in section 2 of the manual (system calls), on glibc it is actually a library function that callssigaction(). That is exactly whysignal()'s portable behavior depends on how the C library maps it — one more reason to callsigaction()directly.13Đọc disposition mà không đổiReading disposition without changing it
Không thể dùng
signal()để chỉ đọc disposition hiện thời mà không đồng thời thay đổi nó; muốn chỉ xem (hoặc xem rồi đặt) thì phải dùngsigaction(), vốn tách bạch hai việc này.14You cannot usesignal()merely to read the current disposition without also changing it; to just inspect it (or inspect-then-set) you must usesigaction(), which separates those operations.14 -
SIG_IGN
Bỏ qua khác với chặn — một phân biệt quan trọng "Lờ đi" không giống "cho chờ" Ignoring is not blocking — an important distinction
Đặt disposition thành
SIG_IGNkhiến tín hiệu bị vứt bỏ khi được phát — nó đến rồi biến mất, không để lại dấu vết. Điều này khác hẳn việc chặn (block) qua mặt nạ tín hiệu: tín hiệu bị chặn không bị vứt, mà được giữ lại ở trạng thái đang chờ cho tới khi được bỏ chặn.12"Lờ đi" (
SIG_IGN) nghĩa là tín hiệu tới thì bị ném đi luôn, coi như chưa từng có. "Cho chờ" (chặn) thì khác: tín hiệu đứng đợi ngoài cửa, chưa bị bỏ, và sẽ vào khi bạn mở chặn. Nhớ nhé: lờ đi là ném bỏ, chặn là bắt đứng chờ.Setting the disposition to
SIG_IGNcauses the signal to be discarded on delivery — it arrives and vanishes, leaving no trace. This differs entirely from blocking via the signal mask: a blocked signal is not discarded but held in the pending state until it is unblocked.12Bạn gặp nó ở đâuWhere you meet it Khi một chương trình muốn phớt lờSIGINTtrong một đoạn tới hạn — đặtSIG_IGNthì Ctrl-C hoàn toàn vô hại trong đoạn đó.3 Khi muốn Ctrl-C không có tác dụng trong một lúc — đặt tín hiệu đó thành "lờ đi". When a program wants to ignoreSIGINTduring a critical section — settingSIG_IGNmakes Ctrl-C harmless there.3 -
disposition is inherited
Hai tín hiệu vẫn trơ: SIGKILL và SIGSTOP Dù muốn, bạn vẫn không đổi được hai tín hiệu này Two still immovable: SIGKILL and SIGSTOP
Cả
signal()lẫnsigaction()đều không thể đổi disposition củaSIGKILLhaySIGSTOP— nỗ lực làm vậy sẽ thất bại.14 Đây là hệ quả trực tiếp của tính "không thể bắt, chặn hay bỏ qua" đã nêu ở cụm trước: không có cửa sau nào, dù qua API cũ hay mới.1Dù dùng cách cũ hay cách mới, bạn vẫn không thể dặn lại hai tín hiệu "giết" và "dừng". Chúng luôn làm đúng việc của mình. Nhớ nhé: không API nào đổi được SIGKILL và SIGSTOP.
Neither
signal()norsigaction()can change the disposition ofSIGKILLorSIGSTOP— any such attempt fails.14 This follows directly from their "cannot be caught, blocked, or ignored" property from the previous cluster: there is no back door, old API or new.1
Cụm khái niệm 03 Concept cluster 03
Gửi tín hiệu: kill(), raise(), killpg() — và câu chuyện quyền hạn Cách tự gửi tín hiệu, và ai được phép gửi cho ai Sending signals: kill(), raise(), killpg() — and the question of permission
Một tiến trình (nếu đủ quyền) có thể gửi tín hiệu tới tiến trình khác bằng kill(pid, sig) — tên "kill" là di sản lịch sử, vì mặc định của phần lớn tín hiệu đời đầu là kết thúc tiến trình.15 Để tự gửi cho chính mình có raise(sig); để gửi cho cả một nhóm tiến trình (process group) có killpg().167 Việc gửi bị ràng buộc bởi quyền: nói đại thể, một tiến trình thường chỉ được gửi tín hiệu tới tiến trình khác nếu người gửi và người nhận thuộc cùng chủ sở hữu (hoặc người gửi là đặc quyền).15
Để gửi một tín hiệu cho chương trình khác, ta dùng kill() — cái tên nghe đáng sợ nhưng chỉ là "gửi tín hiệu", không nhất thiết là giết. Muốn gửi cho chính mình thì dùng raise(); gửi cho cả một nhóm thì dùng killpg(). Nhưng không phải ai cũng gửi được cho ai: thường chỉ gửi được cho tiến trình cùng chủ, trừ khi bạn là quản trị. Nhớ nhé: kill() là "gửi tín hiệu", và phải đúng quyền mới gửi được.
A process (with suitable permission) can send a signal to another with kill(pid, sig) — the name "kill" is a historical legacy, since most early signals defaulted to terminating the process.15 To send to itself there is raise(sig); to send to a whole process group there is killpg().167 Sending is bound by permission: broadly, an ordinary process may signal another only if sender and recipient share an owner (or the sender is privileged).15
Định nghĩa. kill(pid, sig) gửi tín hiệu sig tới tiến trình có số pid. Giá trị pid còn có các nghĩa đặc biệt: pid > 0 gửi tới một tiến trình; pid == 0 gửi tới mọi thành viên cùng nhóm tiến trình với người gửi; pid == -1 gửi tới mọi tiến trình người gửi có quyền gửi; pid < -1 gửi tới nhóm tiến trình có mã bằng trị tuyệt đối của pid.15
Nói đơn giản. kill(ai, tín_hiệu): gửi tín hiệu cho một tiến trình (theo số của nó). Có mẹo: số 0 nghĩa là "cả nhóm của tôi", số −1 nghĩa là "mọi tiến trình tôi được phép". Nhớ nhé: kill nhận một số pid, và vài giá trị đặc biệt mở rộng phạm vi gửi.
Definition. kill(pid, sig) sends signal sig to the process numbered pid. The pid value carries special meanings: pid > 0 targets one process; pid == 0 targets every member of the sender's own process group; pid == -1 targets every process the sender may signal; pid < -1 targets the process group whose ID equals the absolute value of pid.15
#include <signal.h>
int kill(pid_t pid, int sig);
int raise(int sig); /* gửi cho chính mình */
int killpg(pid_t pgrp, int sig);
kill(pid, 0); /* tín hiệu null: kiểm tra tồn tại */
kill(pid, sig) --> gửi cho pid
raise(sig) --> gửi cho mình
kill(pid, 0) --> pid còn sống không?
#include <signal.h>
int kill(pid_t pid, int sig);
int raise(int sig); /* send to self */
int killpg(pid_t pgrp, int sig);
kill(pid, 0); /* null signal: existence check */
-
null signal (0)
Gửi tín hiệu 0 không gửi gì — nó kiểm tra sự tồn tại Tín hiệu số 0: không gửi gì, chỉ để dò xem còn sống không Signal 0 sends nothing — it checks existence
Nếu
siglà 0 (gọi là tín hiệu null),kill()không thực sự gửi tín hiệu nào; thay vào đó nó chỉ làm các bước kiểm tra lỗi thường lệ.15 Nhờ đó ta dùng tín hiệu null để kiểm tra một tiến trình có tồn tại không: nếu gọi thất bại với lỗiESRCH, tiến trình đó không tồn tại; nếu thất bại vớiEPERMthì nó tồn tại nhưng ta không có quyền gửi.15Gửi "tín hiệu số 0" là một mẹo: thực ra chẳng gửi gì cả, chỉ để hỏi "tiến trình kia còn sống không?". Nếu máy báo lỗi "không tìm thấy" (
ESRCH), nghĩa là nó đã biến mất. Nhớ nhé: tín hiệu 0 là cách gõ cửa hỏi "có ai ở nhà không" mà không làm phiền.If
sigis 0 (the null signal),kill()sends no signal; instead it performs only the usual error checks.15 This lets you use the null signal to test whether a process exists: if the call fails withESRCH, the process does not exist; if it fails withEPERM, it exists but you lack permission to signal it.15Bạn gặp nó ở đâuWhere you meet it Trong các script giám sát viết bằng C: định kỳ gọikill(pid, 0)để biết một dịch vụ còn chạy hay đã chết.5 Khi muốn biết một tiến trình còn chạy không —kill(pid, 0)trả lời mà không làm phiền nó. In monitoring code: periodically callkill(pid, 0)to learn whether a service is still running or has died.5Một cuộc đua ẩnA hidden race
Kiểm tra tồn tại bằng tín hiệu null có một bẫy: PID được tái sử dụng (Chương 6), nên tiến trình trả về "còn sống" có thể là một tiến trình khác vừa nhận lại PID cũ. Vì vậy cách này chỉ nên dùng khi biết PID chưa thể bị tái dùng trong khoảng thời gian liên quan.15Existence checks via the null signal have a trap: PIDs are reused (Chapter 6), so a process reported "alive" might be a different process that just inherited the old PID. Use this only when you know the PID cannot be recycled within the relevant window.15
Các mã lỗi cần phân biệtError codes to distinguish
ESRCH("no such process") nghĩa là không có tiến trình với PID đó;EPERMnghĩa là có nhưng thiếu quyền. Phân biệt hai mã này cho phép suy ra tiến trình tồn tại ngay cả khi không gửi được tín hiệu thật.15ESRCH("no such process") means no process with that PID exists;EPERMmeans one exists but you lack permission. Distinguishing the two lets you infer existence even when you cannot actually deliver a signal.15 -
permission
Không phải ai cũng gửi được cho ai Phải đúng quyền mới gửi tín hiệu được Not everyone may signal everyone
Để gửi tín hiệu, tiến trình gửi phải có quyền phù hợp. Quy tắc cốt lõi: một tiến trình đặc quyền (CAP_KILL) gửi được cho bất kỳ ai; một tiến trình thường chỉ gửi được nếu định danh người dùng thực hoặc hiệu lực của nó khớp với định danh thực hoặc đã-lưu của tiến trình đích.15 Riêng
SIGCONTcó ngoại lệ: có thể gửi tới bất kỳ tiến trình nào trong cùng một phiên (session).1Hệ điều hành không cho bạn gửi tín hiệu lung tung tới chương trình của người khác — đó là chuyện bảo mật. Nói nôm na: bạn gửi được cho các chương trình của chính mình; chỉ "người quản trị" mới gửi được cho tất cả. Nhớ nhé: thường thì chỉ gửi tín hiệu được cho tiến trình cùng chủ với mình.
To send a signal, the sender must have appropriate permission. The core rule: a privileged process (CAP_KILL) may signal anyone; an ordinary process may signal only if its real or effective user ID matches the real or saved user ID of the target.15
SIGCONTis a special case: it may be sent to any process in the same session.1Bạn gặp nó ở đâuWhere you meet it Khi lệnhkillbáo "Operation not permitted" (EPERM) lúc bạn cố dừng tiến trình của người dùng khác mà không có quyền root.5 Khi bạn cố dừng chương trình của người khác và máy báo "không được phép" — đó là quy tắc quyền hạn. Whenkillreports "Operation not permitted" (EPERM) as you try to stop another user's process without root.5 -
strsignal / psignal
Mô tả tín hiệu bằng lời người đọc được Đổi số tín hiệu thành câu chữ dễ hiểu Describing a signal in human-readable words
strsignal(sig)trả về một chuỗi mô tả tín hiệu (ví dụ "Interrupt"); nó có kiểm tra biên và nhạy locale, nên mô tả hiển thị theo ngôn ngữ hệ thống.18psignal(sig, msg)in ra lỗi chuẩn (stderr) chuỗimsg, dấu hai chấm, rồi phần mô tả tín hiệu. Cả hai được ưa dùng hơn việc truy cập trực tiếp mảngsys_siglist.18Con số tín hiệu khó nhớ.
strsignal()đổi một số tín hiệu thành câu chữ dễ hiểu như "Interrupt".psignal()thì in thẳng dòng mô tả ra màn hình lỗi. Nhớ nhé: strsignal cho bạn tên mô tả, psignal in nó ra giúp bạn.strsignal(sig)returns a string describing the signal (e.g. "Interrupt"); it does bounds checking and is locale-sensitive, so the description appears in the system's language.18psignal(sig, msg)prints to standard error the stringmsg, a colon, then the signal description. Both are preferable to directly accessing thesys_siglistarray.18Bạn gặp nó ở đâuWhere you meet it Trong thông báo lỗi thân thiện: thay vì in "caught signal 2", chương trình in "caught signal: Interrupt" nhờstrsignal().8 Khi một chương trình báo "bị tín hiệu: Interrupt" thay vì "tín hiệu 2" — đó là nhờ strsignal. In friendly error messages: instead of "caught signal 2", a program prints "caught signal: Interrupt" viastrsignal().8Chuẩn hóa muộnStandardized late
psignal(),strsignal()vàsys_siglistkhông nằm trong SUSv3 nhưng có mặt trên nhiều hệ UNIX; SUSv4 về sau mới bổ sung đặc tả chopsignal()vàstrsignal().18psignal(),strsignal(), andsys_siglistare not in SUSv3 but are present on many UNIX systems; SUSv4 later added specifications forpsignal()andstrsignal().18Vì sao ưu tiên hàmWhy prefer the function
Ngoài tính nhạy locale,
strsignal()còn kiểm tra biên củasigvà trả về NULL nếu không hợp lệ — an toàn hơn so với lập chỉ mục thô vào mảngsys_siglist.18Beyond locale sensitivity,strsignal()also bounds-checkssigand returns NULL if it is invalid — safer than raw indexing into thesys_siglistarray.18
Cụm khái niệm 04 Concept cluster 04
Tập tín hiệu, mặt nạ, tín hiệu đang chờ — và một sự thật: tín hiệu chuẩn không xếp hàng Gom tín hiệu, chặn tạm, xem ai đang chờ, và đứng đợi Signal sets, the mask, pending signals — and a truth: standard signals don't queue
Nhiều system call cần biểu diễn một nhóm tín hiệu. Kiểu dữ liệu sigset_t làm việc đó, thao tác qua bộ hàm: sigemptyset() (tập rỗng), sigfillset() (chứa mọi tín hiệu, kể cả realtime), sigaddset()/sigdelset() (thêm/bớt một tín hiệu), và sigismember() (kiểm tra một tín hiệu có trong tập).19 Nhân giữ cho mỗi tiến trình một mặt nạ tín hiệu (signal mask) — tập các tín hiệu đang bị chặn khỏi việc phát; sigprocmask() xem và sửa mặt nạ này, còn sigpending() trả về tập tín hiệu đang chờ.11011
Thay vì xử lý từng tín hiệu một, hệ thống cho ta một "danh sách" tín hiệu (sigset_t): tạo danh sách rỗng, điền đầy, thêm hoặc bớt từng cái, hay hỏi "cái này có trong danh sách không". Dùng danh sách đó, ta bảo máy "hãy cho mấy tín hiệu này đứng chờ" (mặt nạ), rồi có thể hỏi "ai đang chờ ngoài cửa". Nhớ nhé: tập tín hiệu là một danh sách; mặt nạ là danh sách những tín hiệu đang bị cho chờ.
Many system calls must represent a group of signals. The sigset_t type does this, manipulated by a family of functions: sigemptyset() (empty set), sigfillset() (contains every signal, including realtime), sigaddset()/sigdelset() (add/remove one), and sigismember() (test membership).19 The kernel keeps, for each process, a signal mask — the set of signals currently blocked from delivery; sigprocmask() inspects and modifies it, while sigpending() returns the set of pending signals.11011
Định nghĩa. sigprocmask(how, set, oldset) thay đổi mặt nạ theo ba cách tùy how: SIG_BLOCK (thêm set vào mặt nạ), SIG_UNBLOCK (bỏ set khỏi mặt nạ), và SIG_SETMASK (đặt mặt nạ đúng bằng set). Truyền set là NULL chỉ để lấy mặt nạ hiện thời vào oldset mà không đổi gì.110
Nói đơn giản. sigprocmask sửa danh sách "đang cho chờ": thêm vào (SIG_BLOCK), bỏ ra (SIG_UNBLOCK), hay đặt lại cả danh sách (SIG_SETMASK). Nhớ nhé: BLOCK là thêm vào chặn, UNBLOCK là mở ra, SETMASK là thay cả danh sách.
Definition. sigprocmask(how, set, oldset) changes the mask three ways by how: SIG_BLOCK (add set to the mask), SIG_UNBLOCK (remove set), and SIG_SETMASK (set the mask exactly to set). Passing set as NULL just fetches the current mask into oldset without changing anything.110
sigset_t s;
sigemptyset(&s);
sigaddset(&s, SIGINT);
sigprocmask(SIG_BLOCK, &s, NULL);
/* ... đoạn tới hạn ... */
sigprocmask(SIG_UNBLOCK, &s, NULL);
sigemptyset(&s); // danh sách rỗng
sigaddset(&s, SIGINT);
sigprocmask(SIG_BLOCK, &s, NULL);
// ...làm việc quan trọng...
sigprocmask(SIG_UNBLOCK, &s, NULL);
sigset_t s;
sigemptyset(&s);
sigaddset(&s, SIGINT);
sigprocmask(SIG_BLOCK, &s, NULL);
/* ... critical section ... */
sigprocmask(SIG_UNBLOCK, &s, NULL);
-
blocking ≠ discarding
Chặn chỉ hoãn, không vứt — tín hiệu nằm chờ Chặn là bắt chờ, không phải ném đi Blocking defers, it doesn't discard — the signal stays pending
Khi một tín hiệu bị chặn trong mặt nạ, nó không bị mất: nó trở thành pending và ở đó cho tới khi được bỏ chặn, lúc ấy mới được phát.110 Nỗ lực chặn
SIGKILLhaySIGSTOPbị âm thầm bỏ qua — một hệ quả nữa của tính bất khả cưỡng của chúng.1 Nếu ta đổi disposition của một tín hiệu đang chờ, thì khi nó được bỏ chặn, nó sẽ được xử theo disposition mới.1Chặn một tín hiệu không làm nó biến mất — nó chỉ đứng chờ ngoài cửa, và sẽ vào ngay khi bạn mở chặn. Riêng SIGKILL và SIGSTOP thì không chặn được (máy lặng lẽ lờ yêu cầu chặn). Nhớ nhé: chặn là cho chờ, mở chặn là tín hiệu ùa vào.
When a signal is blocked by the mask it is not lost: it becomes pending and stays so until unblocked, at which point it is delivered.110 Attempts to block
SIGKILLorSIGSTOPare silently ignored — another consequence of their irresistibility.1 If you change the disposition of a pending signal, then when it is later unblocked it is handled by its new disposition.1Bạn gặp nó ở đâuWhere you meet it Khi bảo vệ một đoạn tới hạn khỏi bị cắt ngang: chặn tín hiệu, làm việc, rồi bỏ chặn — tín hiệu dồn lại sẽ được phát sau.10 Khi làm một việc không được phép bị ngắt giữa chừng — chặn tín hiệu trong lúc đó rồi mở lại. When protecting a critical section from interruption: block the signal, do the work, then unblock — the deferred signal is delivered afterward.10 -
not queued
Tín hiệu chuẩn không xếp hàng — chờ nhiều lần chỉ còn một Nhiều tín hiệu cùng loại dồn lại chỉ tính một Standard signals don't queue — many pending become one
Đây là một sự thật dễ gây sốc: mỗi tín hiệu chuẩn chỉ được đánh dấu "đang chờ" một lần duy nhất. Nếu cùng một tín hiệu được sinh ra nhiều lần trong khi nó đang bị chặn, thì khi bỏ chặn nó chỉ được phát một lần — các lần sinh dư "biến mất".1 Tập pending thực chất là một tập hợp (có/không), không phải một hàng đợi đếm số lần. Đây chính là điểm khác cốt lõi với tín hiệu thời gian thực, vốn được xếp hàng.12
Tưởng tượng chuông cửa chỉ nhớ "có người bấm" chứ không đếm bấm bao nhiêu lần. Tín hiệu chuẩn cũng vậy: nếu nó tới năm lần trong lúc bị chặn, khi mở ra bạn chỉ nhận một lần. Bốn lần kia mất. Nhớ nhé: tín hiệu chuẩn chỉ nhớ "có hay không", không đếm số lần.
A surprising truth: each standard signal is marked pending only once. If the same signal is generated many times while blocked, then on unblocking it is delivered just once — the extra generations simply "disappear."1 The pending set is really a set (present/absent), not a counting queue. This is the core difference from realtime signals, which are queued.12
Bạn gặp nó ở đâuWhere you meet it Khi một handlerSIGCHLDbỏ sót vài tiến trình con — vì nhiềuSIGCHLDdồn lại chỉ phát một lần, nên handler phải lặp để thu hết (Chương 26).1 Khi chương trình "bỏ quên" vài sự kiện — vì nhiều tín hiệu cùng loại dồn lại chỉ tính một lần. When aSIGCHLDhandler misses some children — because severalSIGCHLDs coalesce into one delivery, so the handler must loop to reap them all (Chapter 26).1Vì sao chỉ "có/không"Why only present/absent
Trạng thái "đang chờ" của tập tín hiệu chuẩn được biểu diễn bằng một mặt nạ bit — mỗi tín hiệu một bit. Một bit chỉ ghi được "có" hoặc "không", nên tự nó không thể đếm số lần tín hiệu được sinh. Đó là lý do kỹ thuật khiến tín hiệu chuẩn không xếp hàng.1The pending state of standard signals is represented by a bit mask — one bit per signal. A single bit records only "present" or "absent," so it inherently cannot count generations. That is the technical reason standard signals do not queue.1
Khác biệt với realtimeDifference from realtime
Tín hiệu thời gian thực giữ một hàng đợi riêng nên mỗi lần sinh đều được phát đúng một lần và còn kèm dữ liệu đi theo. Khi cần đếm chính xác số sự kiện, đó là lựa chọn phù hợp hơn tín hiệu chuẩn.12Realtime signals maintain a queue, so every generation is delivered exactly once and can carry accompanying data. When you need an exact count of events, they are a better fit than standard signals.12
-
pause()
pause(): đứng yên cho tới khi có tín hiệu pause(): ngủ cho tới khi có tín hiệu đánh thức pause(): sleep until a signal arrives
pause()treo tiến trình gọi cho tới khi một tín hiệu được bắt (hoặc một tín hiệu kết thúc tiến trình).112 Đây là cách đơn giản nhất để một tiến trình "ngủ" mà không tốn CPU, rồi tỉnh dậy đúng khi có việc — thường dùng trong các ví dụ chờ tín hiệu của chương này.1 Khi tín hiệu được xử lý,pause()trở về với lỗiEINTR.112pause()là cách nói "tôi ngủ đây, có tín hiệu thì gọi tôi dậy". Trong lúc ngủ, chương trình không tốn sức máy. Khi một tín hiệu tới và được xử lý xong, nó tỉnh dậy. Nhớ nhé: pause() là nút "ngủ cho tới khi có tín hiệu".pause()suspends the calling process until a signal is caught (or one terminates it).112 It is the simplest way for a process to "sleep" without burning CPU and wake exactly when there is work — used in this chapter's signal-waiting examples.1 After the signal is handled,pause()returns with the errorEINTR.112Bạn gặp nó ở đâuWhere you meet it Trong các chương trình nhỏ chỉ chờ một tín hiệu để hành động — một vòng chờ tối giản, không bận rộn quay CPU.12 Khi một chương trình đứng im chờ Ctrl-C hay một tín hiệu khác rồi mới làm gì đó. In small programs that just wait for one signal to act — a minimal wait loop that does not spin the CPU.12Một cuộc đua rình rậpA lurking race
pause()có một cuộc đua kinh điển: nếu tín hiệu đến giữa lúc kiểm tra điều kiện và lúc gọipause(), chương trình có thể ngủ mãi. Cách giải đúng làsigsuspend(), kết hợp mở mặt nạ và chờ một cách nguyên tử — sách phát triển ý này ở các chương sau.1pause()has a classic race: if the signal arrives between testing a condition and callingpause(), the program may sleep forever. The correct fix issigsuspend(), which atomically unblocks and waits — the book develops this in later chapters.1Trả về EINTRIt returns EINTR
pause()luôn trả về −1 vớierrnođặt làEINTRsau khi một handler chạy xong — một ví dụ điển hình của việc một system call bị tín hiệu cắt ngang, chủ đề được bàn kỹ ở Chương 21.112pause()always returns −1 witherrnoset toEINTRafter a handler runs — a textbook case of a system call interrupted by a signal, a topic treated fully in Chapter 21.112
Ba ngộ nhận thường gặp Three common misconceptions
“kill() nghĩa là giết một tiến trình” “kill() means killing a process”
Ngộ nhận: kill() luôn kết thúc tiến trình đích.
Thực tế: kill() chỉ gửi một tín hiệu; cái tên là di sản lịch sử. Có thể gửi SIGCONT (cho chạy tiếp), hay tín hiệu null 0 (không gửi gì, chỉ kiểm tra tồn tại).15
Myth: kill() always terminates the target.
Reality: kill() merely sends a signal; the name is historical. You can send SIGCONT (resume), or the null signal 0 (sends nothing, just checks existence).15
“Chặn và bỏ qua là như nhau” “Blocking and ignoring are the same”
Ngộ nhận: chặn một tín hiệu cũng vứt bỏ nó như SIG_IGN.
Thực tế: SIG_IGN vứt bỏ tín hiệu khi phát; chặn chỉ hoãn — tín hiệu thành pending và sẽ được phát khi bỏ chặn.110
Myth: blocking a signal discards it, just like SIG_IGN.
Reality: SIG_IGN discards the signal on delivery; blocking only defers it — the signal becomes pending and is delivered once unblocked.110
“Mỗi lần tín hiệu tới đều được nhận riêng” “Every signal arrival is received separately”
Ngộ nhận: gửi cùng một tín hiệu năm lần thì handler chạy đủ năm lần.
Thực tế: tín hiệu chuẩn không xếp hàng — nhiều lần sinh khi đang chờ chỉ gộp thành một lần phát. Cần đếm chính xác thì dùng tín hiệu thời gian thực.12
Myth: sending the same signal five times runs the handler five times.
Reality: standard signals do not queue — multiple generations while pending coalesce into one delivery. For an exact count, use realtime signals.12
Tự kiểm Self-check
Năm câu hỏi để biết bạn đã nắm nền tảng về tín hiệu Năm câu hỏi kiểm tra nhanh Five questions to check you grasped the signal fundamentals
Trả lời được những câu này nghĩa là bạn đã nắm vòng đời, cách đổi disposition, cách gửi và cách điều phối tín hiệu — nền cho Chương 21 về thiết kế trình xử lý tín hiệu.
Nếu trả lời được, nghĩa là bạn đã hiểu chương này.
Answering these means you grasp the lifecycle, changing disposition, sending, and controlling signals — the groundwork for Chapter 21 on designing signal handlers.
-
Mô tả vòng đời của một tín hiệu: sinh ra, đang chờ, được phát. Năm hành động mặc định là gì?
Gợi ý sâu hơn: vì sao mã nên tham chiếu tín hiệu bằng tên biểu tượng chứ không bằng số?
-
Vì sao
sigaction()được ưu tiên hơnsignal()? Ba hằngSIG_DFL,SIG_IGN,SIG_ERRcó nghĩa gì?Gợi ý sâu hơn: vì sao không thể dùng
signal()để chỉ đọc disposition mà không đổi nó? -
SIGKILLvàSIGSTOPkhác mọi tín hiệu khác ở điểm nào, và vì sao phải như vậy?Gợi ý sâu hơn: điều gì sẽ xảy ra nếu một tiến trình có thể bắt hoặc chặn
SIGKILL? -
Tín hiệu null (0) dùng để làm gì, và
ESRCHvớiEPERMcho ta biết điều gì khác nhau?Gợi ý sâu hơn: vì sao kiểm tra tồn tại bằng tín hiệu null có thể sai do PID được tái sử dụng?
-
Phân biệt "chặn" với "bỏ qua" một tín hiệu. Vì sao gửi cùng một tín hiệu chuẩn năm lần có thể chỉ được nhận một lần?
Gợi ý sâu hơn: ba giá trị
SIG_BLOCK,SIG_UNBLOCK,SIG_SETMASKcủasigprocmask()làm gì?
Một tín hiệu đi qua những bước nào từ lúc sinh ra tới lúc được xử lý?
Có mấy cách dặn máy phản ứng với một tín hiệu, và nên dùng cách nào?
Vì sao
kill -9luôn tắt được một chương trình treo?Làm sao kiểm tra một tiến trình còn sống mà không làm phiền nó?
"Chặn" một tín hiệu khác gì với "lờ đi" nó?
Describe a signal's lifecycle: generated, pending, delivered. What are the five default actions?
Why is
sigaction()preferred oversignal()? What doSIG_DFL,SIG_IGN, andSIG_ERRmean?How do
SIGKILLandSIGSTOPdiffer from all other signals, and why must they?What is the null signal (0) used for, and what do
ESRCHversusEPERMtell you?Distinguish "blocking" from "ignoring" a signal. Why can sending the same standard signal five times be received only once?
Khép lại chương Closing the chapter
Một tín hiệu là tiếng gõ cửa bất đồng bộ do nhân điều phối: nó đi qua vòng đời sinh–chờ–phát, mỗi loại có hành động mặc định riêng, ta đổi cách đáp lại nó qua
sigaction(), gửi nó đi đúng quyền vớikill(), và điều phối thời điểm bằng mặt nạ tín hiệu — luôn nhớ rằng tín hiệu chuẩn không xếp hàng. Nắm được nền này, bạn đã sẵn sàng cho câu hỏi kế tiếp — viết một trình xử lý tín hiệu cho an toàn và đúng đắn.1Tín hiệu là tiếng gõ cửa bất ngờ: có vòng đời rõ ràng, có nếp xử mặc định, có cách đổi cách đáp, cách gửi, và cách bảo nó chờ. Hiểu bộ khung này là đủ để đi tiếp sang chuyện viết trình xử lý tín hiệu. Nhớ nhé: tín hiệu là tiếng gõ cửa — bạn chọn cách mở, cách gửi, và cách bảo nó đợi.
A signal is an asynchronous knock at the door orchestrated by the kernel: it runs a generate–pending–deliver lifecycle, each type has its own default action, you change your response via
sigaction(), send it with the right permission viakill(), and control its timing with the signal mask — always remembering that standard signals do not queue. Grasp this groundwork and you are ready for the next question — how to write a signal handler safely and correctly.1