Windows XP Windows 7 Windows 2003 Windows Vista Windows 綜合教學 Linux 系統教學
Windows 10 Windows 8 Windows 2008 Windows NT Windows Server 電腦軟體教學

從零撰寫 Linux 字元設備驅動:基於 udev 動態建立節點

編輯:Linux 系統常見問題解答

本文記錄在 Ubuntu 3.8.0 核心上,參考《Linux 裝置驅動程式》(LDD3,針對 2.6 核心)編寫一個簡單的字元設備驅動 star。模組載入後透過 udev 自動建立 /dev/star0,讀取裝置節點可輸出指定數量的星號,數量由模組參數 howmany 動態指定。

專案結構

star.h      // 模組資訊巨集
star.c      // 驅動主體
Makefile    // 核心模組編譯腳本

核心程式碼片段 (star.c)

#include <linux/init.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/uaccess.h>
#include "star.h"

#define STAR_MAJOR 0          // 動態分配主裝置號
#define STAR_DEVS 1
#define DEVICE_NAME "star0"
#define CLASS_NAME "star"

static int howmany = 5;
module_param(howmany, int, S_IRUGO);  // 可透過 insmod howmany=10 指定

MODULE_AUTHOR("Tao Yang");
MODULE_DESCRIPTION("A CHAR DEVICE DRIVER SAMPLE USING UDEV");
MODULE_VERSION("0.1");
MODULE_LICENSE("GPL");

static struct class *star_class;
static struct device *star_sysdevice;
static int star_major;
static struct star_dev {
    char *data;
    struct cdev cdev;
} star_device;

// read: 輸出 howmany 個 '*'
static ssize_t star_read(struct file *filp, char __user *buf, size_t count, loff_t *ppos) {
    char star_str[10000];
    int i, len;
    for (i = 0; i < howmany; i++) star_str[i] = '*';
    star_str[howmany] = '\n';
    len = howmany + 1;
    if (count < len) return -EINVAL;
    if (*ppos != 0) return 0;
    if (copy_to_user(buf, star_str, len)) return -EINVAL;
    *ppos = len;
    return len;
}

static int star_open(struct inode *inode, struct file *filp) {
    struct star_dev *dev = container_of(inode->i_cdev, struct star_dev, cdev);
    filp->private_data = dev;
    return 0;
}

static const struct file_operations star_fops = {
    .owner = THIS_MODULE,
    .read = star_read,
    .open = star_open,
};

static void star_setup_cdev(struct star_dev *dev, int index) {
    int err, devno = MKDEV(star_major, index);
    cdev_init(&dev->cdev, &star_fops);
    dev->cdev.owner = THIS_MODULE;
    err = cdev_add(&dev->cdev, devno, 1);
    if (err) printk(KERN_ALERT "Error %d adding star%d", err, index);
}

static int __init star_init(void) {
    dev_t dev;
    int ret;
    printk(KERN_ALERT "hello tom!\n");
    if (star_major) {
        dev = MKDEV(star_major, 0);
        ret = register_chrdev_region(dev, STAR_DEVS, DEVICE_NAME);
    } else {
        ret = alloc_chrdev_region(&dev, 0, STAR_DEVS, DEVICE_NAME);
        star_major = MAJOR(dev);
        printk(KERN_ALERT "dynamic! Major=%d\n", star_major);
    }
    if (ret < 0) return ret;

    star_setup_cdev(&star_device, 0);
    star_class = class_create(THIS_MODULE, CLASS_NAME);
    star_sysdevice = device_create(star_class, NULL, MKDEV(star_major, 0), NULL, DEVICE_NAME);
    return 0;
}

static void __exit star_exit(void) {
    device_destroy(star_class, MKDEV(star_major, 0));
    class_destroy(star_class);
    cdev_del(&star_device.cdev);
    printk(KERN_ALERT "goodbye...\n");
}

module_init(star_init);
module_exit(star_exit);

Makefile

ifneq ($(KERNELRELEASE),)
obj-m := star.o
else
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
default:
	$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
endif

編譯、載入、測試

make
sudo insmod star.ko howmany=10
dmesg -c
# 輸出: dynamic! Major=249 等訊息
cat /dev/star0
**********
# 輸出 10 個星號

關鍵點總結

  • 動態申請主裝置號 alloc_chrdev_region,避免衝突。
  • 使用 class_create + device_create 配合 udev 自動建立 /dev/star0。
  • 模組參數 module_param 實現運行時配置星號數量。
  • copy_to_user 將核心資料安全複製到用戶空間。

此範例為字元驅動最小原型,可擴展寫入、ioctl、非阻塞、輪詢等功能,奠定驅動開發基礎。

Copyright © Windows教程網 All Rights Reserved