Chương 37 · Daemon Chapter 37 · Daemons
Daemon: tiến trình sống lâu, chạy nền, không có terminal điều khiển Daemon: chương trình chạy ngầm, âm thầm, sống rất lâu Daemons: long-lived background processes with no controlling terminal
Kerrisk định nghĩa daemon là một tiến trình có hai đặc điểm: nó sống lâu (thường được tạo lúc hệ thống khởi động và chạy tới khi tắt máy), và nó chạy nền, không có terminal điều khiển — nhờ đó nhân không bao giờ tự sinh ra các tín hiệu điều khiển công việc hay liên quan terminal (như SIGINT, SIGTSTP, SIGHUP) cho nó.12 Chương đi qua: các bước biến một tiến trình thành daemon, mẫu hàm trợ giúp becomeDaemon(), cách dùng SIGHUP để nạp lại cấu hình, và cách ghi log qua tiện ích syslog.1
Hãy hình dung một người gác đêm trong tòa nhà: không ngồi ở quầy tiếp khách, không ai thấy mặt, nhưng lúc nào cũng có mặt làm việc suốt đêm. Daemon là chương trình như vậy — nó chạy ngầm phía sau, sống rất lâu, và không gắn với cửa sổ dòng lệnh nào cả. Nhớ nhé: daemon là chương trình chạy ngầm, âm thầm, sống dai.
Kerrisk defines a daemon as a process with two characteristics: it is long-lived (often created at system startup and running until shutdown), and it runs in the background with no controlling terminal — so that the kernel never automatically generates job-control or terminal-related signals (such as SIGINT, SIGTSTP, SIGHUP) for it.12 The chapter covers: the steps to turn a process into a daemon, the becomeDaemon() helper pattern, using SIGHUP to reinitialize, and logging through the syslog facility.1
Vì sao phải cắt bỏ terminal điều khiển? Một tiến trình gắn với terminal sẽ nhận được các tín hiệu khi người dùng gõ Ctrl-C (SIGINT), Ctrl-Z (SIGTSTP), hay khi terminal bị ngắt kết nối (SIGHUP). Một chương trình nền phải sống độc lập với mọi phiên đăng nhập, nên nó cố ý tự tách khỏi terminal; khi đó SIGHUP được giải phóng để daemon tái sử dụng cho mục đích riêng (xem phần 03).12Why shed the controlling terminal? A terminal-attached process receives signals when the user types Ctrl-C (SIGINT), Ctrl-Z (SIGTSTP), or when the terminal disconnects (SIGHUP). A background program must outlive any login session, so it deliberately detaches from the terminal; that frees up SIGHUP for the daemon to repurpose (see section 03).12
Luận đề của chương: biến một chương trình thành daemon là một chuỗi bước định hình môi trường — tách khỏi terminal bằng setsid(), dọn umask, đổi thư mục làm việc, đóng và mở lại các bộ mô tả tệp — để tiến trình chạy ngầm, bền bỉ và không lệ thuộc vào phiên đăng nhập; và một khi đã chạy nền, nó liên lạc với thế giới qua syslog thay vì màn hình.1
Ý chính: muốn một chương trình chạy ngầm đúng cách, ta làm theo một danh sách bước chuẩn để cắt nó khỏi terminal và dọn dẹp môi trường. Vì không còn màn hình để in ra, nó gửi thông báo vào một hệ thống ghi log chung tên là syslog. Nhớ nhé: thành daemon là làm đúng một danh sách bước, rồi ghi log qua syslog.
The chapter's thesis: turning a program into a daemon is a sequence of environment-shaping steps — detaching from the terminal with setsid(), clearing the umask, changing the working directory, closing and reopening file descriptors — so the process runs in the background, durably and independent of any login; and once backgrounded, it talks to the world through syslog rather than a screen.1
Cụm khái niệm 01 Concept cluster 01
Daemon sống lâu và chạy nền — và điều quan trọng nhất là nó không có terminal điều khiển Daemon sống rất lâu, chạy ngầm, và không gắn với cửa sổ dòng lệnh nào A daemon is long-lived and backgrounded — and above all has no controlling terminal
Một daemon được viết để làm một nhiệm vụ cụ thể và chạy liên tục. Kerrisk nêu các ví dụ quen thuộc: cron thực thi lệnh theo lịch; sshd (secure shell daemon) cho phép đăng nhập từ xa qua giao thức bảo mật; httpd (máy chủ HTTP Apache) phục vụ trang web; và inetd (Internet superserver) lắng nghe kết nối mạng trên các cổng TCP/IP rồi khởi chạy chương trình máy chủ phù hợp.1 Nhiều daemon chuẩn chạy với quyền đặc biệt (effective user ID bằng 0), nên cần được viết theo các hướng dẫn an toàn ở Chương 38.1
Daemon là những "nhân viên hậu trường" của hệ điều hành, mỗi đứa lo một việc. cron là anh hẹn giờ, cứ tới giờ là chạy việc đã đặt. sshd là anh gác cổng cho người ở xa đăng nhập vào. httpd là anh bồi bàn mang trang web ra cho trình duyệt. Chúng làm việc âm thầm, bạn gần như không bao giờ thấy mặt. Nhớ nhé: mỗi daemon là một nhân viên hậu trường lo đúng một việc.
A daemon is written to carry out a specific task and runs continuously. Kerrisk gives the familiar examples: cron executes commands at scheduled times; sshd (the secure shell daemon) permits remote logins over a secure protocol; httpd (the Apache HTTP server) serves web pages; and inetd (the Internet superserver) listens for network connections on TCP/IP ports and launches the appropriate server program.1 Many standard daemons run as privileged processes (effective user ID 0), so they should be coded following the security guidelines of Chapter 38.1
Định nghĩa. Terminal điều khiển (controlling terminal) là terminal gắn với một phiên (session) mà qua đó nhân gửi tín hiệu bàn phím cho tiến trình. Daemon cố tình không có terminal điều khiển; nhờ đó nhân không bao giờ tự sinh các tín hiệu như SIGINT (Ctrl-C) hay SIGHUP (ngắt kết nối) cho nó, và nó sống độc lập với mọi phiên đăng nhập.12
Nói đơn giản. "Terminal điều khiển" là cửa sổ dòng lệnh mà từ đó bạn gõ Ctrl-C để dừng một chương trình. Daemon tự ngắt khỏi cửa sổ đó, nên không ai gõ Ctrl-C mà tắt được nó, và nó sống tiếp kể cả khi bạn đăng xuất. Nhớ nhé: daemon tự cắt dây với cửa sổ dòng lệnh để không bị tắt ngang.
Definition. A controlling terminal is the terminal attached to a session through which the kernel delivers keyboard-generated signals to processes. A daemon deliberately has no controlling terminal; so the kernel never automatically generates signals such as SIGINT (Ctrl-C) or SIGHUP (disconnection) for it, and it lives independently of any login session.12
Đặc điểm của daemon:
• long-lived → tạo lúc boot, chạy tới shutdown
• background → không có controlling terminal
• ví dụ → cron, sshd, httpd, inetd
• quy ước → tên thường kết thúc bằng 'd'
daemon = sống lâu + chạy ngầm
ví dụ: cron, sshd, httpd
tên thường tận cùng bằng chữ 'd'
Daemon traits:
• long-lived → created at boot, runs to shutdown
• background → no controlling terminal
• examples → cron, sshd, httpd, inetd
• convention → names often end in 'd'
-
long-lived
Sống lâu: sinh ra lúc boot, mất đi lúc tắt máy Sống suốt: bật từ lúc khởi động tới lúc tắt máy Long-lived: born at boot, gone at shutdown
Đặc điểm đầu tiên là sống lâu: một daemon thường được tạo lúc hệ thống khởi động và chạy cho tới khi tắt máy.1 Chính vì tuổi thọ dài, Kerrisk lưu ý rằng daemon phải đặc biệt cẩn thận với rò rỉ bộ nhớ và rò rỉ bộ mô tả tệp — những lỗi mà một chương trình ngắn hạn có thể bỏ qua nhưng sẽ tích tụ chí mạng trong một tiến trình chạy hàng tháng.1
Daemon không chạy một lúc rồi tắt như lệnh thường; nó bật lên cùng máy và chỉ nghỉ khi máy tắt. Vì sống lâu như thế, một lỗi nhỏ "quên dọn dẹp" sẽ dồn lại thành vấn đề lớn theo năm tháng. Nhớ nhé: sống càng lâu thì càng phải dọn dẹp sạch sẽ.
The first trait is being long-lived: a daemon is often created at system startup and runs until shutdown.1 Because of this longevity, Kerrisk notes that a daemon must be especially wary of memory leaks and file-descriptor leaks — bugs a short-lived program can ignore but that accumulate fatally in a process running for months.1
Bạn gặp nó ở đâuWhere you meet it Khi một dịch vụ chạy nhiều ngày rồi "phình" bộ nhớ dần — thường là rò rỉ tích tụ, và cách chữa duy nhất là khởi động lại daemon.1 Khi một dịch vụ chạy lâu ngày tự ngốn thêm bộ nhớ — đó là rò rỉ dồn lại, phải khởi động lại mới hết. When a service running for days slowly "bloats" — usually an accumulating leak, and the only cure is to restart the daemon.1 -
no controlling terminal
Không terminal điều khiển: nhân không gửi tín hiệu bàn phím Không gắn cửa sổ dòng lệnh: Ctrl-C không tới được nó No controlling terminal: no keyboard signals reach it
Đặc điểm thứ hai là chạy nền và không có terminal điều khiển. Việc thiếu terminal điều khiển bảo đảm nhân không bao giờ tự sinh các tín hiệu điều khiển công việc hay liên quan terminal — như
SIGINT,SIGTSTP,SIGHUP— cho daemon.1 Đây là điểm cốt lõi phân biệt daemon với một chương trình chỉ đơn thuần chạy "ở nền" (background job) nhưng vẫn gắn với terminal.2Một lệnh chạy "ở nền" bình thường vẫn dính với cửa sổ dòng lệnh: đóng cửa sổ là nó cũng chết theo. Daemon thì cắt hẳn sợi dây đó, nên đóng cửa sổ hay đăng xuất cũng không ảnh hưởng. Nhớ nhé: cắt dây với terminal là bước khác biệt lớn nhất của daemon.
The second trait is running in the background with no controlling terminal. The lack of a controlling terminal ensures the kernel never automatically generates job-control or terminal-related signals — such as
SIGINT,SIGTSTP,SIGHUP— for the daemon.1 This is the core point distinguishing a daemon from a program that merely runs as a background job but remains attached to a terminal.2Bạn gặp nó ở đâuWhere you meet it Khi một dịch vụ tiếp tục chạy sau khi bạn đăng xuất SSH — vì nó không còn gắn với terminal của phiên đó nữa.2 Khi một dịch vụ vẫn chạy dù bạn đã đăng xuất — đó là nhờ nó không còn dính với cửa sổ nào. When a service keeps running after you log out of SSH — because it is no longer tied to that session's terminal.2Daemon trong nhânKernel-thread daemons
Trên Linux, một số daemon chạy như luồng nhân (kernel thread): mã của chúng là một phần của nhân và thường được tạo lúc khởi động. Khi liệt kê bằng
ps(1), tên chúng được đặt trong dấu ngoặc vuông[]. Ví dụ Kerrisk nêu làpdflush, định kỳ đẩy các trang "bẩn" (ví dụ từ buffer cache) xuống đĩa.1On Linux, some daemons run as kernel threads: their code is part of the kernel and they are typically created at startup. When listed withps(1), their names are shown in square brackets[]. Kerrisk's example ispdflush, which periodically flushes dirty pages (e.g. from the buffer cache) to disk.1Dọn dẹp khi tắt máyShutdown cleanup
Lúc tắt máy,
initgửiSIGTERMtới các tiến trình con; theo mặc định tín hiệu này kết thúc tiến trình. Nếu daemon cần dọn dẹp trước khi chết, nó đặt handler choSIGTERM— nhưng phải nhanh, vìinitgửi tiếpSIGKILLsau 5 giây.1At shutdown,initsendsSIGTERMto its children; by default that signal terminates the process. If a daemon needs to clean up before dying, it installs aSIGTERMhandler — but must be quick, sinceinitfollows up withSIGKILLafter 5 seconds.1 -
naming · 'd'
Quy ước đặt tên: thường kết thúc bằng chữ 'd' Tên daemon hay tận cùng bằng chữ 'd' Naming convention: names often end in 'd'
Theo một quy ước (không phải ai cũng tuân thủ), tên các daemon thường kết thúc bằng chữ d:
croncó phần giao tiếp quacrond, cònsshd,httpd,inetd,syslogdđều mang đuôi ấy.1 Đây chỉ là quy ước đặt tên giúp người quản trị nhận ra một tiến trình là dịch vụ nền, không phải một đặc tính kỹ thuật bắt buộc.2Nếu thấy một chương trình có tên tận cùng bằng chữ "d" — như
sshd,httpd— khả năng cao đó là một daemon. Đây chỉ là thói quen đặt tên để dễ nhận ra, không bắt buộc. Nhớ nhé: đuôi 'd' là dấu hiệu quen thuộc của một daemon.By convention (not universally observed), daemon names often end in the letter d:
sshd,httpd,inetd, andsyslogdall carry it.1 This is merely a naming convention that helps administrators recognize a background service, not a required technical property.2Bạn gặp nó ở đâuWhere you meet it Khi đọc đầu rapshaysystemctlvà lướt nhanh tìm các dịch vụ nền — đuôi 'd' là gợi ý trực quan đầu tiên.2 Khi dò danh sách tiến trình tìm dịch vụ nền — cứ để ý những tên kết thúc bằng 'd'. When scanningpsorsystemctloutput for background services — the trailing 'd' is the first visual hint.2
Cụm khái niệm 02 Concept cluster 02
Các bước biến một tiến trình thành daemon — và hàm trợ giúp becomeDaemon() Danh sách bước để biến một chương trình thành daemon The steps to turn a process into a daemon — and the becomeDaemon() helper
Để trở thành daemon, một chương trình thực hiện một chuỗi bước chuẩn: (1) gọi fork() rồi cho cha thoát, con tiếp tục — nhờ đó con được init nhận nuôi và chắc chắn không là trưởng nhóm tiến trình; (2) con gọi setsid() để lập một phiên mới và tự giải phóng khỏi mọi terminal điều khiển; (3) tùy chọn fork() lần hai để con cháu không còn là trưởng phiên (session leader); (4) đặt lại umask(0); (5) chdir("/") về một thư mục biết chắc; (6) đóng mọi bộ mô tả tệp thừa kế không dùng; (7) mở lại 0, 1, 2 vào /dev/null.12
Biến một chương trình thành daemon giống như chuẩn bị cho một người làm ca đêm độc lập: cho người cũ rời đi (fork + cha thoát), mở một "ca trực" mới tách khỏi phòng điều khiển (setsid), dọn bàn làm việc (umask, đổi thư mục), và rút hết các "đường dây" cũ nối tới màn hình (đóng và mở lại 0/1/2 vào /dev/null — tức "thùng rác"). Nhớ nhé: cắt dây với terminal, dọn môi trường, rồi mới chạy ngầm.
To become a daemon, a program performs a standard sequence of steps: (1) call fork(), then let the parent exit while the child continues — so the child is adopted by init and is guaranteed not to be a process group leader; (2) the child calls setsid() to start a new session and free itself of any controlling terminal; (3) optionally fork() a second time so the grandchild is no longer a session leader; (4) reset umask(0); (5) chdir("/") to a known directory; (6) close all unneeded inherited file descriptors; (7) reopen 0, 1, 2 to /dev/null.12
Định nghĩa. setsid() tạo một phiên (session) mới nếu tiến trình gọi không là trưởng nhóm tiến trình. Tiến trình gọi trở thành trưởng phiên của phiên mới, trưởng nhóm của một nhóm tiến trình mới, và không có terminal điều khiển. Đây chính là lý do bước fork() đầu tiên tồn tại: để bảo đảm con không là trưởng nhóm, nhờ đó setsid() mới thành công.13
Nói đơn giản. setsid() là lời gọi "mở một ca trực hoàn toàn mới" — tách tiến trình khỏi mọi terminal cũ. Nhưng nó chỉ chạy được nếu tiến trình chưa phải là "trưởng nhóm"; vì vậy ta fork trước để chắc chắn điều đó. Nhớ nhé: fork trước để setsid sau mới chạy được.
Definition. setsid() creates a new session if the calling process is not a process group leader. The caller becomes the session leader of the new session, the group leader of a new process group, and has no controlling terminal. This is exactly why the first fork() exists: to guarantee the child is not a group leader, so that setsid() can succeed.13
1. fork(); cha exit, con tiếp
2. setsid(); /* phiên mới, bỏ terminal */
3. fork() lần 2 (tùy chọn)
4. umask(0);
5. chdir("/");
6. đóng fd thừa kế không dùng
7. mở lại 0,1,2 → /dev/null
fork → cha thoát
setsid() → cắt terminal
umask(0), chdir("/")
đóng fd cũ
mở lại 0,1,2 vào /dev/null
1. fork(); parent exits, child continues
2. setsid(); /* new session, drop terminal */
3. fork() again (optional)
4. umask(0);
5. chdir("/");
6. close unneeded inherited fds
7. reopen 0,1,2 → /dev/null
-
fork + setsid
fork rồi cho cha thoát — để setsid() lập phiên mới thành công Cho cha rời đi, rồi mở ca trực mới fork, then the parent exits — so setsid() can start a new session
Bước
fork()đầu tiên phục vụ hai mục đích. Thứ nhất, nếu daemon được chạy từ dòng lệnh, việc cha kết thúc được shell nhận ra: shell in dấu nhắc mới và để con chạy tiếp ở nền. Thứ hai, con chắc chắn không là trưởng nhóm tiến trình — nó thừa kế process group ID từ cha nhưng mang một PID riêng khác — và đây là điều kiện bắt buộc đểsetsid()ở bước sau chạy được.13Ta fork ra một con, rồi cho cha thoát. Nhờ cha thoát, nếu bạn chạy từ dòng lệnh thì shell trả lại dấu nhắc ngay, còn con lặng lẽ chạy tiếp. Và con này chưa phải "trưởng nhóm", nên lát nữa nó mới mở được ca trực mới. Nhớ nhé: cha thoát để shell rảnh tay và để con đủ điều kiện gọi setsid.
The first
fork()serves two purposes. First, if the daemon is started from the command line, the parent's termination is noticed by the shell, which prints a fresh prompt and leaves the child to continue in the background. Second, the child is guaranteed not to be a process group leader — it inherits its parent's process group ID but has its own distinct PID — which is the precondition for the latersetsid()to succeed.13Bạn gặp nó ở đâuWhere you meet it Khi khởi chạy một daemon từ terminal và lập tức lấy lại được dấu nhắc shell — đó là vì tiến trình cha vừa thoát.1 Khi chạy một dịch vụ mà màn hình trả lại ngay dấu nhắc — đó là lúc "cha" vừa thoát. When launching a daemon from a terminal and immediately getting the shell prompt back — that is the parent having just exited.1Vì sao đôi khi fork lần haiWhy the optional second fork
Sau
setsid(), tiến trình là trưởng phiên và có thể vô tình giành lại một terminal điều khiển nếu sau này nó mở một thiết bị terminal. Cách phòng đơn giản làfork()lần hai và cho cha thoát: con cháu không còn là trưởng phiên, nên theo quy ước System V về việc giành terminal điều khiển, nó không thể vô tình có lại terminal. Cách khác là luôn dùng cờO_NOCTTYkhiopen()một thiết bị terminal.1Aftersetsid(), the process is a session leader and could accidentally reacquire a controlling terminal if it later opens a terminal device. The simple guard is a secondfork()with the parent exiting: the grandchild is no longer a session leader, so under the System V conventions for controlling-terminal acquisition it cannot accidentally regain one. The alternative is to always use theO_NOCTTYflag whenopen()-ing a terminal device.1setsid và nhóm tiến trìnhsetsid and process groups
Nếu tiến trình gọi
setsid()đang là trưởng nhóm tiến trình thì lời gọi thất bại với lỗiEPERM. Đó là lý do kỹ thuật khiến bước fork đầu không thể bỏ: fork tạo ra một con mang PID mới khác process group ID thừa kế, nên con chắc chắn không phải trưởng nhóm.13If the process callingsetsid()is already a process group leader, the call fails withEPERM. That is the technical reason the first fork is indispensable: fork yields a child with a new PID different from the inherited process group ID, so the child is certainly not a group leader.13 -
umask · chdir · /dev/null
Dọn môi trường: umask, thư mục làm việc, và các fd chuẩn Dọn bàn làm việc và rút các đường dây cũ Clean the environment: umask, working directory, standard fds
Sau khi tách phiên, daemon dọn dẹp môi trường. Nó gọi
umask(0)để xoá mặt nạ tạo tệp, bảo đảm các tệp nó tạo ra có đúng quyền mong muốn.14 Nóchdir("/")(hoặc một thư mục biết chắc) để không giữ khoá một hệ thống tệp khiến hệ thống đó không thể unmount. Nó đóng mọi bộ mô tả tệp thừa kế không dùng — vì fd là tài nguyên hữu hạn và daemon đã mất terminal. Cuối cùng, sau khi đóng 0, 1, 2, nó mở lại chúng vào/dev/null— thiết bị ảo luôn vứt bỏ mọi dữ liệu ghi vào.1Trước khi vào ca, người làm đêm dọn bàn và rút các dây nối cũ. Daemon cũng vậy: xoá "mặt nạ quyền tệp" để tệp mới có đúng quyền, chuyển về thư mục gốc "/" cho chắc chắn, đóng mọi "đường dây" thừa, rồi nối ba đường dây chuẩn (vào/ra/lỗi) vào "/dev/null" — một cái thùng rác nuốt sạch mọi thứ. Nhớ nhé: dọn umask, về thư mục chắc chắn, và đổ ra/vào/lỗi vào /dev/null.
After detaching the session, the daemon cleans its environment. It calls
umask(0)to clear the file-mode creation mask, ensuring files it creates get the exact permissions intended.14 Itchdir("/")(or to a known directory) so it does not keep a filesystem busy and prevent it from being unmounted. It closes all unneeded inherited file descriptors — since fds are a finite resource and the daemon has lost its terminal. Finally, after closing 0, 1, 2, it reopens them to/dev/null— the virtual device that always discards data written to it.1Bạn gặp nó ở đâuWhere you meet it Khi một dịch vụ ghiprintfra stdout mà "biến mất không dấu vết" — vì fd 1 của nó đã trỏ vào/dev/null; muốn thấy, phải ghi qua syslog.1 Khi một dịch vụ in ra màn hình mà chẳng thấy đâu — vì đầu ra của nó đã bị đổ vào thùng rác /dev/null. When a service'sprintfto stdout "vanishes" — because its fd 1 points at/dev/null; to see it, log via syslog.1Vì sao mở lại 0/1/2Why reopen 0/1/2
Daemon mở lại ba fd chuẩn vào
/dev/nullđể tránh một lỗi tinh vi: nếu để fd 0, 1, 2 đóng, một tệp mở sau này có thể vô tình nhận số 0, 1 hoặc 2, rồi một thư viện gọiprintf(ghi vào fd 1) sẽ làm hỏng tệp đó. Trỏ sẵn chúng vào thùng rác khiến ghi vào stdout/stderr trở nên vô hại.1The daemon reopens the three standard fds to/dev/nullto avoid a subtle bug: if 0, 1, 2 were left closed, a later-opened file could accidentally get descriptor 0, 1, or 2, and a library call toprintf(which writes fd 1) would corrupt that file. Pointing them at the bit-bucket makes writes to stdout/stderr harmless.1Bao nhiêu fd để đóngHow many fds to close
Khi không biết fd lớn nhất đang mở, hàm trợ giúp của sách đoán một trần
BD_MAX_CLOSEbằng 8192 và đóng lần lượt từ 0 tới trần đó. Một số hệ thống cung cấpclosefrom()để đóng tất cả fd từ một số trở lên một cách gọn hơn.1When the highest open fd is unknown, the book's helper guesses a ceilingBD_MAX_CLOSEof 8192 and closes each from 0 up to it. Some systems provideclosefrom()to close all fds from a given number upward more cleanly.1 -
becomeDaemon() · BD_*
becomeDaemon(): gói toàn bộ chuỗi bước sau một tham số cờ Một hàm làm sẵn mọi bước, điều khiển bằng cờ becomeDaemon(): the whole sequence behind one flags argument
Kerrisk đóng gói toàn bộ chuỗi bước vào một hàm trợ giúp:
int becomeDaemon(int flags);. Tham sốflagslà một bit-mask cho phép bỏ qua từng bước khi cần:BD_NO_CHDIR(khôngchdir("/")),BD_NO_CLOSE_FILES(không đóng các fd đang mở),BD_NO_REOPEN_STD_FDS(không mở lại 0/1/2 vào/dev/null), vàBD_NO_UMASK0(không gọiumask(0)).1 GọibecomeDaemon(0)là thực hiện đầy đủ mọi bước mặc định.1Thay vì tự gõ lại từng bước mỗi lần, ta viết một hàm làm sẵn tất cả tên là
becomeDaemon(). Muốn bỏ bớt bước nào thì bật một "công tắc" (cờ) tương ứng. Gọi với số 0 nghĩa là "làm hết mọi bước". Nhớ nhé: một hàm gói sẵn mọi bước, bật tắt từng bước bằng cờ.Kerrisk packages the whole sequence into a helper:
int becomeDaemon(int flags);. Theflagsargument is a bit-mask that lets you skip individual steps as needed:BD_NO_CHDIR(don'tchdir("/")),BD_NO_CLOSE_FILES(don't close open fds),BD_NO_REOPEN_STD_FDS(don't reopen 0/1/2 to/dev/null), andBD_NO_UMASK0(don't callumask(0)).1 CallingbecomeDaemon(0)performs the full default sequence.1Bạn gặp nó ở đâuWhere you meet it Khi một daemon cần giữ lại thư mục làm việc hiện tại (ví dụ để ghi tệp cạnh dữ liệu) — nó gọibecomeDaemon(BD_NO_CHDIR)thay vì về "/".1 Khi muốn daemon ở lại đúng thư mục hiện tại — bật cờ BD_NO_CHDIR để bỏ bước đổi thư mục. When a daemon must keep its current directory (say, to write files beside its data) — it callsbecomeDaemon(BD_NO_CHDIR)instead of moving to "/".1
Cụm khái niệm 03 Concept cluster 03
SIGHUP: tín hiệu "làm mới" mà daemon tự do tái dụng Gửi SIGHUP để bảo daemon làm mới chính nó SIGHUP: the "refresh" signal a daemon is free to repurpose
Vì nhiều daemon phải chạy liên tục, hai vấn đề xuất hiện: (1) một daemon thường đọc tham số vận hành từ một tệp cấu hình, và đôi khi ta muốn đổi tham số "ngay lúc chạy" mà không dừng rồi khởi động lại; (2) một số daemon tạo tệp log, và nếu nó không bao giờ đóng tệp log thì tệp cũ không thể bị dọn gọn (một tệp vẫn tồn tại chừng nào còn tiến trình mở nó). Giải pháp cho cả hai là để daemon đặt một handler cho SIGHUP.1
Daemon chạy mãi, nên có hai việc hay cần: bảo nó đọc lại tệp cấu hình vừa sửa, và bảo nó mở một tệp log mới (sau khi tệp log cũ đã được cất đi). Cả hai đều làm được bằng cách gửi cho nó một tín hiệu đặc biệt tên SIGHUP, và dặn trước nó phải làm gì khi nhận. Nhớ nhé: SIGHUP là tiếng gõ cửa bảo daemon "đọc lại cấu hình, mở lại log".
Because many daemons must run continuously, two problems arise: (1) a daemon typically reads operational parameters from a configuration file, and sometimes we want to change parameters "on the fly" without stopping and restarting; (2) some daemons produce log files, and if a daemon never closes its log file, the old file cannot be tidied away (a file persists as long as any process holds it open). The solution to both is for the daemon to establish a handler for SIGHUP.1
Định nghĩa. SIGHUP bình thường được sinh ra cho tiến trình điều khiển khi terminal điều khiển bị ngắt kết nối. Nhưng vì daemon đã không còn terminal điều khiển, nhân không bao giờ tự sinh SIGHUP cho nó — nên daemon được tự do tái sử dụng tín hiệu này cho mục đích riêng: ra hiệu "hãy khởi tạo lại".1
Nói đơn giản. Với chương trình thường, SIGHUP nghĩa là "cửa sổ terminal vừa bị đóng". Nhưng daemon đã bỏ terminal rồi, nên không bao giờ nhận SIGHUP một cách tự nhiên — thế là nó mượn luôn tín hiệu rảnh rỗi này để làm mã lệnh "làm mới". Nhớ nhé: SIGHUP với daemon là tín hiệu rảnh, dùng làm lệnh làm mới.
Definition. SIGHUP is normally generated for the controlling process when its controlling terminal is disconnected. But since a daemon has no controlling terminal, the kernel never generates SIGHUP for it — so the daemon is free to repurpose this signal for its own ends: to signal "reinitialize yourself."1
/* handler chỉ đặt một cờ */
void h(int s){ hupReceived = 1; }
sigaction(SIGHUP, &sa, NULL);
...
if (hupReceived){ /* đọc lại config, */
/* mở lại log */ hupReceived = 0; }
nhận SIGHUP → bật một lá cờ
vòng lặp chính thấy cờ →
đọc lại cấu hình, mở lại log
/* handler just sets a flag */
void h(int s){ hupReceived = 1; }
sigaction(SIGHUP, &sa, NULL);
...
if (hupReceived){ /* re-read config, */
/* reopen log */ hupReceived = 0; }
-
reread config
Đọc lại cấu hình mà không phải khởi động lại Đổi cấu hình ngay lúc đang chạy Re-read config without a restart
Nhu cầu đầu tiên: daemon đọc tham số từ một tệp cấu hình lúc khởi động. Khi quản trị viên sửa tệp ấy, gửi
SIGHUPđể daemon đọc lại và áp dụng tham số mới "ngay lúc chạy", không cần dừng rồi bật lại — tránh gián đoạn dịch vụ.1 Đây là lý do nhiều dịch vụ có lệnh kiểureloadthực chất chỉ gửiSIGHUP.2Sửa xong tệp cấu hình, bạn không muốn tắt hẳn dịch vụ (gây gián đoạn) chỉ để nó đọc cái mới. Gửi SIGHUP là nó tự đọc lại và áp dụng ngay. Nhớ nhé: sửa cấu hình xong, gửi SIGHUP là xong, khỏi tắt máy chủ.
The first need: a daemon reads parameters from a config file at startup. When an administrator edits that file, sending
SIGHUPmakes the daemon re-read and apply the new parameters "on the fly," with no stop-and-restart — avoiding service disruption.1 This is why many services'reloadcommand really just sendsSIGHUP.2Bạn gặp nó ở đâuWhere you meet it Lệnh kinh điểnkillall -HUP syslogdbuộc chínhsyslogdđọc lại/etc/syslog.confsau khi ta sửa quy tắc.1 Nhưkillall -HUP syslogd— bảo syslogd đọc lại tệp cấu hình của nó. The classickillall -HUP syslogdforcessyslogditself to re-read/etc/syslog.confafter you edit its rules.1 -
reopen log · logrotate
Mở lại tệp log — chìa khoá cho việc xoay vòng log Mở tệp log mới sau khi tệp cũ bị cất đi Reopen the log file — the key to log rotation
Nhu cầu thứ hai: nếu daemon giữ tệp log mở mãi, tệp đó không thể bị dọn gọn vì nó vẫn tồn tại chừng nào còn tiến trình mở. Khi nhận
SIGHUP, daemon đóng tệp log hiện tại và mở lại — cho phép công cụ xoay vòng log đổi tên tệp cũ rồi để daemon viết vào tệp mới.1 Kerrisk nhắc rằng chương trìnhlogrotateđược dùng để tự động hoá việc xoay vòng tệp log của daemon.1Nếu daemon cứ ghi mãi vào một tệp log, tệp đó phình to không dọn được. Người ta đổi tên tệp cũ đi, rồi gửi SIGHUP để daemon mở một tệp log mới tinh mà ghi tiếp. Công cụ
logrotatelàm chuyện này tự động. Nhớ nhé: đổi tên log cũ, gửi SIGHUP, daemon mở log mới.The second need: if a daemon keeps its log file open forever, that file cannot be tidied away because it persists as long as a process holds it open. On
SIGHUP, the daemon closes the current log file and reopens it — letting a rotation tool rename the old file and have the daemon write to a fresh one.1 Kerrisk notes thelogrotateprogram is used to automate rotation of daemon log files.1Bạn gặp nó ở đâuWhere you meet it Mỗi đêm khilogrotatenén nhật ký hôm qua và tạo tệp mới — nó dựa vào việc daemon mở lại log khi nhận tín hiệu.1 Mỗi khi nhật ký được cắt theo ngày và nén lại — đó là nhờ daemon mở lại log khi được báo hiệu. Each night whenlogrotatecompresses yesterday's log and starts a new file — it relies on the daemon reopening its log on signal.1Handler chỉ nên đặt một cờThe handler should just set a flag
Trong ví dụ Listing 37-3 của sách, handler
SIGHUPchỉ làm đúng một việc: đặt một biến cờ toàn cục khác 0. Vòng lặp chính của daemon kiểm tra cờ đó, và khi thấy nó được bật thì mới thực sự đọc lại cấu hình cùng mở lại tệp log, rồi đặt cờ về 0 để chờ lần sau. Cách này tránh làm những việc phức tạp, không an toàn trong ngữ cảnh async-signal.1In the book's Listing 37-3, theSIGHUPhandler does exactly one thing: set a nonzero global flag. The daemon's main loop checks that flag, and only when it is set does it actually re-read the config and reopen the log file, then clear the flag to wait for next time. This avoids doing complex, async-signal-unsafe work inside the handler.1Một biến thểA variant
Kerrisk lưu ý một cách tiếp cận khác mà một số daemon dùng khi nhận
SIGHUP: đóng mọi tệp rồi tự khởi động lại chính mình bằng một lời gọiexec(). Cách này nạp lại hoàn toàn chương trình thay vì chỉ đọc lại cấu hình.1Kerrisk notes an alternative some daemons take onSIGHUP: close all files and then restart themselves with anexec()call. This fully reloads the program rather than merely re-reading configuration.1 -
free signal
Vì sao SIGHUP lại "rảnh" để tái dụng Vì sao daemon mượn được SIGHUP Why SIGHUP is "free" to repurpose
Điều làm
SIGHUPtrở thành lựa chọn tự nhiên chính là đặc điểm ở phần 01: vì daemon không có terminal điều khiển, nhân không bao giờ tự sinhSIGHUPcho nó. Tín hiệu này do đó "rảnh" và không gây nhập nhằng — gửi nó tới daemon chỉ có thể là một mệnh lệnh cố ý từ quản trị viên.12Nhớ ở phần đầu: daemon đã bỏ terminal, nên nó chẳng bao giờ bị SIGHUP "tự nhiên" bay tới. Chính vì vậy tín hiệu này bỏ không, daemon mượn dùng mà không sợ nhầm với tín hiệu thật. Nhớ nhé: bỏ terminal khiến SIGHUP bỏ trống, thành tín hiệu lệnh an toàn.
What makes
SIGHUPthe natural choice is exactly the trait from section 01: because a daemon has no controlling terminal, the kernel never generatesSIGHUPfor it. The signal is therefore "free" and unambiguous — sending it to a daemon can only be a deliberate command from an administrator.12Bạn gặp nó ở đâuWhere you meet it Khi tài liệu một dịch vụ viết "gửi SIGHUP để reload" — giờ bạn hiểu vì sao đúng tín hiệu đó, không phải tín hiệu khác.2 Khi thấy hướng dẫn "gửi SIGHUP để nạp lại" — giờ bạn hiểu lý do chọn đúng tín hiệu này. When a service's docs say "send SIGHUP to reload" — now you know why that exact signal, and not another.2
Cụm khái niệm 04 Concept cluster 04
syslog: một đường ghi log tập trung cho mọi daemon của hệ thống syslog: nơi ghi log chung của cả hệ thống syslog: one centralized logging path for every daemon
Nếu mỗi daemon tự ghi log theo kiểu riêng vào tệp riêng thì người quản trị khó lòng theo dõi. Tiện ích syslog ra đời để giải quyết: nó cho một đường ghi log tập trung, duy nhất mà mọi tiến trình dùng được. Kiến trúc có hai thành phần chính: daemon syslogd và hàm thư viện syslog(3).15 Tiện ích này xuất hiện lần đầu ở 4.2BSD; SUSv3 đã chuẩn hoá syslog(3) và các hàm liên quan, nhưng để ngỏ phần cài đặt của syslogd và định dạng tệp syslog.conf.18
Tưởng tượng một toà nhà mà mỗi phòng tự ghi nhật ký riêng một kiểu — quản lý sẽ phát điên. syslog giống như một cuốn sổ ghi chép chung đặt ở sảnh: mọi phòng (mọi chương trình) đều gửi ghi chú vào đó, và có một nhân viên (syslogd) sắp xếp, phân loại các ghi chú ấy. Nhớ nhé: syslog là cuốn sổ nhật ký chung của cả hệ thống.
If every daemon logged in its own way to its own file, an administrator could hardly keep track. The syslog facility was devised to solve this: it provides a single, centralized logging path usable by any process. Its architecture has two principal components: the syslogd daemon and the syslog(3) library function.15 The facility first appeared in 4.2BSD; SUSv3 standardized syslog(3) and related functions but leaves the implementation of syslogd and the format of syslog.conf unspecified.18
Định nghĩa. Mỗi thông báo qua syslog mang hai thuộc tính: một facility (loại chương trình sinh ra thông báo, ví dụ LOG_DAEMON, LOG_USER, LOG_KERN) và một level (mức nghiêm trọng/ưu tiên, từ cao xuống thấp: LOG_EMERG, LOG_ALERT, LOG_CRIT, LOG_ERR, LOG_WARNING, LOG_NOTICE, LOG_INFO, LOG_DEBUG). Tham số priority truyền cho syslog() là OR của một facility với một level.15
Nói đơn giản. Mỗi ghi chú gửi vào syslog kèm hai nhãn: "của ai" (facility — loại chương trình) và "khẩn cấp cỡ nào" (level — từ "khẩn nguy" tới "gỡ lỗi"). Nhờ hai nhãn này mà người ta lọc và sắp xếp log dễ dàng. Nhớ nhé: mỗi dòng log có nhãn "của ai" và nhãn "khẩn cỡ nào".
Definition. Each syslog message carries two attributes: a facility (the kind of program producing it, e.g. LOG_DAEMON, LOG_USER, LOG_KERN) and a level (severity/priority, from highest to lowest: LOG_EMERG, LOG_ALERT, LOG_CRIT, LOG_ERR, LOG_WARNING, LOG_NOTICE, LOG_INFO, LOG_DEBUG). The priority argument to syslog() is a facility ORed with a level.15
#include <syslog.h>
openlog(ident, options, facility);
syslog(priority, format, ...);
/* priority = facility | level */
closelog(void);
openlog(...) → đặt mặc định
syslog(mức, "thông báo...")
closelog() → đóng lại
#include <syslog.h>
openlog(ident, options, facility);
syslog(priority, format, ...);
/* priority = facility | level */
closelog(void);
-
syslogd · /dev/log · klogd
Kiến trúc: syslogd nhận qua /dev/log, klogd góp log của nhân Ai nhận log, log đi qua đâu The architecture: syslogd, /dev/log, and klogd
Daemon syslogd nhận thông báo từ hai nguồn: một socket miền UNIX tên
/dev/log(giữ các thông báo sinh ra cục bộ), và — tuỳ chọn — một socket miền Internet (cổng UDP 514) cho thông báo tới qua mạng TCP/IP. Hàmsyslog(3)dựng thông báo theo định dạng chuẩn rồi đặt lên/dev/logchosyslogdđọc. Ngoài ra, daemon klogd (Kernel Log daemon) thu các thông báo của nhân (sinh ra bằngprintk()) qua/proc/kmsgvà system callsyslog(2), rồi cũng đặt lên/dev/log.16Nhân viên ghi sổ tên
syslogdnhận ghi chú qua một "khe thư" tên/dev/log. Chương trình thường bỏ ghi chú vào khe đó bằng hàmsyslog(). Còn những ghi chú từ chính nhân hệ điều hành thì có một nhân viên riêng tênklogdthu gom rồi cũng bỏ vào khe ấy. Nhớ nhé: mọi log đổ vào khe /dev/log cho syslogd ghi sổ.The syslogd daemon accepts messages from two sources: a UNIX domain socket,
/dev/log(holding locally produced messages), and — optionally — an Internet domain socket (UDP port 514) for messages arriving across a TCP/IP network. Thesyslog(3)function builds a message in a standard format and places it on/dev/logforsyslogdto read. Additionally, the klogd (Kernel Log daemon) collects kernel messages (produced viaprintk()) through/proc/kmsgand thesyslog(2)system call, then also places them on/dev/log.16Bạn gặp nó ở đâuWhere you meet it Khi gửi log tập trung nhiều máy về một máy chủ log qua cổng UDP 514 — đó chính là socket miền Internet củasyslogd.1 Khi gom log của nhiều máy về một chỗ qua mạng — đó là cổng mạng của syslogd. When centralizing many machines' logs to one log server over UDP port 514 — that is exactlysyslogd's Internet domain socket.1syslog(2) khác syslog(3)syslog(2) is not syslog(3)
Dù trùng tên,
syslog(2)vàsyslog(3)làm việc hoàn toàn khác nhau.syslog(2)là một system call thao tác với bộ đệm log của nhân; glibc cung cấp giao diện cho nó dưới tênklogctl(). Khi sách nói "syslog()" trong phần này, luôn hiểu làsyslog(3)— hàm thư viện ghi log ứng dụng.16Despite the shared name,syslog(2)andsyslog(3)do quite different things.syslog(2)is a system call operating on the kernel log buffer; glibc provides an interface to it under the nameklogctl(). When the book says "syslog()" in this section, it always meanssyslog(3)— the application-logging library function.16Đích đến của thông báoWhere messages end up
Dựa trên facility và level,
syslogdđịnh đoạt mỗi thông báo theo/etc/syslog.conf. Đích đến có thể là một terminal hay console ảo, một tệp trên đĩa, một FIFO, một hay nhiều (hoặc tất cả) người dùng đang đăng nhập, hoặc một tiến trình khác (thường là mộtsyslogdkhác trên máy từ xa).17Based on facility and level,syslogddisposes of each message per/etc/syslog.conf. Destinations can be a terminal or virtual console, a disk file, a FIFO, one or more (or all) logged-in users, or another process (typically anothersyslogdon a remote host).17 -
openlog / syslog / closelog
API: mở, ghi, đóng — và ident, options, facility Ba hàm: mở sổ, ghi dòng, đóng sổ The API: open, log, close — ident, options, facility
Bất kỳ tiến trình nào cũng dùng được
syslog(3).openlog(ident, log_options, facility)thiết lập mặc định cho các lời gọisyslog()về sau; gọi nó là tuỳ chọn.identlà chuỗi được chèn vào đầu mỗi thông báo (nếu làNULL, glibc tự dùng tên chương trình).log_optionslà bit-mask gồm các cờ nhưLOG_PID(ghi kèm PID của tiến trình gọi),LOG_CONS,LOG_NDELAY,LOG_NOWAIT,LOG_ODELAY. Cuối cùngcloselog()giải phóng bộ mô tả tệp dùng cho/dev/log.15Ba hàm như mở sổ, ghi dòng, đóng sổ.
openlog()đặt sẵn vài thứ mặc định (như cái "tên" gắn vào mỗi dòng và có kèm số PID hay không).syslog()ghi một dòng.closelog()đóng lại. Thường người ta bỏ quaopenlog()vàcloselog()cho gọn. Nhớ nhé: openlog đặt mặc định, syslog ghi, closelog đóng.Any process can use
syslog(3).openlog(ident, log_options, facility)establishes defaults for subsequentsyslog()calls; calling it is optional.identis a string prepended to each message (ifNULL, glibc uses the program name).log_optionsis a bit-mask of flags such asLOG_PID(log the caller's PID),LOG_CONS,LOG_NDELAY,LOG_NOWAIT,LOG_ODELAY. Finally,closelog()frees the file descriptor used for/dev/log.15Bạn gặp nó ở đâuWhere you meet it Ví dụ của sách:openlog(argv[0], LOG_PID | LOG_CONS | LOG_NOWAIT, LOG_LOCAL0), rồisyslog(LOG_USER | LOG_INFO, "Exiting")— lời gọi sau ghi đè facility mặc định do openlog đặt.1 Như trong sách: gọi openlog đặt tên và tuỳ chọn, rồi syslog ghi từng dòng với mức tương ứng. The book's example:openlog(argv[0], LOG_PID | LOG_CONS | LOG_NOWAIT, LOG_LOCAL0), thensyslog(LOG_USER | LOG_INFO, "Exiting")— the later call overrides the default facility set by openlog.1Một bẫy bảo mậtA security trap
Không bao giờ truyền chuỗi do người dùng cung cấp làm tham số định dạng:
syslog(priority, user_string)là lỗ hổng format-string. Luôn viếtsyslog(priority, "%s", user_string)để chuỗi được xử lý như dữ liệu, không phải định dạng.1Never pass a user-supplied string as the format argument:syslog(priority, user_string)is a format-string vulnerability. Always writesyslog(priority, "%s", user_string)so the string is treated as data, not a format.1Lọc theo mứcFiltering by level
setlogmask()đặt một mặt nạ quyết định mức nào được ghi; thông báo có mức không nằm trong mặt nạ bị loại bỏ. MacroLOG_MASK()(do SUSv3 quy định) tạo mặt nạ cho một mức, cònLOG_UPTO()(phổ biến nhưng không chuẩn hoá) tạo mặt nạ cho một mức và tất cả mức nghiêm trọng hơn.15setlogmask()sets a mask deciding which levels are logged; messages whose level is not in the mask are discarded. TheLOG_MASK()macro (specified by SUSv3) builds a mask for one level, whileLOG_UPTO()(common but unspecified) builds a mask for a level and all more-severe levels.15 -
/etc/syslog.conf
/etc/syslog.conf: luật "facility.level → action" Tệp cấu hình quyết định log đi đâu /etc/syslog.conf: the "facility.level → action" rules
Tệp
/etc/syslog.confđiều khiển hoạt động củasyslogd. Mỗi luật có dạngfacility.level action: cặp facility và level gọi chung là selector (vì nó chọn ra thông báo nào bị tác động), còn action cho biết gửi chúng đi đâu. Ví dụ, một levelerrnghĩa là "mứcLOG_ERRtrở lên", còn ký tự đại diện*cho facility nghĩa là "mọi loại chương trình".17 Sau khi sửa tệp, phải báosyslogdkhởi tạo lại — bằng chínhkillall -HUP syslogd.1Tệp
/etc/syslog.conflà bảng nội quy cho anh ghi sổ syslogd: mỗi dòng nói "log loại này, mức này thì ghi vào chỗ kia". Sửa bảng nội quy xong, ta gửi SIGHUP để anh ấy đọc lại. Nhớ nhé: syslog.conf dạy syslogd log nào đi đâu; sửa xong gửi SIGHUP.The
/etc/syslog.conffile controlssyslogd's operation. Each rule has the formfacility.level action: the facility and level pair is together the selector (since it selects which messages are affected), while the action says where to send them. For instance, a level oferrmeans "LOG_ERRand above," and a*wildcard for facility means "all kinds of program."17 After editing the file, you must asksyslogdto reinitialize — with that samekillall -HUP syslogd.1Bạn gặp nó ở đâuWhere you meet it Khi bạn muốn tách riêng log xác thực vào một tệp — thêm một luật nhưauth.notice /var/log/auth.logrồi gửi SIGHUP.7 Khi muốn một loại log riêng ra một tệp — thêm một dòng luật rồi gửi SIGHUP cho syslogd. When you want to split authentication logs into their own file — add a rule likeauth.notice /var/log/auth.logand send SIGHUP.7
Ba ngộ nhận thường gặp Three common misconceptions
“Chạy nền (&) là đã thành daemon” “Backgrounding with & makes it a daemon”
Ngộ nhận: thêm dấu & cuối lệnh là chương trình đã trở thành daemon.
Thực tế: một job nền vẫn thuộc phiên của shell và vẫn có terminal điều khiển; đóng terminal hay đăng xuất có thể khiến nó nhận SIGHUP và chết. Daemon thật phải gọi setsid() để tách khỏi phiên và bỏ terminal điều khiển.13
Myth: appending & to a command makes it a daemon.
Reality: a background job still belongs to the shell's session and still has a controlling terminal; closing the terminal or logging out can deliver it SIGHUP and kill it. A true daemon must call setsid() to leave the session and shed the controlling terminal.13
“syslog(2) và syslog(3) là một hàm” “syslog(2) and syslog(3) are the same call”
Ngộ nhận: trùng tên nên chắc là cùng một thứ.
Thực tế: syslog(3) là hàm thư viện để ứng dụng ghi log; syslog(2) là system call thao tác với bộ đệm log của nhân (glibc gọi là klogctl()). Hai thứ khác hẳn nhau về nhiệm vụ.16
Myth: same name, so they must be the same.
Reality: syslog(3) is the library function for application logging; syslog(2) is a system call operating on the kernel log buffer (glibc calls it klogctl()). They do quite different jobs.16
“level cao là số lớn, ưu tiên thấp” “A higher level number means lower priority”
Ngộ nhận: LOG_DEBUG là mức "cao nhất" vì nghe quan trọng.
Thực tế: thang mức đi từ nghiêm trọng nhất xuống ít nghiêm trọng nhất: LOG_EMERG (hệ thống không dùng được) là cao nhất, LOG_DEBUG (thông báo gỡ lỗi) là thấp nhất. Bộ lọc LOG_UPTO(LOG_ERR) giữ LOG_ERR và mọi mức nghiêm trọng hơn.15
Myth: LOG_DEBUG is the "highest" level because it sounds important.
Reality: the scale runs from most severe down to least severe: LOG_EMERG (system unusable) is highest, LOG_DEBUG (debug message) is lowest. A filter of LOG_UPTO(LOG_ERR) keeps LOG_ERR and every more-severe level.15
Tự kiểm Self-check
Năm câu hỏi để biết bạn đã nắm cách daemon ra đời và ghi log Năm câu hỏi kiểm tra nhanh Five questions to check you grasped daemons and logging
Trả lời được những câu này nghĩa là bạn đã nắm đặc điểm của daemon, chuỗi bước tạo ra nó, vai trò của SIGHUP, và kiến trúc syslog.
Nếu trả lời được, nghĩa là bạn đã hiểu chương này.
Answering these means you grasp a daemon's characteristics, the sequence that creates one, the role of SIGHUP, and the syslog architecture.
-
Hai đặc điểm định nghĩa một daemon là gì? Vì sao việc không có terminal điều khiển lại quan trọng?
Gợi ý sâu hơn: nhân sẽ không tự sinh những tín hiệu nào cho một tiến trình không có terminal điều khiển?
-
Kể chuỗi bước biến một tiến trình thành daemon. Vì sao phải
fork()trước khi gọisetsid()?Gợi ý sâu hơn: lần
fork()thứ hai (tuỳ chọn) phòng ngừa điều gì, và cờO_NOCTTYlà lựa chọn thay thế ra sao? -
Vì sao daemon mở lại 0, 1, 2 vào
/dev/nullthay vì để chúng đóng? HàmbecomeDaemon()dùng các cờBD_*để làm gì?Gợi ý sâu hơn: nếu để fd 0/1/2 đóng, lỗi tinh vi nào có thể xảy ra khi một thư viện gọi
printf? -
Vì sao
SIGHUPlà lựa chọn tự nhiên để ra hiệu cho daemon khởi tạo lại? Nó giải quyết hai vấn đề nào?Gợi ý sâu hơn: vì sao handler
SIGHUPchỉ nên đặt một cờ rồi để vòng lặp chính xử lý? -
Kiến trúc syslog gồm những thành phần nào? Phân biệt
syslog(3)vớisyslog(2), và cho biết facility cùng level là gì.Gợi ý sâu hơn: vì sao
syslog(priority, user_string)là lỗ hổng, và viết lại thế nào cho an toàn?
Daemon khác một chương trình thường ở chỗ nào?
Vì sao daemon phải tự cắt khỏi cửa sổ dòng lệnh?
Vì sao đầu ra của daemon lại được đổ vào /dev/null?
Gửi SIGHUP cho một daemon để làm gì?
syslog dùng để làm gì, và ai là người ghi sổ?
What two characteristics define a daemon? Why does having no controlling terminal matter?
List the steps that turn a process into a daemon. Why must you
fork()before callingsetsid()?Why does a daemon reopen 0, 1, 2 to
/dev/nullrather than leave them closed? What are theBD_*flags ofbecomeDaemon()for?Why is
SIGHUPthe natural signal to tell a daemon to reinitialize? Which two problems does it solve?What are the components of the syslog architecture? Distinguish
syslog(3)fromsyslog(2), and define facility and level.
Khép lại chương Closing the chapter
Một daemon là chương trình được dạy cách tự lui vào hậu trường: cắt khỏi terminal bằng
setsid(), dọn môi trường, rồi sống lâu và lặng lẽ. Vì không còn màn hình, nó kể chuyện qua syslog, và nghe lệnh "làm mới" quaSIGHUP. Nắm được chuỗi bước này, bạn đã sẵn sàng cho câu hỏi kế — làm sao viết các tiến trình đặc quyền ấy một cách an toàn.1Daemon là chương trình biết tự lui vào hậu trường: cắt dây với cửa sổ dòng lệnh, dọn dẹp, rồi chạy ngầm bền bỉ. Nó kể chuyện qua syslog và nghe lệnh làm mới qua SIGHUP. Nhớ nhé: thành daemon là lui vào hậu trường đúng cách, rồi nói chuyện qua syslog.
A daemon is a program taught to step into the background: detaching from the terminal with
setsid(), cleaning its environment, then living long and quietly. Having no screen, it speaks through syslog, and hears the "refresh" command throughSIGHUP. Grasp this sequence and you are ready for the next question — how to write such privileged processes securely.1