当前位置: 首页 > 编程日记 > 正文

linux内核创建节点,Linux内核驱动自动创建设备节点文件

Linux下生成驱动设备节点文件的方法有3个:1、手动mknod;2、利用devfs;3、利用udev

在刚开始写Linux设备驱动程序的时候,很多时候都是利用mknod命令手动创建设备节点,实际上Linux内核为我们提供了一组函数,可以用来在模块加载的时候自动在/dev目录下创建相应设备节点,并在卸载模块时删除该节点。

在2.6.17以前,在/dev目录下生成设备文件很容易,

devfs_mk_bdev

devfs_mk_cdev

devfs_mk_symlink

devfs_mk_dir

devfs_remove

这几个是纯devfs的api,2.6.17以前可用,但后来devfs被sysfs+udev的形式取代,同时期sysfs文件系统可以用的api:

class_device_create_file,在2.6.26以后也不行了,现在,使用的是device_create ,从2.6.18开始可用

struct device *device_create(struct class *class, struct device *parent,

dev_t devt, const char *fmt, ...)

从2.6.26起又多了一个参数drvdata: the data to be added to the device for callbacks

不会用可以给此参数赋NULL

struct device *device_create(struct class *class, struct device *parent,

dev_t devt, void *drvdata, const char *fmt, ...)

下面着重讲解第三种方法udev

在驱动用加入对udev的支持主要做的就是:在驱动初始化的代码里调用class_create(...)为该设备创建一个class,再为每个设备调用device_create(...)( 在2.6较早的内核中用class_device_create)创建对应的设备。

内核中定义的struct class结构体,顾名思义,一个struct class结构体类型变量对应一个类,内核同时提供了class_create(…)函数,可以用它来创建一个类,这个类存放于sysfs下面,一旦创建好了这个类,再调用 device_create(…)函数来在/dev目录下创建相应的设备节点。这样,加载模块的时候,用户空间中的udev会自动响应 device_create(…)函数,去/sysfs下寻找对应的类从而创建设备节点。

struct class和class_create(…) 以及device_create(…)都包含在在/include/linux/device.h中,使用的时候一定要包含这个头文件,否则编译器会报错。

struct class定义在头文件include/linux/device.h中

class_create(…)在/drivers/base/class.c中实现

device_create(…)函数在/drivers/base/core.c中实现

class_destroy(...),device_destroy(...)也在/drivers/base/core.c中实现调用过程类似如下:

static struct class *spidev_class;

/*-------------------------------------------------------------------------*/

static int __devinit spidev_probe(struct spi_device *spi)

{

....

dev = device_create(spidev_class, &spi->dev, spidev->devt,

spidev, "spidev%d.%d",

spi->master->bus_num, spi->chip_select);

...

}

static int __devexit spidev_remove(struct spi_device *spi)

{

......

device_destroy(spidev_class, spidev->devt);

.....

return 0;

}

static struct spi_driver spidev_spi = {

.driver = {

.name =        "spidev",

.owner =    THIS_MODULE,

},

.probe =    spidev_probe,

.remove =    __devexit_p(spidev_remove),

};

/*-------------------------------------------------------------------------*/

static int __init spidev_init(void)

{

....

spidev_class = class_create(THIS_MODULE, "spidev");

if (IS_ERR(spidev_class)) {

unregister_chrdev(SPIDEV_MAJOR, spidev_spi.driver.name);

return PTR_ERR(spidev_class);

}

....

}

module_init(spidev_init);

static void __exit spidev_exit(void)

{

......

class_destroy(spidev_class);

......

}

module_exit(spidev_exit);

MODULE_DESCRIPTION("User mode SPI device interface");

MODULE_LICENSE("GPL");

下面以一个简单字符设备驱动来展示如何使用这几个函数

#include

#include

#include

#include

#include

#include

int HELLO_MAJOR = 0;

int HELLO_MINOR = 0;

int NUMBER_OF_DEVICES = 2;

struct class *my_class;

//struct cdev cdev;

//dev_t devno;

struct hello_dev {

struct device *dev;

dev_t chrdev;

struct cdev cdev;

};

