Tự học linux kernel
Giới thiệuLinux được thiết kế để làm việc với hàng tỉ thiết bị. Nhưng ta không thể đưa tất cả các driver vào trong kernel được, vì sẽ làm cho kích thước kernel rất lớn. Giải pháp cho vấn đề này đó là: thiết kế các driver dưới dạng module tách rời với kernel. Trong quá trình hoạt động, driver nào cần thiết sẽ được lắp vào kernel, còn driver nào không cần thiết sẽ bị tháo ra khỏi kernel (dynamic loading). Như vậy, để trở thành lập trình viên Linux device driver, ta cần hiểu về Linux kernel module. Bài học này sẽ tập trung làm rõ các vấn đề liên quan tới Linux kernel module:
Linux kernel moduleLinux kernel module là một file với tên mở rộng là (.ko). Nó sẽ được lắp vào hoặc tháo ra khỏi kernel khi cần thiết. Chính vì vậy, nó còn có một tên gọi khác là loadable kernel module. Một trong những kiểu loadable kernel module phổ biến đó là driver. Đến đây, các bạn có thể có một số thắc mắc:
Chúng ta sẽ lần lượt giải đáp các thắc mắc trên. Việc thiết kế driver theo kiểu loadable module mang lại 3 lợi ích:
Phần lớn các driver đều là các loadable kernel module, nhưng không phải là tất cả. Vẫn có một số driver được tích hợp luôn vào trong kernel, đặc biệt là các bus driver. Chúng được gọi là built-in driver. Các device driver thường sẽ là các loadable kernel module. Ngược lại, không phải loadable kernel module nào cũng là driver, ví dụ kvm.ko là loadable kernel module nhưng không phải là driver. Trên thực tế, loadable kernel module được chia làm 3 loại chính: device driver, system call và file system. Trong khóa học này, các thuật ngữ module, device driver, loadable kernel module, Linux kernel module, loadable module, kernel module đều được hiểu là một. Khi cần một module nhưng nó lại chưa có trong kernel space, kernel sẽ đưa module ấy vào. Quá trình này có thể diễn ra một cách tự động, với trình tự sau:
Như vậy, các module được đưa vào kernel space dưới sự giúp đỡ của tiến trình modprobe. Câu hỏi đặt ra là: kernel kích hoạt tiến trình modprobe bằng cách nào?
Hình 1 Minh họa quá trình kích hoạt modprobe bằng udevd case studyBây giờ, chúng ta cùng tìm hiểu cách viết một Linux kernel module. Bước 1: tạo thư mục cho bài học hôm nay
Bước 2: dùng lệnh "cd" di chuyển vào thư mục phan_1/bai_1_3, rồi dùng lệnh “vim hello.c” để mở một file có tên hello.c. Sau đó, sao chép đoạn mã sau vào file hello.c
Do module hello cần dùng một số hàm hoặc macro của Linux kernel, nên chúng ta sẽ sử dụng từ khóa #include để chỉ rõ các file cần dùng. Điều này cũng tương tự như khi viết các chương trình ứng dụng trên user space, chúng ta cũng dùng #include cùng với tên của các thư viện. Do đó, có thể nói rằng, Linux kernel chính là một thư viện, cung cấp các hàm, các macro để chúng ta phát triển kernel module. Trong ví dụ trên, ta chỉ cần tham chiếu tới file của Linux kernel là
Trong ví dụ trên, init_hello() là hàm được gọi ngay sau khi module hello được lắp vào, và exit_hello() là hàm được gọi ngay trước khi module hello bị tháo ra khỏi kernel. Macro __init thường đi kèm với hàm khởi tạo. Trong ví dụ trên, macro __init xuất hiện trước tên hàm init_hello. Macro này giúp kernel biết rằng, hàm init_hello() chỉ phải thực thi lúc khởi tạo, nên vùng nhớ chứa hàm này có thể được giải phóng sau khi nó thực thi xong mà không ảnh hưởng gì. Tương tự, macro __exit thường đi kèm với hàm kết thúc. Trong ví dụ trên, __exit xuất hiện trước tên hàm exit_hello. Macro này cho kernel biết, khi lắp module vào kernel thì chưa cần đưa hàm exit_hello vào trong bộ nhớ RAM. Chỉ khi chuẩn bị tháo module ra khỏi kernel, hàm exit_hello này mới cần được đưa vào RAM và thực thi. Trong quá trình viết kernel module, các lập trình viên thường sử dụng hàm printk để ghi lai quá trình hoạt động của module. Việc này được gọi là logging. Mục đích của việc logging là để phục vụ quá trình gỡ lỗi sau này (debug). Ta có thể sử dụng lệnh dmesg để xem quá trình hoạt động của kernel kể từ lúc nó khởi động. Chúng ta sẽ tìm hiểu kỹ hơn về hàm printk trong bài 3_1. Các macro nằm ở cuối ví dụ trên cung cấp các thông tin về module. Ta có thể sử dụng lệnh modinfo để xem các thông tin của một module:
Biên dịch kernel moduleĐể biên dịch kernel module, ta sử dụng phương pháp Kbuild. Theo phương pháp này, chúng ta cần tạo ra 2 file: một file có tên là Makefile, file còn lại có tên là Kbuild. Đầu tiên, ta sẽ tạo ra Makefile.
Trong Makefile trên:
Tiếp theo, ta tạo ra file Kbuild nằm trong cùng thư mục với Makefile:
Trong file Kbuild trên:
Để tạo ra kernel module, ta gõ lệnh make hoặc make all (hình 2). Khi ta gõ lệnh "make", tiến trình make sẽ dựa vào Makefile và Kbuild để biên dịch mã nguồn, tạo ra kernel module. Hình 2. sử dụng công cụ make để biên dịch kernel module Sau khi biên dịch xong, ta sẽ thấy xuất hiện môt file có tên mở rộng là .ko (ko là viết tắt của kernel object). Đây chính là kernel module. Để biết được các thông tin về module, ta sử dụng lệnh modinfo (hình 3). Hình 3. sử dụng công cụ modinfo để biết thông tin về module Lắp/tháo kernel moduleĐể lắp module vào trong kernel, ta có thể thực hiện thủ công bằng cách gõ lệnh insmod. Sau khi lắp xong, ta sẽ dùng lệnh lsmod để kiểm tra xem module đã được load thành công chưa. Tiếp theo, ta sẽ dùng lệnh dmesg để theo dõi quá trình hoạt động của module (hình 4). Cuối cùng, chúng ta sẽ dùng lệnh rmmod để tháo module ra khỏi kernel. Hình 4. Đưa module vào/ra khỏi kernel Kết luậnDriver có thể được tích hợp luôn vào trong kernel hoặc được thiết kế dưới dạng module tách rời. Driver cho các thiết bị cố định, ví dụ các thiết bị trong smartphone, sẽ được tích hợp luôn vào trong kernel. Driver cho các thiết bị hay phải thay đổi, sẽ được thiết kế dưới dạng loadable module. Thông thường, các bus driver sẽ được tích hợp vào trong kernel, còn các device driver được thiết kế dưới dạng loadable module. Ngoài device driver, thì system call và file system cũng được thiết kế theo kiểu loadable module. Kernel module có thể được đưa vào trong kernel một cách tự động, hoặc thủ công.
Linux kernel cũng giống như một thư viện giúp lập trình viên xây dựng các kernel module. Khi viết một kernel module, ta cần phải tham chiếu tới các file trong thư mục include/linux. Bất cứ một module nào cũng cần tham chiếu tới file |