+1

Những điều cơ bản về Linux Driver (Phần 1)

Driver là 1 thành phần phần mềm chịu trách nhiệm điều khiển và quản lí 1 thiết bị phần cứng cụ thể trong hệ thống, vì vậy nó nó được gọi là device driver. Từ góc nhìn của hệ điều hành, driver có thể thuộc tầng không gian nhân(kernel space) hoặc tầng không gian tiến trình (user space). Kernel space có nhiều đặc quyền về khả năng truy cập vào phần cứng hơn tầng user space và thông thường sẽ có khả năng truy cập trực tiếp (full access) vào phần cứng. Chúng ta sẽ chỉ tập trung vào driver trong kernel space trong series này

Bài viết này sẽ có các nội dung sau:

  • Quá trình biên dịch 1 linux driver
  • Load và unload 1 driver

Để nội dung bài viết được ngắn gọn, hai nội dung dưới sẽ được trình bày trong phần 2 của bài viết

  • Khung sườn của 1 driver
  • Gỡ lỗi và quản lí các message gỡ lỗi

User space and kernel space

Trước tiên chúng ta sẽ đi làm rõ về kernel space và user space, khái niệm của chúng mang hơi tính trừu tượng. Về mặt bản chất, chúng sẽ khác nhau trên 2 phương diện: không gian bộ nhớ và quyền truy cập tài nguyên. Chúng ta có thể xem rằng, kernel có các đặc quyền đầy đủ, trong khi các ứng dụng trên tầng process lại bị giới hạn quyền truy cập. Nó liên quan chặt chẽ đến 1 đặc tính của các CPU hiện đại, cho phép CPU có thể chạy ở chế độ đặc quyền hoặc chế độ bị giới hạn đặc quyền. Khái niệm này chúng ta sẽ đi làm rõ hơn ở những bài viết liên quan đến Kernel Memory Management.

User space và kernel space

Như các bạn thấy ở hình trên user space và kernel space là hai phần tách biệt, cầu nối giữa chúng chính là những system calls như: read, write, ioclt, fork, exec, exit, kill,.. chúng ta sẽ đi sâu hơn về những system call này ở những bài viết tiếp theo. Có thể mô tả từng không gian như sau:

Kernel space: Là 1 không gian địa chỉ, nơi nó được lưu trữ và vận hành. Không gian kernel (kernel space) là 1 dải bộ nhớ thuộc quyền sở hữu của kernel, được bảo vệ bằng các cơ chế kiểm soát truy cập. Nhờ vậy, các ứng dụng trên tầng process không thể vô tình, cố ý hay can thiệp vào vùng nhớ của kernel. Song song với đó, kernel lại có khả năng truy cập toàn bộ không gian địa chỉ, vì nó được chạy với quyền ưu tiên cao hơn trong hệ thống. Ở kernel mode, CPU có thể truy cập vào toàn bộ vùng nhớ của kernel và user.

User space: Là không gian địa chỉ nơi chạy những chương trình ứng dụng bình thường ví dụ như: grep, vim,... bị hạn chế quyền trong quá trình chạy. Bạn có thể xem nó như là 1 dạng sanbox hoặc "nhà tù", đảm bảo rằng nó không thể nhìn thấy hay can thiệp vào bất kì tài nguyên nào thuộc về chương trình khác. CPU khi chạy ở user mode chỉ có thể truy cập được các vùng nhớ được đánh nhãn là user-space access rights. Chương trình user sẽ chạy với độ ưu tiên thấp hơn so với kernel. Khi 1 process gọi system call nó sẽ raise ra 1 ngắt mềm được gửi xuống kernel, điều đó làm cho process chuyển sang chế độ đặc quyền và chạy được dưới kenrel space. Khi systemcall return, kernel sẽ tắt chế độ đặc quyền đi và process lại bị "đóng tù" lại.

Về kernel module

Trong linux kernel module có thể plug and play(tải động) vào trong kernel lúc đang chạy mà không yêu cầu reboot, sau khi tải nó sẽ mở rộng chức năng của kernel và sẵn sàng để sử dụng. Linux được thiết kế để có thể bật tắt tương đối dễ dàng việc sử dụng 1 kernel bằng việc build source code cùng với 1 option như ví dụ dưới đây:

CONFIG_MODULES=y

Module dependencies

Trong linux 1 kernel module có thể cung cấp các hàm thực thi hoặc các biến, chúng được export bằng cách sử dụng macaro EXPORT_SYMBOL, điều này làm cho các moduel khác có thể sử dụng chúng. Chúng cũng được gọi là các symbols. Module B được gọi là bị phụ thuộc vào module A khi B sử dụng các symbol do A export.

