Lấy thông tin từ kernel log buffer Giới thiệu Ở bài học trước, chúng ta thấy rằng, hàm printk in các thông điệp ra console. Tuy nhiên, trong quá trình

Kích thước: px
Bắt đầu hiển thị từ trang:

Download "Lấy thông tin từ kernel log buffer Giới thiệu Ở bài học trước, chúng ta thấy rằng, hàm printk in các thông điệp ra console. Tuy nhiên, trong quá trình"

Bản ghi

1 Lấy thông tin từ kernel log buffer Giới thiệu Ở bài học trước, chúng ta thấy rằng, hàm printk in các thông điệp ra console. Tuy nhiên, trong quá trình hoạt động, device driver có thể gọi hàm printk rất nhiều lần, nên sẽ có hàng nghìn thông điệp được in ra. Trong khi đó, kích thước của console thì lại hữu hạn. Điều này dẫn tới nhiều thông điệp có giá trị bị trôi mất, gây khó khăn trong quá trình phân tích nguyên nhân gây ra lỗi. Để giải quyết vấn đề trên, hàm printk cũng lưu các thông điệp vào một buffer trong kernel space. Tất cả các thông điệp đều được lưu vào buffer này, dù message log level bằng bao nhiêu. Điều này khác với cơ chế in thông điệp ra console: chỉ những thông điệp có message log level thấp hơn console log level mới được in ra console. Việc lưu tất cả các thông điệp vào trong buffer này rất có ích cho việc phân tích nguyên nhân của lỗi. Phần thứ nhất của bài học này sẽ trình bày về cơ chế lưu thông tin vào buffer nói trên. Phần thứ hai sẽ trình bày các cách thu thập thông tin trong buffer đó. Cuối cùng sẽ là các ví dụ để làm rõ hơn những gì đã trình bày trong bài học. Hàm printk sẽ lưu các thông điệp vào đâu? Hàm printk sẽ ghi các thông điệp vào một ring buffer trong kernel space. Tập hợp tất cả các thông điệp trong trong buffer này được gọi là kernel log. Để phân biệt với các buffer khác, ta gọi nó là kernel log buffer. //Tham khảo file /kernel/printk/printk.c #define LOG_BUF_LEN (1 << CONFIG_LOG_BUF_SHIFT) static char log_buf[ LOG_BUF_LEN] aligned(log_align); //kernel log buffer static char log_buf = log_buf; //địa chỉ của kernel log buffer static u32 log_buf_len = LOG_BUF_LEN; //kích thước của kernel log buffer Kích thước của kernel log buffer được thiết lập trong quá trình cấu hình kernel trước khi biên dịch. Ví dụ, nếu ta muốn kích thước của kernel log buffer là 128KB=2^1 7 bytes, thì ta thiết lập CONFIG_LOG_BUF_SHIFT=17 trong file.config. Chính vì vậy, để biết kích thước của kernel log buffer trong một hệ thống, ta gõ lệnh sau: grep CONFIG_LOG_BUF_SHIFT /boot/config-`uname -r`. Lũy thừa bậc 2 của CONFIG_LOG_BUF_SHIFT chính là kích thước của kernel log buffer. Hình 1. Các xác định kích thước của kernel log buffer trong một hệ thống Câu hỏi đặt ra là, sau khi kernel log buffer bị đầy, thì các thông điệp mới sẽ được lưu vào đâu? Để trả lời câu hỏi này, chúng ta cần đọc file /kernel/printk/printk.c để xem hàm printk hoạt động như thế nào. Ta thấy rằng, khi hàm printk được gọi, luồng thực thi sẽ diễn ra như sau: printk > > vprintk_default > > vprintk_emit >> log_store. Hàm log_store có nhiệm vụ ghi thông điệp vào trong kernel log buffer. Trước khi ghi vào, hàm log_store kiểm tra xem còn đủ chỗ trống không. Nếu không còn đủ, log_store sẽ ghi thông điệp này vào vị trí đầu của kernel log buffer, đè lên các thông điệp cũ. Cơ chế này khiến thông tin bị mất sau một khoảng thời gian. Tuy nhiên, thường thì đó là những thông tin quá cũ rồi, không còn giá trị gì để phân tích nữa. file /kernel/printk/printk.c #define PREFIX_MAX 32 #define LOG_LINE_MAX ( PREFIX_MAX) #define LOG_ALIGN 4 Xác định tổng số byte của một bản ghi, bao gồm các byte padding (byte 0). Thêm các byte padding để kích thước của bản ghi chia hết cho LOG_ALIGN. static u32 msg_used_size(u16 text_len, u16 dict_len, u32 pad_len) {

