linux采用模块方法,添加一个新的设备
该文转载自:http://rangercyh.blog.51cto.com/1444712/521244
系统调用是操作系统内核和应用程序之间的接口,而设备驱动程序是操作系统内核和机器硬件之间的接口。
设备驱动程序为应用程序屏蔽了硬件的细节,这样在应用程序看来,硬件设备只是一个设备文件, 应用程序可以像操作普通文件一样对硬件设备进行操作。
设备驱动程序是内核的一部分,它完成以下的功能:
(1) 对设备初始化和释放.
(2) 把数据从内核传送到硬件和从硬件读取数据.
(3) 读取应用程序传送给设备文件的数据和回送应用程序请求的数据.
(4) 检测和处理设备出现的错误.
Linux支持三中不同类型的设备:字符设备(character devices)、块设备(block devices)和网络设备(network interfaces)。
字符设备和块设备的主要区别是:在对字符设备发出读/写请求时,实际的硬件I/O一般就紧接着发生了。
块设备则不然,它利用一块系统内存作缓冲区,当用户进程对设备请求能满足用户的要求,就返回请求的数据,如果不能,就调用请求函数来进行实际的I/O操作。块设备是主要针对磁盘等慢速设备设计的,以免耗费过多的CPU时间来等待.
用户进程是通过设备文件来与实际的硬件打交道,每个设备文件都都有其文件属性(c/b),表示是字符设备还是块设备。
另外每个文件都有两个设备号,第一个是主设备号,标识驱动程序,第二个是从设备号,标识使用同一个设备驱动程序的不同的硬件设备,
比如有两个软盘,就可以用从设备号来区分他们.设备文件的的主设备号必须与设备驱动程序在登记时申请的主设备号一致,否则用户进程将无法访问到驱动程序.。
设备驱动程序工作的基本原理:
用户进程利用系统调用对设备进行诸如read/write操作,系统调用通过设备文件的主设备号找到相应的设备驱动程序,然后读取这个数据结构相应的函数指针,接着把控制权交给该函数。
最后,在用户进程调用驱动程序时,系统进入核心态,这时不再是抢先式调度。也就是说,系统必须在你的驱动程序的子函数返回后才能进行其他的工作。
如果你的驱动程序陷入死循环,你只有重新启动机器了。
下面我们就来添加一个字符设备:
- 编写设备驱动源代码
在设备驱动程序中有一个非常重要的结构file_operations,该结构的每个域都对应着一个系统调用。
用户进程利用系统调用对设备文件进行诸如read/write操作时,系统调用通过设备文件的主设备号找到相应的设备驱动程序,然后读取这个数据结构相应的函数指针,接着把控制权交给该函数。
- struct file_operations {
- int (*seek) (struct inode * ,struct file *, off_t ,int);
- int (*read) (struct inode * ,struct file *, char ,int);
- int (*write) (struct inode * ,struct file *, off_t ,int);
- int (*readdir) (struct inode * ,struct file *, struct dirent * ,int);
- int (*select) (struct inode * ,struct file *, int ,select_table *);
- int (*ioctl) (struct inode * ,struct file *, unsined int ,unsigned long);
- int (*mmap) (struct inode * ,struct file *, struct vm_area_struct *);
- int (*open) (struct inode * ,struct file *);
- int (*release) (struct inode * ,struct file *);
- int (*fsync) (struct inode * ,struct file *);
- int (*fasync) (struct inode * ,struct file *,int);
- int (*check_media_change) (struct inode * ,struct file *);
- int (*revalidate) (dev_t dev);
- }
编写设备驱动程序的主要工作是编写子函数,并填充file_operations的各个域。
例如:
- Struct file_operations my_fops={
- .read=my_read,
- .write=my_write,
- .open=my_open,
- .release=my_release
- }
然后再定义函数my_read,my_write,my_open,my_release相应的函数体。
例如:
- static ssize_t my_open(struct inode *inode,struct file *file){
- static int counter=0;
- if(Device_Open)
- return -EBUSY;
- Device_Open++;
- /*写入设备的信息*/
- sprintf(msg,"the device has been called %d times\n",counter++);
- msg_ptr=msg;
- return 0;
- }
同时对于可卸载的内核模块(LKM),至少还有两个基本的模块:
例如本例中的:
- static int __init my_init(void){
- int result;
- result=register_chrdev(0,"sky_driver",&my_fops);
- if(result<0){
- printk("error:can not register the device\n");
- return -1;
- }
- if(my_major==0){
- my_major=result;
- printk("<1>hehe,the device has been registered!\n");
- printk("<1>the virtual device was assigned major number %d.\n",my_major);
- printk("<1>To talk to the driver,create a dev file with\n");
- printk("<1>'mknod/dev/my c %d 0'\n",my_major);
- printk("<1>Remove the dev and the file when done\n");
- }
- return 0;
- }
- static void __exit my_exit(void){
- unregister_chrdev(my_major,"sky_driver");
- printk("<1>unloading the device\n");
- }
my_init 用于注册设备,获得设备的主设备号
调用register_chrdev(0,“sky_driver(设备名)”,&my_fops);
my_exit 用于注销设备
调用unregister_chrdev(my_major, “sky_driver(设备名)”);
然后在程序尾再调用这两个函数
Module_init(my_init);
Module_exit(my_exit)
MODULE_LICENSE(“GPL”);
编写自己的驱动程序源文件mydriver.c:
- #include <linux/kernel.h>
- #include <linux/module.h>
- #include <linux/fs.h>
- #include <linux/init.h>
- #include <linux/uaccess.h>
- #if CONFIG_MODVERSIONS == 1
- #define MODVERSIONS
- #include <linux/version.h>
- #endif
- #define DEVICE_NUM 0 //随机产生一个设备号
- static int device_num = 0; //用来保存创建成功后的设备号
- static char buffer[1024] = "mydriver"; //数据缓冲区
- static int open_nr = 0; //打开设备的进程数,用于内核的互斥
- //函数声明
- static int mydriver_open(struct inode *inode, struct file *filp);
- static int mydriver_release(struct inode *inode, struct file* filp);
- static ssize_t mydriver_read(struct file *file, char __user *buf, size_t count, loff_t *f_pos);
- static ssize_t mydriver_write(struct file *file, const char __user *buf, size_t count, loff_t *f_pos);
- //填充file_operations结构相关入口
- static struct file_operations mydriver_fops = {
- .read = mydriver_read,
- .write = mydriver_write,
- .open = mydriver_open,
- .release = mydriver_release,
- };
- //打开函数
- static int mydriver_open(struct inode *inode, struct file *filp)
- {
- printk("\nMain device is %d, and the slave device is %d\n", MAJOR(inode->i_rdev), MINOR(inode->i_rdev));
- if (open_nr == 0) {
- open_nr++;
- try_module_get(THIS_MODULE);
- return 0;
- }
- else {
- printk(KERN_ALERT "Another process open the char device.\n");//进程挂起
- return -1;
- }
- }
- //读函数
- static ssize_t mydriver_read(struct file *file, char __user *buf, size_t count, loff_t *f_pos)
- {
- //if (buf == NULL) return 0;
- if (copy_to_user(buf, buffer, sizeof(buffer))) //读缓冲
- {
- return -1;
- }
- return sizeof(buffer);
- }
- //写函数,将用户的输入字符串写入
- static ssize_t mydriver_write(struct file *file, const char __user *buf, size_t count, loff_t *f_pos)
- {
- //if (buf == NULL) return 0;
- if (copy_from_user(buffer, buf, sizeof(buffer))) //写缓冲
- {
- return -1;
- }
- return sizeof(buffer);
- }
- //释放设备函数
- static int mydriver_release(struct inode *inode, struct file* filp)
- {
- open_nr--; //进程数减1
- printk("The device is released!\n");
- module_put(THIS_MODULE);
- return 0;
- }
- //注册设备函数
- static int __init mydriver_init(void)
- {
- int result;
- printk(KERN_ALERT "Begin to init Char Device!"); //注册设备
- //向系统的字符登记表登记一个字符设备
- result = register_chrdev(DEVICE_NUM, "mydriver", &mydriver_fops);
- if (result < 0) {
- printk(KERN_WARNING "mydriver: register failure\n");
- return -1;
- }
- else {
- printk("mydriver: register success!\n");
- device_num = result;
- return 0;
- }
- }
- //注销设备函数
- static void __exit mydriver_exit(void)
- {
- printk(KERN_ALERT "Unloading...\n");
- unregister_chrdev(device_num, "mydriver"); //注销设备
- printk("unregister success!\n");
- }
- //模块宏定义
- module_init(mydriver_init);
- module_exit(mydriver_exit);
- MODULE_LICENSE("GPL");
- 编译该设备驱动代码
然后将设备驱动源文件复制到/usr/src/linux/drivers/misc下