static struct hello_dev *my_hello_dev = NULL;

struct file_operations hello_fops = {

.owner = THIS_MODULE

};

static int __init hello_init (void)

{

int err = 0;

struct device *dev;

my_hello_dev = kzalloc(sizeof(struct hello_dev), GFP_KERNEL);

if (NULL == my_hello_dev) {

printk("%s kzalloc failed!\n",__func__);

return -ENOMEM;

}

devno = MKDEV(HELLO_MAJOR, HELLO_MINOR);

if (HELLO_MAJOR)

err= register_chrdev_region(my_hello_dev->chrdev, 2, "memdev");

else

{

err = alloc_chrdev_region(&my_hello_dev->chrdev, 0, 2, "memdev");

HELLO_MAJOR = MAJOR(devno);

}

if (err) {

printk("%s alloc_chrdev_region failed!\n",__func__);

goto alloc_chrdev_err;

}

printk("MAJOR IS %d\n",HELLO_MAJOR);

cdev_init(&(my_hello_dev->cdev), &hello_fops);

my_hello_dev->cdev.owner = THIS_MODULE;

err = cdev_add(&(my_hello_dev->cdev), my_hello_dev->chrdev, 1);

if (err) {

printk("%s cdev_add failed!\n",__func__);

goto cdev_add_err;

}

printk (KERN_INFO "Character driver Registered\n");

my_class = class_create(THIS_MODULE,"hello_char_class");  //类名为hello_char_class

if(IS_ERR(my_class))

{

err = PTR_ERR(my_class);

printk("%s class_create failed!\n",__func__);

goto class_err;

}

dev = device_create(my_class,NULL,my_hello_dev->chrdev,NULL,"memdev%d",0);      //设备名为memdev

if (IS_ERR(dev)) {

err = PTR_ERR(dev);

gyro_err("%s device_create failed!\n",__func__);

goto device_err;

}

printk("hello module initialization\n");

return 0;

device_err:

device_destroy(my_class, my_hello_dev->chrdev);

class_err:

cdev_del(my_hello_dev->chrdev);

cdev_add_err:

unregister_chrdev_region(my_hello_dev->chrdev, 1);

alloc_chrdev_err:

kfree(my_hello_dev);

return err;

}

static void __exit hello_exit (void)

{

cdev_del (&(my_hello_dev->cdev));

unregister_chrdev_region (my_hello_dev->chrdev,1);

device_destroy(my_class, devno);         //delete device node under /dev//必须先删除设备,再删除class类

class_destroy(my_class);                 //delete class created by us

printk (KERN_INFO "char driver cleaned up\n");

}

module_init (hello_init);

module_exit (hello_exit);

MODULE_LICENSE ("GPL");

这样,模块加载后,就能在/dev目录下找到memdev这个设备节点了。

例2:内核中的drivers/i2c/i2c-dev.c

在i2cdev_attach_adapter中调用device_create(i2c_dev_class, &adap->dev,

MKDEV(I2C_MAJOR, adap->nr), NULL,

"i2c-%d", adap->nr);

这样在dev目录就产生i2c-0  或i2c-1节点

接下来就是udev应用,udev是应用层的东西,udev需要内核sysfs和tmpfs的支持,sysfs为udev提供设备入口和uevent通道,tmpfs为udev设备文件提供存放空间

udev的源码可以在去相关网站下载,然后就是对其在运行环境下的移植,指定交叉编译环境,修改Makefile下的CROSS_COMPILE,如为mipsel-linux-,DESTDIR=xxx,或直接make CROSS_COMPILE=mipsel-linux-,DESTDIR=xxx 并install

把主要生成的udevd、udevstart拷贝rootfs下的/sbin/目录内,udev的配置文件udev.conf和rules.d下的rules文件拷贝到rootfs下的/etc/目录内

并在rootfs/etc/init.d/rcS中添加以下几行:

echo “Starting udevd...”

/sbin/udevd --daemon

/sbin/udevstart

