首先,想說明一下,促使我研究class(類)的是因爲它能夠自動創建/dev下的設備節點。當然class還有其另外的作用,且自動創建設備節點的還有udev系統,udev是處於用戶空間的,其自動創建設備節點也是依賴於sysfs文件系統中提供的class類,我有個問題,如果我的內核沒有移植好udev系統,只是利用class(類),能不能夠自動創建設備節點呢???針對這樣一個問題,想寫這個文章理清理清一下思路。
一個類是一個設備的高級視圖, 它抽象出低級的實現細節. 驅動可以見到一個SCSI 磁盤或者一個 ATA 磁盤, 在類的級別, 它們都是磁盤. 類允許用戶空間基於它們做什麼來使用設備, 而不是它們如何被連接或者它們如何工作.
幾乎所有的類都在 sysfs 中在 /sys/class 下出現. 因此, 例如, 所有的網絡接口可在 /sys/class/net 下發現, 不管接口類型. 輸入設備可在 /sys/class/input 下, 以及串行設備在 /sys/class/tty. 一個例外是塊設備, 由於歷史的原因在 /sys/block.
早期的Linux內核(版本2.4之前)並沒有實現一個統一的設備模型,設備節點的創建一般是mknod命令手動創建或利用devfs文件系統創建。早期的Linux發行版一般會採用手動創建的方式預先把通常用到的節點都創建出來,而嵌入式系統則會採用devfs的方式。起初Linux2.6 內核還支持devfs,但從2.6.18開始,內核完全移除了devfs系統而採用的udev的方式動態的創建設備節點。因此,新的Linux發行版都採用udev的方式管理設備節點文件。
udev 依靠所有通過 sysfs 輸出給用戶空間的設備信息, 並且依靠被 /sbin/hotplug 通知有設備添加或去除. 策略決策, 例如給一個設備什麼名子, 可在用戶空間指定, 內核之外. 這保證了命名策略被從內核中去除並且允許大量每個設備名子的靈活性.現在來驗證一下,class類是怎樣自動創建設備節點的。代碼如下:
注意,首先申明一下,在看ldd3的時候,書上說的class的相關接口是class_simple,比如:class_simple_create()、class_simple_destory()、class_device_create()、class_device_destory()等,經我查看,我這是Linux2.6.31版本的內核,裏面沒有這些接口,這些接口都被修改成了class_create()、class_destroy()、device_create()、device_destory()等。相關接口可以自己去查看內核源代碼。
#include <linux/device.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#define DEVNAME "hello"
static dev_t dev;
static struct class *hello_class;
static struct cdev *hello_cdev;
static int hello_open(struct inode *inode,struct file *flp)
{
return 0;
}
static int hello_close(struct inode *inode,struct file *flp)
{
return 0;
}
static struct file_operations hello_fops={
.owner=THIS_MODULE,
.open =hello_open,
.release=hello_close,
};
static int __init hello_init(void)
{
int error;
error = alloc_chrdev_region(&dev, 0, 2, "hello");
if (error)
{
printk("hello: alloc_chardev_region failed! ");
goto out;
}
hello_cdev = cdev_alloc();
if (hello_cdev == NULL)
{
printk("hello: alloc cdev failed! ");
error = -ENOMEM;
goto out_chrdev;
}
hello_cdev->ops = &hello_fops;
hello_cdev->owner = THIS_MODULE;
error = cdev_add(hello_cdev, dev, 1);
if (error)
{
printk("hello: cdev_add failed! ");
goto out_cdev;
}
hello_class = class_create(THIS_MODULE, DEVNAME);
if (IS_ERR(hello_class))
{
error = PTR_ERR(hello_class);
goto out_chrdev;
}
device_create(hello_class, NULL, dev, NULL, DEVNAME);
//memset (hello_buf, 0, sizeof(hello_buf));
//memcpy(hello_buf, DEFAULT_MSG, sizeof(DEFAULT_MSG));
printk("hello: Hello World! ");
return 0;
out_cdev:
cdev_del(hello_cdev);
out_chrdev:
unregister_chrdev_region(hello_cdev->dev, 2);
out:
return error;
}
static void __exit hello_exit(void)
{
device_destroy(hello_class, dev);
class_destroy(hello_class);
unregister_chrdev_region(hello_cdev->dev, 2);
cdev_del(hello_cdev);
printk("hello: Goodbye World ");
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("YWW");
MODULE_DESCRIPTION("HELLO_CLASS_AUTO_DEV");
上述代碼中標註紅色的爲class類自動創建設備節點的代碼。下載到開發板上運行,我們可以查看/dev目錄、/sys/class/目錄,看是否有/dev/hello、/sys/class/hello_class這樣的目錄。
經過哥的驗證,在dev、sys/class目錄下都會有相應的節點。可以說明一下,我的文件系統裏並沒有移植好udev系統。那就是說,class可以自動創建設備節點,不需要udev,而udev自動創建節點需要用到class。
爲了證明我一開始的疑問,需要去深入class_create()、device_create()的源碼探索了。
可以肯定,sys/class中節點的創建是在struct class的註冊函數裏,/dev/節點的創建也應該在device的註冊函數裏
日後分析。