修改misc目录下的Makefile文件,只要在最后添加一句即可:obj-m +=mydriver.o。

在/usr/src/linux/drivers/misc路径下执行命令:Make -C /usr/src/linux SUBDIRS=$PWD modules编译成功将得到mydriver.ko文件。

可以在misc目录下观察得到了mydriver.ko文件。

继续执行insmod ./mydriver.ko命令挂载内核中的模块。
然后通过lsmod命令可以看到增加的设备模块mydriver。

输入cat /var/log/messages可以看到设备注册成功。

此时进入/proc/devices文件会看到在字符设备中有250 mydriver。前面的是系统分配的主设备号,后面是设备注册名。
进入在/dev路径下,执行命令:
mknod /dev/mydriver c 250 0
第一个参数是新建设备文件的地址和名字。
第二个参数是指创建的是字符设备文件。
第三个参数是主设备号,第四个参数是从设备号,自己随便取。
执行成功会在/dev/char中看到一个新的设备文件mydriver

至此设备添加成功。
- 编译测试程序。
编写测试代码如下:
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <string.h>
- #include <stdio.h>
- #include <fcntl.h>
- int main(void)
- {
- int fd;
- char buf[1024];
- char get[1024];
- memset(get, 0, sizeof(get));
- memset(buf, 0, sizeof(buf));
- printf("please enter a string you want input to mydriver:\n");
- gets(get);
- fd = open("/dev/mydriver", O_RDWR, S_IRUSR|S_IWUSR);//打开设备
- if (fd > 0) {
- read(fd, &buf, sizeof(buf));
- printf("The message in mydriver now is: %s\n", buf);
- //将输入写入设备
- write(fd, &get, sizeof(get));
- //读出设备的信息并打印
- read(fd, &buf, sizeof(buf));
- printf("The message changed to: %s\n", buf);
- sleep(1);
- }
- else {
- printf("OMG...");
- return -1;
- }
- close(fd);//释放设备
- return 0;
- }
gcc -o mydriver_test mydriver_test.c
./mydriver_test