(原rcS内容如下:

# mount filesystems

/bin/mount -t proc /proc /proc

/bin/mount -t sysfs sysfs /sys

/bin/mount -t tmpfs tmpfs /dev

# create necessary devices

/bin/mknod /dev/null c 1 3

/bin/mkdir /dev/pts

/bin/mount -t devpts devpts /dev/pts

/bin/mknod /dev/audio c 14 4

/bin/mknod /dev/ts c 10 16

)

这样当系统启动后,udevd和udevstart就会解析配置文件,并自动在/dev下创建设备节点文件

(注:以上内容为多处转载,没有一一列出转载地址)

相关文章:

javascript publish/subscribe or observer pattern

定义 定义一对多的对象封装,目标对象状态发生变化,它所有的接受者都会收到通知并做相应的更新。 使用频率:5/5 最高 概要 观察者模式,也就是发布者/订阅者模式,当发布者发布一个通知的时候,订阅者就会收到通…

图的遍历——DFS(邻接矩阵)

递归 标记 一个连通图只要DFS一次&#xff0c;即可打印所有的点。 #include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <malloc.h>using namespace std;const int VERTEX_NUM 20; const int INFINITY 0x…

徐韬:CCF - 个贷违约预测Baseline

徐韬是华北电力大学数理系大四的学生&#xff0c;Datawhale成员/Dreamtech成员&#xff0c;参加了多期Datawhale的组队学习&#xff0c;也在天池/CCF/讯飞等比赛中取得了不错的成绩&#xff0c;现保送大连理工大学软件学院深造。 这篇图文是他在线下组队学习时&#xff0c;为大…

linux 创建crontab文件位置,[基础教程]linux系统的crontab计划任务添加和删除

在linux系统中&#xff0c;有时候为了节省人力&#xff0c;所以将一些脚本进行定时执行&#xff0c;通过crontab计划任务进行启动和停止&#xff0c;这样能方便大部分时间来做其他事情&#xff0c;下面主要介绍一下如何启动和删除crontab计划任务添加计划任务1.首先要准备好要添…

C#利用lambda在函数中创建内部函数

有使用过JS的朋友&#xff0c;相信都知道function。JS中的function是可以在里面在定义一个作为内部使用的。有时为了控制作用域&#xff0c;或者这种小函数只在这个函数体内会使用&#xff0c;所以就不希望在外部在作额外的定义。那C#中有没有这样类似的方式呢&#xff1f;答案…

IntellJ_打开选中的文件所在的文件夹

方法&#xff08;一&#xff09;最简单 idea 打开 setting 找到keymap 搜索 show in explorer 右键设置一个快捷键 我一般设置 alt v 然后点击项目其中一个文件位置 比如我点击一个 hellowworld.class 然后我按 alt v 就会自己打开他在本地的位置 方法&#xff08;二&#x…

姜子上:利用BiLSTM-CRF进行命名实体识别

姜子上是华北电力大学数理系大二的学生&#xff0c;LSGO软件技术团队&#xff08;Dreamtech算法组&#xff09;成员&#xff0c;参加了多期Datawhale的组队学习。 这篇图文是他在线下组队学习时&#xff0c;为大家分享自己学习“知识图谱”过程中的笔记。 希望参与我们线下组…

linux命令行3d,Linux命令行快捷键

总结一下&#xff1a;1.移动光标&#xff1a;1.1 行首 ctrla1.2 行末 ctrle1.3 前移一字符 ctrlb(注&#xff1a;因为左边的内容先于右边输入 &#xff0c;故向左为向前&#xff0c;向右为向后)1.4 后移一字符 ctrlf1.5 前移一词 altb1.6 后移一词 altf2.删除2.1 删除至行首 ct…

Linux系统配置VI或VIM的技巧

Linux系统配置VI或VIM的技巧作者&#xff1a;IT专家网论坛出处&#xff1a;IT专家网论坛2008-10-28 11:08配置VI和VIM的颜色显示&#xff0c;使它能够高亮度显示一些特别的单词&#xff0c;这对编写程序很有用⋯⋯  1、VI或VIM的配置文件的路径  发现/usr/share/vim/vimrc和…

【组队学习】十一月微信图文索引