depmod utility

depmod là 1 tool gần như là mặc định của các hệ thống như Ubuntu, Centos, ... nó được chạy khi build kernel, để tạo ra các tệp mô tả quan hệ phụ thuộc giữa các module. Công cụ này đọc từng file trong thư mục /lib/modules/<kernel _release> để xác quan hệ phụ thuộc trên. Kết quả được ghi vào thư mục modules.dep và phiên bản nhị phân modules.dep.bin.

image.png image.png

Module loading and unloading

Để một module hoạt động, bạn cần load nó vào kernel. Có thể dùng insmod với đường dẫn đầy đủ tới module — phương pháp này thường dùng trong quá trình phát triển — hoặc dùng modprobe, lệnh “thông minh” tự động xử lý các phụ thuộc của module, thường được ưu tiên trong môi trường product.

Manual loading

Manual loading cần sự can thiệp của người dùng, với quyền truy cập root. Có hai cách để thực hiện việc này:

modprobe and insmod

Trong quá trình phát triển, chúng ta có thể dùng insmod để load 1 module và cần phải cung cấp đầy đủ đường dẫn:

insmod /tpm/test/hello_drv.ko

insmod là hình thức tải module cấp thấp, là cơ sở cho các phương pháp tải module khác. Ngược lại, modprobe thường được quản trị viên hệ thống hoặc sử dụng trong môi trường sản xuất. Đây là một lệnh “thông minh” vì nó phân tích tệp modules.dep để tải trước các module phụ thuộc trước khi tải module chính, tự động xử lý phụ thuộc giống như một trình quản lý gói. modprobe sẽ tải được tự động các file kernel module được đặt trong /lib/modules mà không cần chỉ định đường dẫn khi load:

modprobe hello_drv.ko

Việc có thể dùng modprobe không, sẽ phụ thuộc vào việc depmod đã biết về các module được cài đặt hay chưa.

/etc/modules-load.d/<filename>.conf

image.png Nếu bạn muốn setting để module được tự động load lúc boot time thì hãy tạo 1 file config của riêng mình rồi add tên các module vào tương ứng mỗi dòng trong file config

Auto-loading

Tiện ích depmod không chỉ tạo ra các tệp modules.depmodules.dep.bin, mà còn thực hiện thêm một chức năng quan trọng khác. Khi một người phát triển kernel viết driver, họ biết chính xác phần cứng mà driver đó sẽ hỗ trợ và chịu trách nhiệm cung cấp Product IDVendor ID của tất cả các thiết bị được hỗ trợ. depmod sẽ xử lý các module này để trích xuất thông tin đó và tạo ra tệp modules.alias trong thư mục:

/lib/modules/<kernel_release>/modules.alias

Tệp này ánh xạ các thiết bị với driver tương ứng. Ví dụ trích đoạn trong modules.alias:

alias usb:v0403pFF1Cd*dc*dsc*dp*ic*isc*ip*in* ftdi_sio
alias usb:v0D8Cp0103d*dc*dsc*dp*ic*isc*ip*in* snd_usb_audio

Ở bước này, cần có một user-space hot-plug agent (thường là udev hoặc mdev) đăng ký với kernel để nhận thông báo khi thiết bị mới xuất hiện. Kernel gửi mô tả thiết bị (PID, VID, class, subclass, interface…) cho daemon hot-plug, daemon này sẽ gọi modprobe với thông tin trên.

modprobe sau đó phân tích modules.alias để tìm driver tương ứng với thiết bị. Trước khi load module, modprobe sẽ kiểm tra các phụ thuộc trong modules.dep nếu có, các module phụ thuộc được load trước, sau đó mới load module chính. Nếu không có phụ thuộc, module sẽ được load trực tiếp

Module unload

Để gỡ một module, lệnh thường dùng là rmmod, và nên dùng lệnh này cho các module được load bằng insmod. Tính năng gỡ module phải được bật trong cấu hình kernel (CONFIG_MODULE_UNLOAD=y), nếu không sẽ không thể gỡ module nào. Kernel cũng giữ bộ đếm tham chiếu để ngăn gỡ những module đang được sử dụng; nếu muốn ép gỡ, dùng rmmod -f <module>. Một lệnh cấp cao hơn là modprobe -r <module>, tự động gỡ cả các module phụ thuộc không dùng nữa, rất tiện cho lập trình viên. Có thể kiểm tra module đang load bằng lsmod.


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí