Sử dụng printk Giới thiệu Ngoài biết cách viết device driver, lập trình viên cũng cần biết cách khắc phục khi xảy ra lỗi Để khắc phục lỗi, ta cần biết nguyên nhân Để biết được nguyên nhân, ta cần thu[.]
Sử dụng printk Giới thiệu Ngoài biết cách viết device driver, lập trình viên cần biết cách khắc phục xảy lỗi Để khắc phục lỗi, ta cần biết nguyên nhân Để biết nguyên nhân, ta cần thu thập thơng tin liên quan Đó thơng tin q trình hoạt động device driver, thông tin device driver thời điểm xảy lỗi, thơng tin q trình tương tác tiến trình với device driver Chương trình bày ba phương pháp giúp ta lấy loại thông tin trên: Phương pháp mornitoring cho phép lưu lại thơng tin q trình hoạt động device driver Phương pháp triển khai thông qua hàm printk Phương pháp querying cho phép trích xuất thơng tin device driver thời điểm xảy lỗi Phương pháp triển khai ba kỹ thuật: procfs, ioctl sysfs Phương pháp watching cho phép quan sát tiến trình tương tác với device driver Phương pháp triển khai kỹ thuật strace Bài học trình bày cách sử dụng hàm printk Cách thu thập thông tin trình hoạt động device driver trình bày Sử dụng hàm printk nào? Khi cần lưu lại thông tin, ta thường sử dụng hàmprintk theo mẫu sau: #include /* * message log level gọi log level thông điệp * message log level thể độ ưu tiên thông điệp * Giá trị message log level thấp độ ưu tiên cao * Các giá trị message log level định nghĩa file * include/linux/kern_levels.h * #define KERN_SOH "\001" // ASCII Start Of Header * #define KERN_EMERG KERN_SOH "0" // system is unusable * #define KERN_ALERT KERN_SOH "1" // action must be taken immediately * #define KERN_CRIT KERN_SOH "2" // critical conditions * #define KERN_ERR KERN_SOH "3" // error conditions * #define KERN_WARNING KERN_SOH "4" // warning conditions * #define KERN_NOTICE KERN_SOH "5" // normal but significant condition * #define KERN_INFO KERN_SOH "6" // informational * #define KERN_DEBUG KERN_SOH "7" // debug-level messages */ printk([message log level] "thông điệp", …); Khi sử dụng hàm printk, lập trình viên cần lưu ý điểm sau: Thứ nhất, kích thước tối đa thơng điệp (tính theo byte) quy định macroLOG_LINE_MAX Trong Linux kernel 4.4, LOG_LINE_MAX = 992 byte /* File /kernel/printk/printk.c */ #define PREFIX_MAX 32 #define LOG_LINE_MAX (1024 - PREFIX_MAX) //kích thước tối đa thơng điệp Thứ hai, hàm printk không hỗ trợ định dạng số dấu phẩy động Ví dụ, cách sử dụng gây lỗi trình device driver hoạt động: float msg = 6.9; printk(KERN_INFO "message = %f", msg); // không phép Thứ ba, ta không thiết phải rõ message log level hàmprintk Nếu không rõ, log level thông điệp gán MESSAGE_LOGLEVEL_DEFAULT Để biết giá trị message log level mặc định hệ thống, ta có cách: Gõ lệnh grep CONFIG_MESSAGE_LOGLEVEL_DEFAULT /boot/config-`uname -r` Gõ lệnh cat /proc/sys/kernel/printk Số thứ hai dãy số biểu thị giá trị message log level mặc định Hình Hai cách xác định giá trị mặc định message log level Đến đây, bạn thắc mắc rằng: gắn message log level cho thơng điệp để làm gì? Để trả lời câu hỏi này, đọc file /kernel/printk/printk.c để xem hàm printk hoạt động Ta thấy rằng, hàm printk gọi, luồng thực thi diễn sau:printk > > vprintk_default >> vprintk_emit > > console_unlock > > call_console_drivers Hàm call_console_drivers thực in thông điệp console Nếu in nhiều thông điệp console, hệ thống chạy chậm Vì vậy, lập trình viên mong muốn rằng: chạy thật device driver có thơng điệp in console, chạy thử nghiệm device driver có nhiều thơng điệp in console Để đạt mong muốn trên, hàm call_console_drivers so sánh log level thông điệp với giá trị Chỉ thơng điệp có message log level nhỏ giá trị in console Giá trị gọi console log level /* File /kernel/printk/printk.c */ static void call_console_drivers(int level, const char *ext_text, size_t ext_len, const char *text, size_t len) { /* * level log level thông điệp * console_loglevel log level console * message log level >= console log level, khơng in console */ if (level >= console_loglevel && !ignore_loglevel) return; /* ghi thông điệp console */ } Hệ điều hành Ubuntu có console với giao diện dòng lệnh (Command Line Interface) Để làm việc với console thứ x, ta nhấn tổ hợp phím Ctrl + Alt + Fx Hệ điều hành gán giá trị gọi console log level cho console Để biết console log level, ta nhập lệnh: cat /proc/sys/kernel/printk Sau nhập xong, ta thu kết có dạng đây: L1 L2 L3 L4 Trong đó, Giá trị L1 console log level Ta sử dụng hai cách sau để thay đổi L1 hệ thống hoạt động mà không cần biên dịch lại kernel (hình 2): Gõ lệnh: echo L > /proc/sys/kernel/printk Gõ lệnh: dmesg -n L (chú ý, L >= L3) Giá trị L2 default message log level Nếu không rõ message log level thông điệp lời gọi hàm printk, thơng điệp có message log level L2 Ta thay đổi L2 cách sửa giá trị MESSAGE_LOGLEVEL_DEFAUL file config biên dịch lại kernel Giá trị L3 minimum console log level Đây giá trị tối thiểu mà ta phép thiết lập cho console log level Ta thay đổi L3 cách sửa lại giá trị CONSOLE_LOGLEVEL_MIN file /include/linux/printk.h biên dịch lại kernel Giá trị L4 default console log level Đây giá trị console log level mặc định lúc khởi động hệ thống Ta thay đổi L4 cách sửa lại giá trị CONSOLE_LOGLEVEL_DEFAULT file /include/linux/printk.h biên dịch lại kernel Hình Hai cách giúp thay đổi console log level Từ suy rằng, muốn thông điệp chắn in console, ta có cách: Cách thứ gắn message log level = KERN_EMERG cho thơng điệp Cách thứ hai thiết lập console log level = Thứ tư, viết device driver, ta thường sử dụng biến thể củaprintk thay sử dụng trực tiếp: Dạng dev_xxxx Dạng pr_xxxx (Tham khảo linux/device.h) (Tham khảo linux/printk.h) KERN_EMERG dev_emerg pr_emerg KERN_ALERT dev_alert pr_alert KERN_CRIT dev_crit pr_crit KERN_ERR dev_err pr_err KERN_WARNING dev_warn Log level thông điệp pr_warn pr_warning KERN_NOTICE dev_notice pr_notice KERN_INFO dev_info pr_info KERN_DEBUG dev_dbg pr_debug pr_devel Case study Ví dụ giúp bạn hiểu mối quan hệ message log level console log level Trước hết, ta tạo thư mục cho học ngày hôm nay: cd /home/ubuntu/ldd mkdir phan_3 mkdir phan_3/bai_3_0 cd phan_3/bai_3_0 Tiếp theo, ta nhập lệnh vim demologlevel.c để mở file có tên demologlevel.c Sau đó, chép đoạn mã vào file này: /* * phan_3/bai_3_0/demologlevel.c - vi du ve cac message log level */ #include /* thu vien dinh nghia cac macro nhu module_init va module_exit */ #include /* thu vien cung cap cac message log level */ #define DRIVER_AUTHOR "Nguyen Tien Dat " #define DRIVER_DESC "A demonstration about message log levels" static int init init_demo_loglevel(void) { printk("default level\n"); printk(KERN_EMERG "emergency level\n"); printk(KERN_ALERT "alert level\n"); printk(KERN_CRIT "critical level\n"); printk(KERN_ERR "error level\n"); printk(KERN_WARNING "warning level\n"); printk(KERN_NOTICE "notice level\n"); printk(KERN_INFO "information level\n"); printk(KERN_DEBUG "debug level\n"); return 0; } static void exit exit_demo_loglevel(void) { printk(KERN_INFO "end demo log level\n"); } module_init(init_demo_loglevel); module_exit(exit_demo_loglevel); MODULE_LICENSE("GPL"); /* giay phep su dung cua module */ MODULE_AUTHOR(DRIVER_AUTHOR); /* tac gia cua module */ MODULE_DESCRIPTION(DRIVER_DESC); /* mo ta chuc nang cua module */ MODULE_SUPPORTED_DEVICE("testdevice"); /* kieu device ma module ho tro */ Sau đó, thư mục /phan_3/bai_3_0, ta tạo Makefile để tiện cho việc biên dịch sau Sau tạo xong, ta gõ lệnh make để biên dịch demologlevel.ko #cd /home/ubuntu/ldd/phan_3/bai_3_0 #vim Makefile obj-m += demologlevel.o all: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules clean: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean Bây giờ, ta nhấn tổ hợp phím Ctrl + Alt + F5 để làm việc với console thứ năm Sau vào console thứ năm, ta gõ lệnh insmod demologlevel.ko để cài đặt module vào kernel (hình 3) Kết quả, console xuất thơng điệp có log level từ (emergency) đến (error) Lý console log level Hình Các thơng điệp có log level nhỏ in console Nếu muốn tất thông điệp in ra, ta gõ lệnhdmesg -n để thay đổi giá trị console log level (hình 4) Hình Tất thông điệp in console console log level Case study Tuy khơng có quy tắc cụ thể quy định việc chọn log level cho thông điệp, ta thường ngầm định rằng: thơng điệp hội xuất (hoặc khơng mong muốn xuất hiện) gán log level thấp (tức độ ưu tiên cao) Lý vì, thơng điệp xuất có giá trị đáng ý Những thông điệp thông báo có cố xảy ta gán log level làKERN_ERR, KERN_CRIT, KERN_ALERT, KERN_EMERG Sự cố nghiêm trọng gán log level thấp Lý cố xảy Những thông điệp thông báo kết thơng thường ta gán log level KERN_INFO KERN_DEBUG Cịn thơng điệp chứa thơng tin mang tính chất cảnh báo ta gán log level KERN_WARNING KERN_NOTICE Cho tới học ioctl chương trước, chưa gắn log level cho thông điệp sử dụng hàm printk Để minh họa "quy tắc ngầm định" nói trên, thực ví dụ Trước hết, ta tạo thư mục cho ví dụ cd /home/ubuntu/ldd/phan_3 mv phan_2/bai_2_8 bai_3_1 cd bai_3_1 Bây giờ, ta thực sửa lại file vchar_driver.c giống Kết luận Để lưu lại thơng tin q trình hoạt động device driver, ta sử dụng hàmprintk theo mẫu sau: printk([message log level] “thông điệp”,…); Giá trị message log level thấp độ ưu tiên thơng điệp cao Nếu message log level nhỏ console log level, thơng điệp in console Trong trường hợp ta không rõ message log level hàm printk, message log level MESSAGE_LOGLEVEL_DEFAULT Bài học giới thiệu chế hoạt động hàm printk cách lấy thông tin trình hoạt động device driver ... lời câu hỏi này, đọc file /kernel /printk/ printk.c để xem hàm printk hoạt động Ta thấy rằng, hàm printk gọi, luồng thực thi diễn sau :printk > > vprintk_default >> vprintk_emit > > console_unlock... tư, viết device driver, ta thường sử dụng biến thể củaprintk thay sử dụng trực tiếp: Dạng dev_xxxx Dạng pr_xxxx (Tham khảo linux/device.h) (Tham khảo linux /printk. h) KERN_EMERG dev_emerg pr_emerg... init_demo_loglevel(void) { printk( "default level "); printk( KERN_EMERG "emergency level "); printk( KERN_ALERT "alert level "); printk( KERN_CRIT "critical level "); printk( KERN_ERR "error level "); printk( KERN_WARNING