十一月微信图文索引 一、组队学习相关 周报&#xff1a; Datawhale组队学习周报&#xff08;第037周&#xff09;Datawhale组队学习周报&#xff08;第038周&#xff09;Datawhale组队学习周报&#xff08;第039周&#xff09;Datawhale组队学习周报&#xff08;第040周&…

僵尸(bot)程序缓解

2019独角兽企业重金招聘Python工程师标准>>> 与浮在表面而广受关注的间谍软件、广告软件、垃圾邮件不同&#xff0c;僵尸程序往往不受注意。它们是散发间谍软件、广告软件、垃圾邮件的罪魁祸首&#xff0c;已经被列为对个人用户及企业 威胁不断增加的一种安全危害。…

Linux火狐解压完运行不了,在Ubuntu系统下firefox账号无法登录的解决

在Ubuntu 16.04系统下默认自带有firefox浏览器&#xff0c;但是使用这个firefox浏览器会发现账号无法登录&#xff0c;原来是在windows系统下的数据没有办法同步&#xff0c;书签也同步不了。经过查询资料后得知&#xff0c;Ubuntu系统下自带的firefox浏览器是国际版本&#xf…

[亿能测试_www.gdtesting.com]测试技术资料网盘共享

[亿能测试_www.gdtesting.com]测试技术资料网盘共享: http://pan.baidu.com/share/home?uk974090852 转载于:https://www.cnblogs.com/preftest/p/3249341.html

【NCEPU】吴丹飞:新闻文章点击预测

吴丹飞是华北电力大学数理系大三的学生&#xff0c;LSGO软件技术团队&#xff08;Dreamtech算法组&#xff09;成员&#xff0c;参加了多期Datawhale的组队学习&#xff0c;荣获多期优秀队长的称号。 这篇图文是她在线下组队学习时&#xff0c;为大家分享自己参与天池“新闻文…

centos中执行apt-get命令提示apt-get command not found

在centos下用yum install xxxyum和apt-get的区别一般来说著名的linux系统基本上分两大类&#xff1a; 1.RedHat系列&#xff1a;Redhat、Centos、Fedora等 2.Debian系列&#xff1a;Debian、Ubuntu等 RedHat 系列 1 常见的安装包格式 rpm包,安装rpm包的命令是“rpm -参数” 2 包…

在linux上装git教程,教你玩转Git-Linux 平台上安装

导读Git 是一个开源的分布式版本控制系统&#xff0c;用于敏捷高效地处理任何或小或大的项目。Git 是 Linus Torvalds 为了帮助管理 Linux 内核开发而开发的一个开放源码的版本控制软件。Git 与常用的版本控制工具 CVS, Subversion 等不同&#xff0c;它采用了分布式版本库的方…

jsp中如何判断el表达式中的BigDecimal==0

比较蠢一点的做法&#xff1a; <c:if test"${not ((someBigDecimal < 0) or (someBigDecimal > 0))}"> (在JSP2.2里面)好点的做法&#xff1a; ${someBigDecimal.unscaledValue() 0} 使用fn函数的做法: ${fn:isZero(someBigDecimal)} 转载于:https://ww…

zabbix 安装时的报错mysql_connect(): Access denied for us

zabbix在安装完成时的报错出现提示&#xff1a;mysql_connect(): Access denied for user zabbixlocalhost (using password: YES) [include/db.inc.php:77]解决方法&#xff1a;# vim /etc/zabbix/web/zabbix.conf.php ## 修改对应数据库的密码即可&#xff0c; 这是…

【NCEPU】毛慧昀:Matplotlib数据可视化

毛慧昀是华北电力大学计算机系大三的学生&#xff0c;LSGO软件技术团队&#xff08;Dreamtech算法组&#xff09;成员&#xff0c;参加了多期Datawhale的组队学习&#xff0c;荣获多期优秀队长的称号。 这篇图文是她在线下组队学习时&#xff0c;为大家分享自己学习Matplotlib…

linux qml 环境,利用Qml与Golang打造Gui客户端(二)qamel环境安装

