The Linux Programming Interface Chương 37 · Daemons

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'

  1. 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
  2. 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.2

    Mộ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.2

    Bạ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.2

    Daemon 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 with ps(1), their names are shown in square brackets []. Kerrisk's example is pdflush, which periodically flushes dirty pages (e.g. from the buffer cache) to disk.1

    Dọn dẹp khi tắt máyShutdown cleanup

    Lúc tắt máy, init gửi SIGTERM tớ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 cho SIGTERM — nhưng phải nhanh, vì init gửi tiếp SIGKILL sau 5 giây.1At shutdown, init sends SIGTERM to its children; by default that signal terminates the process. If a daemon needs to clean up before dying, it installs a SIGTERM handler — but must be quick, since init follows up with SIGKILL after 5 seconds.1

  3. 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: cron có phần giao tiếp qua crond, còn sshd, 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.2

    Nế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, and syslogd all carry it.1 This is merely a naming convention that helps administrators recognize a background service, not a required technical property.2

    Bạn gặp nó ở đâuWhere you meet it Khi đọc đầu ra ps hay systemctl và 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 scanning ps or systemctl output 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

  1. 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.13

    Ta 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 later setsid() to succeed.13

    Bạ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.1

    Vì 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_NOCTTY khi open() một thiết bị terminal.1After setsid(), 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 second fork() 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 the O_NOCTTY flag when open()-ing a terminal device.1

    setsid 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ỗi EPERM. Đó 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 calling setsid() is already a process group leader, the call fails with EPERM. 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

  2. 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.1

    Trướ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 It chdir("/") (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.1

    Bạn gặp nó ở đâuWhere you meet it Khi một dịch vụ ghi printf ra 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's printf to stdout "vanishes" — because its fd 1 points at /dev/null; to see it, log via syslog.1

    Vì 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ọi printf (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/null to 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 to printf (which writes fd 1) would corrupt that file. Pointing them at the bit-bucket makes writes to stdout/stderr harmless.1

    Bao 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_CLOSE bằng 8192 và đóng lần lượt từ 0 tới trần đó. Một số hệ thống cung cấp closefrom() để đó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 ceiling BD_MAX_CLOSE of 8192 and closes each from 0 up to it. Some systems provide closefrom() to close all fds from a given number upward more cleanly.1

  3. 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ố flags là một bit-mask cho phép bỏ qua từng bước khi cần: BD_NO_CHDIR (không chdir("/")), 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ọi umask(0)).1 Gọi becomeDaemon(0) là thực hiện đầy đủ mọi bước mặc định.1

    Thay 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);. The flags argument is a bit-mask that lets you skip individual steps as needed: BD_NO_CHDIR (don't chdir("/")), BD_NO_CLOSE_FILES (don't close open fds), BD_NO_REOPEN_STD_FDS (don't reopen 0/1/2 to /dev/null), and BD_NO_UMASK0 (don't call umask(0)).1 Calling becomeDaemon(0) performs the full default sequence.1

    Bạ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ọi becomeDaemon(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 calls becomeDaemon(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; }

  1. 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ểu reload thực chất chỉ gửi SIGHUP.2

    Sử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 SIGHUP makes 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' reload command really just sends SIGHUP.2

    Bạn gặp nó ở đâuWhere you meet it Lệnh kinh điển killall -HUP syslogd buộc chính syslogd đọc lại /etc/syslog.conf sau 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 classic killall -HUP syslogd forces syslogd itself to re-read /etc/syslog.conf after you edit its rules.1
  2. 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ình logrotate được dùng để tự động hoá việc xoay vòng tệp log của daemon.1

    Nế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ụ logrotate là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 the logrotate program is used to automate rotation of daemon log files.1

    Bạn gặp nó ở đâuWhere you meet it Mỗi đêm khi logrotate né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 when logrotate compresses yesterday's log and starts a new file — it relies on the daemon reopening its log on signal.1

    Handler 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 SIGHUP chỉ 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, the SIGHUP handler 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.1

    Mộ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ọi exec(). 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 on SIGHUP: close all files and then restart themselves with an exec() call. This fully reloads the program rather than merely re-reading configuration.1

  3. 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 SIGHUP trở 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ự sinh SIGHUP cho 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.12

    Nhớ ở 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 SIGHUP the natural choice is exactly the trait from section 01: because a daemon has no controlling terminal, the kernel never generates SIGHUP for it. The signal is therefore "free" and unambiguous — sending it to a daemon can only be a deliberate command from an administrator.12

    Bạ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);

  1. 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àm syslog(3) dựng thông báo theo định dạng chuẩn rồi đặt lên /dev/log cho syslogd đọc. Ngoài ra, daemon klogd (Kernel Log daemon) thu các thông báo của nhân (sinh ra bằng printk()) qua /proc/kmsg và system call syslog(2), rồi cũng đặt lên /dev/log.16

    Nhân viên ghi sổ tên syslogd nhậ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àm syslog(). 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ên klogd thu 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. The syslog(3) function builds a message in a standard format and places it on /dev/log for syslogd to read. Additionally, the klogd (Kernel Log daemon) collects kernel messages (produced via printk()) through /proc/kmsg and the syslog(2) system call, then also places them on /dev/log.16

    Bạ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ủa syslogd.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 exactly syslogd's Internet domain socket.1

    syslog(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ên klogctl(). 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) and syslog(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 name klogctl(). When the book says "syslog()" in this section, it always means syslog(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ột syslogd khác trên máy từ xa).17Based on facility and level, syslogd disposes 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 another syslogd on a remote host).17

  2. 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ọi syslog() về sau; gọi nó là tuỳ chọn. ident là 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_options là 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ùng closelog() giải phóng bộ mô tả tệp dùng cho /dev/log.15

    Ba 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ỏ qua openlog() 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 subsequent syslog() calls; calling it is optional. ident is a string prepended to each message (if NULL, glibc uses the program name). log_options is a bit-mask of flags such as LOG_PID (log the caller's PID), LOG_CONS, LOG_NDELAY, LOG_NOWAIT, LOG_ODELAY. Finally, closelog() frees the file descriptor used for /dev/log.15

    Bạ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ồi syslog(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), then syslog(LOG_USER | LOG_INFO, "Exiting") — the later call overrides the default facility set by openlog.1

    Mộ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ết syslog(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 write syslog(priority, "%s", user_string) so the string is treated as data, not a format.1

    Lọ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ỏ. Macro LOG_MASK() (do SUSv3 quy định) tạo mặt nạ cho một mức, còn LOG_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. The LOG_MASK() macro (specified by SUSv3) builds a mask for one level, while LOG_UPTO() (common but unspecified) builds a mask for a level and all more-severe levels.15

  3. /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ủa syslogd. Mỗi luật có dạng facility.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 level err nghĩa là "mức LOG_ERR trở 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áo syslogd khởi tạo lại — bằng chính killall -HUP syslogd.1

    Tệp /etc/syslog.conf là 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.conf file controls syslogd's operation. Each rule has the form facility.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 of err means "LOG_ERR and above," and a * wildcard for facility means "all kinds of program."17 After editing the file, you must ask syslogd to reinitialize — with that same killall -HUP syslogd.1

    Bạ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.log rồ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 like auth.notice  /var/log/auth.log and 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.

  1. 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?

  2. 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ọi setsid()?

    Gợi ý sâu hơn: lần fork() thứ hai (tuỳ chọn) phòng ngừa điều gì, và cờ O_NOCTTY là lựa chọn thay thế ra sao?

  3. Vì sao daemon mở lại 0, 1, 2 vào /dev/null thay vì để chúng đóng? Hàm becomeDaemon() 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?

  4. Vì sao SIGHUP là 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 SIGHUP chỉ nên đặt một cờ rồi để vòng lặp chính xử lý?

  5. Kiến trúc syslog gồm những thành phần nào? Phân biệt syslog(3) với syslog(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?

  1. Daemon khác một chương trình thường ở chỗ nào?

  2. Vì sao daemon phải tự cắt khỏi cửa sổ dòng lệnh?

  3. Vì sao đầu ra của daemon lại được đổ vào /dev/null?

  4. Gửi SIGHUP cho một daemon để làm gì?

  5. syslog dùng để làm gì, và ai là người ghi sổ?

  1. What two characteristics define a daemon? Why does having no controlling terminal matter?

  2. List the steps that turn a process into a daemon. Why must you fork() before calling setsid()?

  3. Why does a daemon reopen 0, 1, 2 to /dev/null rather than leave them closed? What are the BD_* flags of becomeDaemon() for?

  4. Why is SIGHUP the natural signal to tell a daemon to reinitialize? Which two problems does it solve?

  5. What are the components of the syslog architecture? Distinguish syslog(3) from syslog(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" qua SIGHUP. 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.1

Daemon 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 through SIGHUP. Grasp this sequence and you are ready for the next question — how to write such privileged processes securely.1