26/10/2026 · VietTechies · Biên tập

Trong FreeBSD 4.1, kqueue do Jonathan Lemon thiết kế có thể gộp nhiều lần kích hoạt sự kiện thành một thông báo duy nhất. Tài liệu hướng dẫn đề ngày 14 tháng 4 năm 2000 nêu rõ cách hoạt động này: ứng dụng có thể biết một điều kiện cần được xử lý mà không phải nhận bản ghi riêng cho từng lần xảy ra.
Lemon muốn giải quyết hai hạn chế trong cách Unix xử lý sự kiện: khả năng mở rộng khi chương trình theo dõi ngày càng nhiều bộ mô tả tài nguyên (descriptor), và khả năng thông báo hoạt động không chỉ ở các đầu kết nối mạng (socket).
Kqueue cho phép ứng dụng đăng ký những điều kiện cần theo dõi và nhận thông báo qua một giao diện chung. Với thiết kế đó, lập trình viên cần hiểu rõ một điểm: thông báo có thể tổng hợp hoạt động lặp lại, thay vì lưu lại lịch sử theo trình tự thời gian.
Bài toán không chỉ là socket có dữ liệu để đọc
Trong phần tóm tắt bài báo FREENIX năm 2001, Kqueue: A generic and scalable event notification facility, Lemon mô tả những hạn chế ông muốn giải quyết. Theo cách làm truyền thống, ứng dụng Unix dùng các lời gọi hệ thống select() hoặc poll() để chờ hoạt động trên socket hoặc các descriptor khác. Descriptor là một định danh mà chương trình dùng để tham chiếu đến tài nguyên đang mở.
Lemon xác định một vấn đề là khả năng mở rộng khi số lượng descriptor tăng. Phạm vi sự kiện cũng là một hạn chế: ứng dụng còn có thể cần theo dõi tín hiệu, thay đổi trong hệ thống tệp và việc hoàn tất thao tác vào/ra bất đồng bộ, tức thao tác hoàn thành sau khi đã được gửi đi.
Phần tóm tắt đề xuất một cơ chế thông báo hỗ trợ nhiều nguồn sự kiện, đồng thời cho phép bổ sung nguồn mà không thay đổi giao diện ứng dụng. Không nên nhầm năm công bố bài báo với thời điểm giao diện ra mắt: tài liệu hướng dẫn thời đó ghi rằng kqueue() và kevent() xuất hiện lần đầu trong FreeBSD 4.1.
Đăng ký điều kiện cần theo dõi, rồi nhận thông báo
Cơ chế cơ bản gồm hai lời gọi: kqueue() tạo một hàng đợi sự kiện trong nhân hệ điều hành (kernel) và trả về descriptor của hàng đợi; kevent() đăng ký các điều kiện cần theo dõi với hàng đợi đó và lấy những thông báo đang chờ. Kernel là phần lõi của hệ điều hành, có nhiệm vụ kiểm tra các điều kiện được theo dõi.
Mỗi đăng ký kết hợp một định danh với một bộ lọc. Định danh xác định đối tượng cần theo dõi, thường thông qua descriptor tệp. Bộ lọc xác định điều kiện cần quan tâm. Tài liệu hướng dẫn thời đó mô tả các bộ lọc cho dữ liệu có thể đọc, tài nguyên có thể ghi, thay đổi ở tệp, hoạt động của tiến trình và tín hiệu.
Ví dụ, một ứng dụng muốn theo dõi xem socket đang mở có dữ liệu để đọc hay chưa. Ứng dụng đăng ký socket đó với bộ lọc đọc. Khi bộ lọc xác định điều kiện cần được thông báo, một thông báo sẽ sẵn sàng để ứng dụng lấy về.
Ứng dụng có thể gửi thay đổi đăng ký và lấy kết quả trong cùng một lời gọi kevent(). Tài liệu hướng dẫn quy định rõ thứ tự: mọi thay đổi gửi lên đều được áp dụng trước khi đọc các sự kiện đang chờ. Thêm lại cùng một cặp định danh và bộ lọc sẽ sửa đăng ký hiện có, thay vì tạo bản trùng lặp.
Một thông báo có thể tổng hợp hoạt động lặp lại
Tên gọi hàng đợi có thể khiến người đọc nghĩ rằng mỗi lần kích hoạt đều tạo thêm một mục. Nhưng quy tắc hoạt động được tài liệu mô tả lại khác. Nhiều sự kiện kích hoạt cùng một bộ lọc được gộp vào một cấu trúc kevent.
Gộp sự kiện không có nghĩa thông tin trả về lúc nào cũng chỉ là có hoặc không. Ý nghĩa của thông tin phụ thuộc vào bộ lọc. Với socket, tài liệu về bộ lọc đọc thời đó mô tả một trường dữ liệu chứa số byte trong bộ đệm của socket. Tài liệu OpenBSD hiện tại mô tả các giá trị đếm số lần xuất hiện tín hiệu và số lần bộ định thời hết hạn.
Các trường đó trả lời những câu hỏi cụ thể. Số đếm tích lũy vẫn khác với các bản ghi riêng cho biết từng lần xảy ra theo đúng thứ tự. Nếu cần lịch sử đó, ứng dụng phải tính đến sự khác biệt này khi chọn cách ghi lại hoạt động của mình.
Kqueue còn kiểm tra xem thông báo có còn phù hợp hay không. Bộ lọc chạy khi điều kiện được đăng ký theo dõi lần đầu, khi hoạt động liên quan xảy ra và một lần nữa khi ứng dụng lấy thông báo. Nếu đến lúc lấy thông báo mà điều kiện kích hoạt không còn đúng, tài liệu hướng dẫn cho biết thông báo sẽ bị xóa thay vì trả về.
Quy tắc trả thông báo cũng là một phần của thiết kế
Giao diện thời đó có các tùy chọn kiểm soát thời gian tồn tại của đăng ký. EV_ONESHOT xóa đăng ký sau khi ứng dụng lấy thông báo tương ứng. EV_CLEAR đặt lại trạng thái của đăng ký sau khi lấy thông báo; tài liệu hướng dẫn mô tả tùy chọn này là hữu ích với các bộ lọc thông báo sự chuyển trạng thái.
Những lựa chọn này thay đổi cách hệ thống thông báo về các hoạt động xảy ra sau đó. Vì vậy, lập trình viên cần đọc kết hợp quy tắc của bộ lọc và các cờ cấu hình (flag) đã chọn. Chỉ biết hàng đợi chứa một sự kiện thì chưa đủ để hiểu ứng dụng sẽ nhận được gì hoặc điều gì xảy ra sau đó.
Cũng có những giới hạn thực tế. Số bản ghi mà kevent() trả về tối đa bằng sức chứa của mảng đầu ra do bên gọi cung cấp. Việc tạo hàng đợi hoặc đăng ký sự kiện có thể thất bại vì hết bộ nhớ hoặc tài nguyên descriptor. Dù dùng một giao diện chung, chương trình vẫn phải xử lý lỗi.
Giao diện chung, chi tiết riêng theo từng hệ thống
Giao diện này lan sang các hệ thống ngoài FreeBSD. Tài liệu OpenBSD ghi nhận giao diện có từ OpenBSD 2.9; tài liệu NetBSD ghi nhận giao diện xuất hiện trong NetBSD 2.0. Tài liệu của hai hệ thống vẫn giữ mô hình cốt lõi gồm đăng ký, gộp sự kiện và lấy thông báo.
Từng khả năng cụ thể có thể khác nhau. Tài liệu NetBSD hiện tại ghi rõ bộ lọc vào/ra bất đồng bộ chưa được triển khai. Vì vậy, việc tên một bộ lọc xuất hiện trong tài liệu của hệ thống này chưa đủ để kết luận hệ thống khác hỗ trợ cùng thao tác đó.
Một cách hiểu hữu ích bắt đầu từ câu hỏi của ứng dụng: trong các điều kiện đã đăng ký theo dõi, điều kiện nào cần được xử lý? Trong giao diện được Lemon mô tả, nhiều lần kích hoạt có thể gộp thành một thông báo, và điều kiện được kiểm tra lại khi lấy thông báo. Chương trình biết được điều kiện nào cần xử lý, chứ không nhận được toàn bộ lịch sử những gì đã xảy ra.
Bài liên quan
Đọc bằng ngôn ngữ khác: English
Nguồn
- Jonathan Lemon, Kqueue: A generic and scalable event notification facility — tóm tắt bài báo FREENIX năm 2001
- FreeBSD 4.1 kqueue(2) manual — ngày 14 tháng 4 năm 2000
- OpenBSD kqueue(2) manual — đăng ký, trả thông báo và lịch sử
- NetBSD kqueue(2) manual — giao diện, giới hạn và lịch sử
- Ảnh đại diện: Thomas Schanz; Wikimedia Commons; CC BY-SA 4.0 (đã chỉnh sửa từ ảnh gốc)