由于种种原因&#xff0c;放弃了therecipe/qt&#xff0c;转向了更为轻量级的qamel&#xff0c;这个库的安装非常简单&#xff0c;跟随着以下步骤就能解决安装安装qamel非常简单&#xff0c;只需要go get -v github.com/go-qamel/qamel即可。如果一切顺利&#xff0c;将会自动编…

uboot readme 文件 (1)

Software Configuration:软件的配置Configuration is usually done using C preprocessor defines; therationale behind that is to avoid dead code whenever possible.使用c的预处理 避免 “dead code” 两种配置变量There are two classes of configuration variables: * C…

青少年编程竞赛交流群周报(第040周)

2021年12月05日&#xff08;周日&#xff09;晚20:00我们在青少年编程竞赛交流群开展了第四十期直播活动。 一、直播内容 我们直播活动的主要内容如下&#xff1a; 讲解了上次测试中小朋友们做错的题目 Scratch青少年编程能力等级测试模拟题&#xff08;四级&#xff09;。演…

用Cordova打包Vue-vux项目

技术搭建&#xff1a;vue vux 首先推荐阅读这篇文章&#xff0c;写的已经很详细了&#xff1a;https://www.jianshu.com/p/25d797b983cd 此处记录下我按照这篇文章打包的时候报的一些错误&#xff0c;方便大家借鉴&#xff1a; 1、当执行最后一步 cordova build android 时&a…

linux mpp集群环境,Linux环境下,DM8-MPP两节点集群搭建

目录1.主备库&#xff0c;监视器信息部署2.初始化数据库实例3.启停数据库4.数据库备份与还原5.检查、修改和创建配置文件6.启动数据库&#xff0c;修改数据库模式和OGUID8.配置守护进程9.配置监视器10.启动守护进程11.启动监视器1.主备库&#xff0c;监视器信息部署(1)主库实例…

线性代数 2021-2022-1 考试要点

1、线性方程组解的判定、解的结构与求解&#xff1b; 2、矩阵秩的相关性质&#xff1b; 3、矩阵、分块矩阵的运算&#xff1b; 4、行列式的运算&#xff1b; 5、矩阵方程的求解&#xff1b; 6、向量空间&#xff08;RnR^nRn空间&#xff0c;多项式空间&#xff0c;矩阵空间…

JavaServer Faces技术

一、JSF是构建Java Web程序的服务器端的组件框架&#xff0c;包括&#xff1a; 1&#xff09;一个API&#xff0c;用来表示组件及管理他们的状态&#xff1b;处理事件&#xff1b;服务器端&#xff1b;数据转换&#xff1b;定义页面导航&#xff1b;支持国际化和可访问性&#…

Ubuntu安装Anaconda

安装Anaconda的最简单方法是下载最新的Anaconda安装程序bash脚本&#xff0c;然后运行它。 在Anaconda Downloads页面找到最新版本的Anaconda for Python 3 。当前最新版本为5.1.0。 切换到/tmp服务器上的目录 cd /tmp 在运行它之后可以删除掉。 使用wget命令下载anaconda安装包…

linux制作共享服务器,Linux如何制作一个简单的共享服务器

Linux下打开一个终端&#xff0c;cd 到某一个目录下&#xff0c;输入“ python -m SimpleHTTPServer ”&#xff0c;就可以将当前目录下的内容分享出去&#xff0c;如下&#xff1a;linuxidc www.linuxidc.com:~$ cd VideoSpace/linuxidc www.linuxidc.com:~/VideoSpace$ pytho…

报表应用系列——图表JFreeChart: 第 4 章 折线图

双击代码全选12345DefaultCategoryDataset dataset  new DefaultCategoryDataset(); dataset.addValue(100, "Jan", "Spring Security"); dataset.addValue(150, "Jan", "jBPM 4"); dataset.addValue(300, "Jan&qu…

中国电子学会青少年编程能力等级测试图形化四级编程题:小猴数草莓

「青少年编程竞赛交流群」已成立&#xff08;适合6至18周岁的青少年&#xff09;&#xff0c;公众号后台回复【Scratch】或【Python】&#xff0c;即可进入。如果加入了之前的社群不需要重复加入。 我们将有关编程题目的教学视频已经发布到抖音号21252972100&#xff0c;小马老…