一.主设备号和次设备号
主设备号表示设备对应的驱动程序;次设备号由内核使用,用于正确确定设备文件所指的设备。
内核用dev_t类型(<linux/types.h>)来保存设备编号,dev_t是一个32位的数,12位表示主设备号,20为表示次设备号。
在实际使用中,是通过<linux/kdev_t.h>中定义的宏来转换格式。
(dev_t)-->主设备号、次设备号 MAJOR(dev_t dev)
(dev_t)-->主设备号、次设备号 MINOR(dev_t dev)
主设备号、次设备号-->(dev_t) MKDEV(int major,int minor)
二.分配和释放设备号
建立一个字符设备之前,驱动程序首先要做的事情就是获得设备编号。其这主要函数在<linux/fs.h>中声明:
//指定设备编号
int register_chrdev_region(dev_t first, unsigned int count,char*name);
//动态生成设备编号
int alloc_chrdev_region(dev_t *dev, unsigned int firstminor,
unsigned int count, char *name);
//释放设备编号
void unregister_chrdev_region(dev_t first, unsigned int count);
分配之设备号的最佳方式是:默认采用动态分配,同时保留在加载甚至是编译时指定主设备号的余地。在scull中分配的方式是:
if (scull_major) {
dev = MKDEV(scull_major, scull_minor);
result = register_chrdev_region(dev, scull_nr_devs, "scull");
} else {
result = alloc_chrdev_region(&dev, scull_minor, scull_nr_devs,"scull");
scull_major = MAJOR(dev);
}
if (result < 0) {
printk(KERN_WARNING "scull: can't get major %d/n", scull_major);
return result;
}
可以通过以下例子来学习这三个函数:
头文件scull.h如下:如果major不为0,则为指定的major号码,如果为0,则采用系统自行动态分配的方式。我们的模块包括4个设备,minor号码从0-3。
#ifndef _WEI_SCULL_H
#define _WEI_SCULL_H
#define SCULL_MAJOR 0
#define SCULL_MINOR_MIN 0
#define SCULL_DEV_NUM 4
#endif
源文件scull.c如下:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include "scull.h"
MODULE_LICENSE("Dual BSD/GPL");
dev_t dev;
int is_get_dev = -1;
int scull_major = SCULL_MAJOR;
int scull_minor = SCULL_MINOR;
int scull_nr_devs = SCULL_NR_DEVS;
static int __init scull_init(void)
{
printk("Scull module init enter/n");
if(scull_major){ /* 根据major和minor号码获得dev */
dev = MKDEV(scull_major,scull_minor );
is_get_dev = register_chrdev_region (dev,scull_nr_devs,"scull");//注册
}else{ /* 系统动态分配 */
is_get_dev = alloc_chrdev_region (&dev,scull_minor, scull_nr_devs,"scull");
scull_major = MAJOR(dev ); //获取主号码,MINOR()可获取minor号码
}
if(is_get_dev < 0){
printk(KERN_WARNING "scull: can't get device major number %d/n",scull_major);
}
return is_get_dev;
}
/* 在模块结束的时候,应该释放注册的资源*/
static void __exit scull_exit(void)
{
if(is_get_dev < 0){
return ;
}else{
unregister_chrdev_region(dev,scull_nr_devs );
printk("Scull module exit/n");
}
}
module_init(scull_init);
module_exit(scull_exit);
在上边编译完成之后,要通过加载模块,建立设备等过程,此处,书中例子给出了一个脚本scull_load,这里大概解释一下:
#!/bin/sh
module="scull"
device="scull"
mode="644"
#安装scull模块
/sbin/insmod ./$module.ko $* || exit 1
#删除原来的设备
rm -f /dev/${device}[0-3]
#在/proc/devices中找到第二个字段等于scull的行,
#打印该行的第一个字段即主设备号
major=$(awk "/$2==/"$module/" {print /$1}" /proc/devices)
#在/dev下创建4个字符设备
mknod /dev/${device}0 c $major 0
mknod /dev/${device}1 c $major 1
mknod /dev/${device}2 c $major 2
mknod /dev/${device}3 c $major 3
scull_unload
#!/bin/sh
module="scull"
device="scull"
#如果没有加载该模块结束,否则卸载模块,并删除相关的节点。
/sbin/lsmod | grep $module || exit 1
/sbin/rmmod $module
rm -f /dev/${device}[0-3]
Makefile:
ifneq ($(KERNELRELEASE),)
obj-m :=scull.o
else
KDIR :=/lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
default:
$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules
rm -r -f .tmp_versions *.mod.c .*.cmd *.o Modules.symvers
endif
实验过程:
root@xhy-desktop:/home/xhy/MyCode/work-ldd/ch3# make
root@xhy-desktop:/home/xhy/MyCode/work-ldd/ch3# ./scull_load
root@xhy-desktop:/home/xhy/MyCode/work-ldd/ch3# cat /proc/devices
Character devices:
1 mem
4 /dev/vc/0
。。。
226 drm
250 scull //说明已经有scull设备
251 heci
252 hidraw
253 usbmon
254 rtc
root@xhy-desktop:/home/xhy/MyCode/work-ldd/ch3# ls -l /dev/scull*
crw-r--r-- 1 root root 250, 0 2009-12-07 20:45 /dev/scull0
crw-r--r-- 1 root root 250, 1 2009-12-07 20:45 /dev/scull1
crw-r--r-- 1 root root 250, 2 2009-12-07 20:45 /dev/scull2
crw-r--r-- 1 root root 250, 3 2009-12-07 20:45 /dev/scull3
本文介绍Linux系统中设备号的管理方法,包括主设备号和次设备号的概念、宏定义及其转换,以及如何通过内核提供的函数进行设备号的分配与释放。
705

被折叠的 条评论
为什么被折叠?