2 u32 size; size = sizeof(struct printk_log) + text_len + dict_len; pad_len = (-size) & (LOG_ALIGN - 1); size += pad_len; return size; hàm này thêm một bản ghi vào trong kernel log buffer. Nếu kernel log buffer đầy, trở lại vị trí đầu tiên để ghi. Sau khi ghi xong, cập nhật lại vị trí kế tiếp sẽ ghi vào. static int log_store(int facility, int level, enum log_flags flags, u64 ts_nsec, const char dict, u16 dict_len, const char text, u16 text_len) { struct printk_log msg; u32 size, pad_len; u16 trunc_msg_len = 0; size = msg_used_size(text_len, dict_len, &pad_len); nếu không còn đủ chỗ thì vị trí ghi kế tiếp sẽ là vị trí đầu tiên của kernel log buffer if (log_next_idx + size + sizeof(struct printk_log) > log_buf_len) { This message + an additional empty header does not fit at the end of the buffer. Add an empty header with len == 0 to signify a wrap around. memset(log_buf + log_next_idx, 0, sizeof(struct printk_log)); log_next_idx = 0; Lưu thông điệp vào kernel log buffer dưới dạng bản ghi. Bản ghi gồm 2 phần: tiêu đề (message header) và dữ liệu (message data). Bước 1: sao chép thông điệp vào phần message data msg = (struct printk_log )(log_buf + log_next_idx); memcpy(log_text(msg), text, text_len); Bước 2: Điền các thông tin mô tả vào trong message header msg->text_len = text_len; //lưu lại kích thước của phần message data if (trunc_msg_len) { memcpy(log_text(msg) + text_len, trunc_msg, trunc_msg_len); msg->text_len += trunc_msg_len; msg->facility = facility; //lưu lại nơi đã sinh ra thông điệp này msg->level = level & 7; //lưu lại độ ưu tiên của thông điệp này msg->flags = flags & 0x1f; if (ts_nsec > 0) msg->ts_nsec = ts_nsec; else msg->ts_nsec = local_clock(); //lưu lại timestamp memset(log_dict(msg) + dict_len, 0, pad_len); //Thêm các byte padding msg->len = size; //lưu lại kích thước của bản ghi cập nhật lại vị trí kế tiếp sẽ ghi vào log_next_idx += msg->len; log_next_seq++; return msg->text_len; Cũng từ quá trình tìm hiểu trên mà ta thấy rằng, các thông điệp được lưu vào kernel log buffer theo dạng bản ghi. Kích thước của mỗi bản ghi không vượt quá 1024 byte và phải chia hết cho LOG_ALIGN. Mỗi bản ghi gồm

3 2 thành phần: Thành phần tiêu đề (message header): Thành phần này chứa các thông tin mô tả thông điệp như kích thước của bản ghi, kích thước của phần message data, log level của thông điệp, timestamp của thông điệp Thành phần này được đại diện bởi cấu trúc printk_log. Cấu trúc này có kích thước không vượt quá PREFIX_MAX = 32 byte. Thành phần dữ liệu (message data): Thành phần này chính là thông điệp mà ta muốn in ra. Kích thước tối đa của phần này được quy định bởi macro LOG_LINE_MAX = PREFIX_MAX byte. Làm thế nào để lấy được kernel log? Hình 2. Cách bố trí các thông điệp trong kernel log buffer Khi xảy ra lỗi, điều đầu tiên cần làm đó là thu thập các thông điệp trong kernel log buffer. Để làm được điều này, ta có 3 cách. Đọc kernel log thông qua /proc/kmsg Hiện nay, có nhiều tiến trình trên user space hỗ trợ việc đọc kernel log thông qua /proc/kmsg. Ví dụ, hệ điều hành Ubuntu sử dụng tiến trình rsyslogd. Đối với các hệ thống khác, tiến trình syslog-ng hoặc klogd cũng có thể được sử dụng. Xét hệ điều hành Ubuntu Tiến hành kiểm tra file cấu hình của tiến trình rsyslogd, ta thấy rằng: tiến trình rsyslogd sẽ đọc kernel log thông qua /proc/kmsg rồi lưu vào file /var/log/kern.log. Vì vậy, khi device driver của bạn xảy ra lỗi, bạn chỉ cần mở file này ra và phân tích thông tin để tìm ra nguyên nhân của lỗi. Hình 3. Hệ điều hành Ubuntu sử dụng tiến trình rsyslogd để đọc kernel log thông qua /proc/kmsg Trong trường hợp mà hệ thống của bạn không có tiến trình nào lấy kernel log thông qua /proc/kmsg, bạn nên làm như sau:

4 Bước 1: Trước khi chạy device driver trên hệ thống, bạn cần chắc chắn rằng không có một tiến trình nào đang đọc file /proc/kmsg. Lý do là vì, mỗi một thông điệp chỉ có thể được đọc thông qua /proc/kmsg một lần duy nhất. Do đó, nếu một thông điệp đã được đọc bởi một một tiến trình, thì các tiến trình khác không thể có được thông điệp đó nữa. Bạn có thể đọc hàm kmsg_read trong /fs/proc/kmsg.c để rút ra điều này. Bước 2: Gõ lệnh cat /proc/kmsg > /YourDirectory/kernel.log & để tạo một tiến trình chạy ngầm thu thập kernel log. Bước 3: Cho device driver của bạn bắt đầu hoạt động. Bước 4: Khi xảy ra lỗi, bạn chỉ cần lấy file kernel.log trong thư mục YourDirectory ra để phân tích tìm nguyên nhân gây ra lỗi. Tại bước 2, chúng ta đã lưu kernel log vào trong một file trên ổ cứng. Việc làm này có hai mục đích: Thứ nhất, ngay cả khi phải kiểm tra device driver trong một thời gian dài, ta cũng không cần lo vấn đề tràn kernel log buffer. Vì file trên ổ cứng được xem như có dung lượng "vô hạn". Thứ hai, kể cả khi device driver của bạn gây ra lỗi nghiêm trọng khiến hệ thống của bạn bị treo, buộc phải khởi động lại, bạn cũng không lo bị mất thông tin, vì file được lưu trên ổ cứng. Đọc kernel log thông qua /dev/kmsg Ngoài /proc/kmsg, Linux còn cung cấp giao diện /dev/kmsg để đọc kernel log. Đây là một character device file. Để biết chi tiết hơn về /dev/kmsg, các bạn có thể tham khảo tài liệu và mã nguồn trên mạng Internet. Các bản phân phối của Linux thường cung cấp chương trình dmesg để đọc kernel log thông qua file /dev/kmsg. Đối với những hệ thống như vậy, bạn tiến hành lấy kernel log như sau: Bước 1: Gõ lệnh dmesg -C để lờ đi những thông điệp đã xuất hiện từ trước khi chạy device driver của bạn. Chú ý rằng, đứng từ góc nhìn của người dùng, thì lệnh này sẽ "xóa" kernel log buffer. Nhưng đứng từ góc nhìn của kernel, thì không có thông điệp nào bị xóa cả, chỉ là các thông điệp hiện tại sẽ bị lờ đi. Bước 2: Gõ lệnh dmesg -w > /YourDirectory/kernel.log & để tạo một tiến trình chạy ngầm thu thập kernel log. Bước 3: Cho device driver của bạn bắt đầu hoạt động. Bước 4: Khi xảy ra lỗi, bạn chỉ cần lấy file kernel.log trong thư mục YourDirectory ra để phân tích tìm nguyên nhân gây ra lỗi. Nếu hệ thống chưa cài đặt dmesg, bạn có thể làm như sau: Bước 1: Gõ lệnh echo "my device driver start here" > /dev/kmsg để đánh dấu thời điểm bắt đầu chạy device driver của bạn. Bước 2: Gõ lệnh cat /dev/kmsg > /YourDirectory/kernel.log & để tạo một tiến trình chạy ngầm thu thập kernel log. Bước 3: Cho device driver của bạn bắt đầu hoạt động. Bước 4: Khi xảy ra lỗi, bạn chỉ cần lấy file kernel.log trong thư mục YourDirectory ra để phân tích tìm nguyên nhân gây ra lỗi. Những gì bạn cần quan tâm là các thông điệp bắt đầu từ "my device driver start here". Khác với đọc kernel log thông qua /proc/kmsg, bạn không cần phải quan tâm có tiến trình nào đang đọc file /dev/kmsg hay không. Lý do là vì, một thông điệp có thể được đọc thông qua /dev/kmsg bao nhiêu lần cũng được và bởi bao nhiêu tiến trình cũng được. Bạn có thể đọc hàm devkmsg_read trong file /kernel/printk/printk.c để rút ra điều này. Sử dụng system call syslog để đọc kernel log Nếu không thích sử dụng các công cụ có sẵn ở trên, ta có thể viết một chương trình sử dụng system call syslog. System call này cho phép ta làm việc với kernel log buffer. Do system call syslog có tên trùng với một library call trong thư viện C, nên thư viện C đã tạo ra một hàm có tên là klogctl bao bọc system call này. Vì vậy, khi viết chương trình lấy kernel log, ta nên sử dụng hàm klogctl để tránh nhầm lẫn. Dưới đây là cách sử dụng hàm klogctl để làm việc với kernel log buffer. #include <sys/klog.h> Hàm này dùng để làm việc với kernel log buffer, và làm việc với console log level. Tham số đầu vào: type: kiểu thao tác làm việc với kernel log buffer. Các thao tác này bao gồm:

5 (0) SYSLOG_ACTION_CLOSE: đóng kernel log buffer (hiện tại chưa triển khai) (1) SYSLOG_ACTION_OPEN: mở kernel log buffer (hiện tại chưa triển khai) (2) SYSLOG_ACTION_READ: đọc kernel log. Kiểu thao tác này sẽ đợi cho tới khi xuất hiện thông điệp mới trong kernel log buffer. Sau đó, thông điệp này sẽ được sao chép vào [bufp] (không quá [len] byte). Chú ý rằng, mỗi một thông điệp chỉ có thể được đọc theo kiểu này 1 lần. Điều này cũng giống như một tiến trình đọc kernel log thông qua /proc/kmsg. Ví dụ: giả sử kernel log buffer đang có hai thông điệp A và B. Nếu tiến trình 1 đọc kernel log theo kiểu này, hai thông điệp sẽ được đọc vào [bufp]. Sau đó tiến trình 1 sẽ đợi cho tới khi có thông điệp mới. Nếu sau đó, tiến trình 2 cũng đọc kernel log theo kiểu này, thì nó sẽ không đọc được hai thông điệp A và B nữa. (3) SYSLOG_ACTION_READ_ALL: đọc các thông điệp (kể từ lần gần nhất thực hiện thao tác SYSLOG_ACTION_CLEAR) trong kernel log buffer vào trong buffer có địa chỉ [bufp]. Nếu tổng kích thước của các thông điệp vượt quá [len] byte, thì chỉ đọc[len] byte cuối cùng. Khác với SYSLOG_ACTION_READ, về sau, các thông điệp này vẫn có thể được đọc bởi các tiến trình khác. Ví dụ, trong kernel log buffer đang có hai thông điệp A và B. Bây giờ, tiến trình gửi lệnh SYSLOG_ACTION_CLEAR xuống. Sau đó, xuất hiện thêm ba thông điệp C, D, E. Nếu lúc này, tiến trình 1 đọc kernel log theo kiểu SYSLOG_ACTION_READ_ALL, thì chỉ có ba thông điệp C, D, E được đọc vào user buffer (tại địa chỉ [bufp]), miễn là tổng kích thước ba thông điệp không vượt quá [len] byte. Nếu sau đó, tiến trình 2 cũng đọc kernel log theo kiểu này, thì nó vẫn sẽ đọc được ba thông điệp C, D và E. (4) SYSLOG_ACTION_READ_CLEAR: kết hợp giữa thao tác SYSLOG_ACTION_READ_ALL và SYSLOG_ACTION_CLEAR. (5) SYSLOG_ACTION_CLEAR: thực hiện "xóa" các thông điệp trong kernel log buffer. Đối với thao tác này, ta không cần quan tâm [bufp] và [len]. Chú ý, thực tế, không có thông điệp nào bị xóa cả. Thao tác này chỉ thay đổi các biến quản lý kernel log buffer, giúp thay đổi kết quả trả về của thao tác SYSLOG_ACTION_READ_ALL và SYSLOG_ACTION_READ_CLEAR. Điều này khiến cho người dùng cảm thấy: tất cả các thông điệp trước khi thực hiện thao tác này đã bị "xóa" mất. Các thao tác SYSLOG_ACTION_READ và SYSLOG_ACTION_SIZE_UNREAD không bị ảnh hưởng bởi thao tác này. (6) SYSLOG_ACTION_CONSOLE_OFF: lưu lại console log level hiện tại rồi thiết lập console log level bằng mimimum console log level. Mục đích của việc này là để không có thông điệp nào được in ra console. Đối với thao tác này, ta không cần quan tâm [bufp] và [len]. (7) SYSLOG_ACTION_CONSOLE_ON: nếu đã thực hiện SYSLOG_ACTION_CONSOLE_OFF trước đó, thì thao tác này giúp khôi phục console log level về như giá trị ban đầu. Đối với thao tác này, ta không cần quan tâm [bufp] và [len]. (8) SYSLOG_ACTION_CONSOLE_LEVEL: thiết lập console log level = [len]. Chú ý, [len] phải có giá trị nguyên trong khoảng từ 1 đến 8. Nếu tham số [len] nhỏ hơn giá trị minimum console log level thì console log level chỉ được thiết lập bằng minimum console log level. Đối với thao tác này, ta không cần quan tâm [bufp]. (9) SYSLOG_ACTION_SIZE_UNREAD: trả về số byte có thể đọc được từ kernel log buffer trong trường hợp sử dụng SYSLOG_ACTION_READ. Đối với thao tác này, ta không cần quan tâm [bufp] và [len]. (10) SYSLOG_ACTION_SIZE_BUFFER: trả về kích thước của kernel log buffer. Đối với thao tác này, ta không cần quan tâm [bufp] và [len]. bufp: địa chỉ của buffer trên user space dùng để chứa kernel log. len : kích thước của buffer trên user space. Giá trị trả về: Đối với các thao tác (2), (3), (4), hàm này trả về số byte đọc được từ kernel log buffer. Đối với thao tác (9), hàm này trả về số byte có thể đọc được nếu sử dụng thao tác (2). Đối với thao tác (10), hàm này trả về kích thước của kernel log buffer. Đối với các thao tác còn lại, hàm này trả về 0 nếu thực hiện thành công. Nếu xảy ra lỗi, hàm này sẽ trả về -1 và biến errno sẽ chứa mã lỗi.

6 int klogctl(int type, char bufp, int len); Trên thực tế, klogd và dmesg cũng có thể đọc kernel log bằng phương pháp này: Nếu ta biên dịch klogd với cờ ENABLE_FEATURE_KLOGD_KLOGCTL được bật, thì klogd sẽ lấy kernel log bằng phương pháp này thay vì đọc thông qua /proc/kmsg. Nếu ta gõ lệnh dmesg -S, thì tiến trình dmesg sẽ lấy kernel log bằng phương pháp này thay vì đọc thông qua /dev/kmsg. Case study Phần này hướng dẫn các bạn thực hành lấy kernel log theo các cách khác nhau. Để phục vụ quá trình thực hành, vchar driver trong bài học trước sẽ được sử dụng. Đọc kernel log thông qua /proc/kmsg Như đã trình bày ở trên, trong hệ điều hành Ubuntu 16.04, tiến trình rsyslogd đọc kernel log thông qua /proc/kmsg rồi lưu vào file /var/log/kern.log. Để minh chứng cho điều này, chúng ta cùng thực hiện những bước sau: Bước 1: Nhấn tổ hợp phím Ctrl + Alt + T để mở một terminal, sau đó nhập vào lệnh tailf -n 0 /var/log/kern.log. Bước 2: Nhấn tổ hợp phím Ctrl + Alt + T để mở một terminal khác, sau đó di chuyển tới thư mục phan_3/bai_3_1/ rồi nhập vào lệnh sudo insmod vchar_driver.ko để lắp vchar driver vào trong kernel. Quan sát trên terminal thứ nhất, ta thấy có các thông điệp của vchar driver được in ra (hình 4). Hình 4. Tiến trình rsyslogd lấy kernel log thông qua /proc/kmsg rồi lưu vào /var/log/kern.log Nếu không thích tiến trình rsyslogd, bạn có thể dùng lệnh cat /proc/kmsg để lấy kernel log. Các bước làm như sau: Bước 1: Gõ lệnh sudo service rsyslog stop để hủy tiến trình rsyslog. Nếu không hủy tiến trình này, bạn không thể đọc được kernel log. Bởi vì một thông điệp chỉ có thể được đọc qua /proc/kmsg một lần duy nhất và bởi một tiến trình duy nhất. Bước 2: Gõ lệnh sudo cat /proc/kmsg > kernel.txt &. Sau khi nhập lệnh này, tiến trình cat sẽ chạy ngầm để đọc kernel log thông qua /proc/kmsg. Kernel log sẽ được lưu vào file kernel.txt trong thư mục hiện tại. Bước 3: Gõ lệnh sudo insmod vchar_driver.ko để lắp vchar driver vào kernel. Bước 4: Gõ lệnh cat kernel.txt thì sẽ thu được kết quả tương tự như hình 5. Điều này chứng tỏ rằng, ta đã lấy kernel log thành công thông qua /proc/kmsg.

7 Đọc kernel log thông qua /dev/kmsg Hình 5. Sử dụng lệnh cat /proc/kmsg để lấy kernel log Hệ điều hành Ubuntu có công cụ dmesg. Mặc định, dmesg sẽ lấy kernel log thông qua /dev/kmsg. Để minh họa cho việc lấy kernel log bằng dmesg, ta thực hiện các bước sau: Bước 1: Gõ lệnh dmesg -C để lờ đi những thông điệp hiện tại đang có trong kernel log buffer. Bước 2: Gõ lệnh dmesg -w > kernel.log &. Sau khi nhập lệnh này, tiến trình dmesg sẽ chạy ngầm để đọc kernel log thông qua /dev/kmsg. Kernel log sẽ được lưu vào file kernel.log trong thư mục hiện tại. Bước 3: Gõ lệnh sudo insmod vchar_driver.ko để lắp vchar driver vào kernel. Bước 4: Gõ lệnh cat kernel.log thì sẽ thu được kết quả tương tự như hình 6. Điều này chứng tỏ rằng, ta đã lấy kernel log thành công thông qua /dev/kmsg. Hình 6. Sử dụng dmesg để lấy kernel log thông qua /dev/kmsg Nếu không thích sử dụng dmesg để lấy kernel log thông qua /dev/kmsg, ta có thể làm như sau: Bước 1: Gõ lệnh echo "vchar driver start here" > /dev/kmsg. Thông điệp này sẽ được ghi vào kernel log buffer. Mục đích của việc này là để đánh dấu thời điểm bắt đầu chạy device driver của bạn. Bước 2: Gõ lệnh cat /dev/kmsg > kernel.log &. Sau khi nhập lệnh này, tiến trình cat sẽ chạy ngầm để đọc kernel log thông qua /dev/kmsg. Kernel log sẽ được lưu vào file kernel.log trong thư mục hiện tại. Bước 3: Gõ lệnh sudo insmod vchar_driver.ko để lắp vchar driver vào kernel.

8 Hình 7. Sử dụng cat để lấy kernel log thông qua /dev/kmsg Bây giờ, ta gõ lệnh cat kernel.log để xem những gì đã lưu vào trong file kernel.log. Ta có thể thấy rằng, toàn bộ kernel log được lấy ra. Hầu hết các dòng đều có định dạng: <message log level>,<sequence number>, <timestamp>,<flag>;<nội dung>. Các thông điệp của vchar driver được in ra kể từ thông điệp "vchar driver start here". Thông điệp này có message log level bằng 12, thể hiện đây là thông điệp đến từ user space. Đọc kernel log bằng system call Hình 8. Kernel log thu thập được khi sử dụng lệnh cat /dev/kmsg Trong phần này, chúng ta sẽ cùng tìm hiểu cách mà chương trình klogd lấy kernel log bằng system call. Dựa trên mã nguồn của klogd, bạn có thể viết chương trình của riêng bạn để lấy kernel log bằng phương pháp system call. Hiện nay, nhiều hệ thống nhúng vẫn sử dụng Busybox để cung cấp các công cụ cơ bản (basic utility), hai trong số đó là klogd và syslogd. klogd là một chương trình trên user space. Nhiệm vụ của nó là lấy kernel log, sau đó gửi cho tiến trình syslogd thông qua một UNIX socket có tên là /dev/log. Thông qua UNIX socket /dev/log, tiến trình syslogd sẽ nhận được kernel log cùng với log của các tiến trình khác, rồi ghi vào một file trên ổ cứng. Thông thường, file đó là /var/log/message.

9 Hình 9. Sơ đồ của quá trình thu thập log trong hệ thống sử dụng Busybox Trong quá trình biên dịch Busybox, ta có thể thiết lập cấu hình ENABLE_FEATURE_KLOGD_KLOGCTL. Nếu cấu hình này không được bật, thì klogd sẽ đọc kernel log thông qua giao diện /proc/kmsg. Còn nếu được bật, thì klogd sẽ đọc kernel log bằng phương pháp system call syslog. Mã nguồn triển khai cho cả hai cách này như sau: #include "libbb.h" #include <syslog.h> The Linux-specific klogctl(3) interface does not rely on the filesystem and allows us to change the console loglevel. Alternatively, we read the messages from _PATH_KLOG. #if ENABLE_FEATURE_KLOGD_KLOGCTL # include <sys/klog.h> static int klogd_read(char bufp, int len) { return klogctl(2, bufp, len); #else # include <paths.h> enum { klogfd = 3 ; static void klogd_open(void) { _PATH_KLOG được định nghĩa trong thư viện <paths.h> int fd = xopen(_path_klog, O_RDONLY); xmove_fd(fd, klogfd); static int klogd_read(char bufp, int len) { return read(klogfd, bufp, len); #endif

10 Kết luận Hàm printk sẽ ghi các thông điệp vào một ring buffer trong kernel space. Tập các thông điệp trong ring buffer này được gọi là kernel log. Do đó, buffer này được gọi là kernel log buffer. Kích thước của kernel log buffer phụ thuộc vào tham số CONFIG_LOG_BUF_SHIFT. Ta có thể điều chỉnh được tham số này lúc biên dịch kernel. Khi xảy ra lỗi, ta cần phải lấy kernel log. Có ba cách cho phép lấy kernel log từ user space. Cách thứ nhất là đọc kernel log thông qua /proc/kmsg. Cách thứ hai là đọc kernel log thông qua /dev/kmsg. Cách thứ ba là đọc kernel log thông qua system call syslog. Các hệ thống hiện tại đã có nhiều công cụ trên user space để lấy kernel log, ví dụ như klogd, rsyslog, syslogng, dmesg. Các công cụ này lấy kernel log theo một trong ba cách nói trên.