输入任意字符串,驱动程序将字符串拷贝进新加入的设备,然后再读取出来,设备中保留字符串信息,再次输入将覆盖原来的信息
linux采用模块方法,添加一个新的设备的更多相关文章
- Linux 在添加一个新账号后却没有权限怎么办
当添加一个新账号后,我们可能会发现新账号sudo 时会报告不在sudoers中,使用su -s时输入密码后也会认证失败 上网搜索大部分都要求修改/etc/sudoers中的内容,但修改这个文件必须需要 ...
- Flink资料(6) -- 如何添加一个新的Operator
false false false false EN-US ZH-CN X-NONE /* Style Definitions */ table.MsoNormalTable {mso-style-n ...
- 012.Adding a New Field --【添加一个新字段】
Adding a New Field 添加一个新字段 2016-10-14 3 分钟阅读时长 作者 By Rick Anderson In this section you'll use Entity ...
- Mysql学习(一)添加一个新的用户并用golang操作Mysql
Mysql添加一个新的用户并赋予权限 添加一个自己的用户到mysql 首先我们需要先用root用户登录mysql,但是刚安装完没有密码,我们先跳过密码 ailumiyana@ailumiyana:~/ ...
- RK平台Android4.4 添加一个新的遥控器支持以及添加特殊按键【转】
本文转载自:http://blog.csdn.net/coding__madman/article/details/52904063 版权声明:本文为博主原创文章,未经博主允许不得转载. 瑞芯微平台 ...
- 【IntelliJ IDEA】添加一个新的tomcat,tomcat启动无法访问欢迎页面,空白页,404
===================================第一部分,添加一个tomcat================================================== ...
- JS对象 数组连接 concat() 方法用于连接两个或多个数组。此方法返回一个新数组,不改变原来的数组。 语法 arrayObject.concat(array1,array2,.arrayN)
concat() 方法用于连接两个或多个数组.此方法返回一个新数组,不改变原来的数组. 语法 arrayObject.concat(array1,array2,...,arrayN) 参数说明: 注意 ...
- destoon 添加一个新的模块
根目录rename,中config.inc.php文件/module/rename下两个文件,my.inc.php ,rename.class.php/module/rename/admin/三个文件 ...
- 使用layui 做后台管理界面,在Tab中的链接点击后添加一个新TAB的解决方法
给链接或按钮 添加 onclick="self.parent.addTab('百度','http://www.baidu.com','icon-add')" 如: <a h ...
随机推荐
- json.net json转换神器
json.nethttps://json.codeplex.com/ api documenthttp://james.newtonking.com/json/help/index.html#
- 使用WIF实现单点登录Part III —— 正式实战 -摘自网络
经过前两篇文章,估计大家对WIF已经有比较充分的认识了,估计大家在经过了枯燥的理论以后,肯定是摩拳擦掌赶紧付诸于行动了.没办法,咱们程序员就是这个毛病.那好吧,我也不那么多废话了,直接进入正题吧. 我 ...
- localstorage本地存储
前段时间项目上用到了本地存储,所以研究看了下,在这做下笔记. 本地存储是一个window的一个属性,分别是localStorage和sessionStorage,两者用法完全相同,只不过一个是sess ...
- UIImage的使用
UIImage是IOS中层级比较高的一个用来加载和绘制图像的一个类,更底层的类还有CGImage,以及IOS5.0以后新增加的CIImage.今天我们主要聊一聊UIImage的三个属性: imageO ...
- 谁才是最快的消息队列:ActiveMQ, RabbitMQ[转]
Lately I performed a message queue benchmark, comparing several queuing frameworks (RabbitMQ, Active ...
- iOS 检测网络状态
一般有两种方式,都是第三方的框架,轮子嘛,能用就先用着,后面再优化. 一:Reachability 1.首先在AppDelegate.h添加头文件"Reachability.h", ...
- 服务框架HSF分析之一容器启动
大家平时都在用这个服务框架.简单阅读了下代码,了解其原理可以方便解决一些常见hsf的问题.限于篇幅,整个分析将分几个系列发布.第一篇将简单介绍Hsf的启动和各组件之间关系. 一. Hsf总体架构 这 ...
- 【JavaScript】出现即使设置了ID也获取不到的可能原因与window.onload
有时候.在JavaScript中.即使设置了ID也有可能出现document.getElementById()获取不到的情况,然后你就開始想document是否写错之类的.事实上根本就不是你的代码的大 ...
- 写个 Hello World 而已,要不要这么震撼?!
Atom 编辑器的一个插件,可以让你写代码的时候体验狂拽酷炫的效果! 如果来点音乐.再配上机械键盘的话,写代码是不是爽到爆呢? 这货全名叫: activate-power-mode atom pack ...
- jquery操作iframe中的js函数
关键字:jquery操作iframe中的js函数 1.jquery操作iframe中的元素(2种方式) var tha = $(window.frames["core_content&quo ...