应用层如何内核.mdhtml
首先来讲是设备号的引入,咱们经过 cat/proc/kallsyms |grep mydevice 能够查看设备号,固然咱们也是能够本身建立设备号,这是源于咱们在写内核模块的时候在程序中指定。设备号有了,他就能够标识咱们具体的设备。那咱们应用层如何操做那?其实咱们应用层须要建立一个设备节点文件建立的方法是sudo mknod /dev/hello c 255 0 这样咱们就能够建立一个指定设备号的设备节点文件。经过设备号,使设备节点文件和内核中的设备驱动程序关联了起来。这其实也就打通了。有了文件,咱们应用层就能够用open啊,read啊去操做这个设备节点文件。真正体现了linux下一切皆文件啊!。
node
用户空间经过mknod建立了一个设备文件hello(设备号) 对应在内核空间建立一个inode结构体(包含有设备号)与之对应
应用程序打开这个设备文件: 操做inode结构体,取出inode里面的设备号 ==》 到内核的cdev链表中去查找这个设备号对应的cdev对象 ==》 找到
cdev对象里面的fops ===》 找到fops里面的open函数指针 ==》 这个函数指针指向咱们本身实行的open函数。linux
按照之前的思路:咱们如今若是是一个服务,服务于多个设备,那么问题就来了,显然咱们是不会写多个驱动程序的,可是有些数据是咱们必需要有多份的。好比咱们如今拿最简单
的一个操做,咱们在驱动程序里面注册俩个设备号,那么意味着咱们在用户空间,有俩个设备节点文件,咱们在应用程序中打开俩份,都采用写入的方式,写数据,若是内核中咱们把
接受数据的缓冲区没有从新备份,公用一份,结果就是数据的覆盖。这些存储的数据显然是要区分的,那个设备就是那个设备,既然是服务于多设备,那设备号的建立也天然是建立多个
注册多个,这些均可以很是容易的指定,具体能够看代码以及cdev显然也是俩份,他们都在内核的链表中,可是注意cdev结构体中的fops指针都指向的是同一个,因此fops是使用的同一个
其实也很好理解fops里面都是些读写操做的函数指针,不必区分。共用更加好。
,对于cdev还比较简单,咱们在建立本身的cdev结构体的时候,指定参数就能够多建立。
最后咱们干脆把这些都要用到的东西打包个结构体,叫作设备结构体。咱们回顾上面,当咱们又一个设备使用驱动程序的时候,咱们指定一个相关数据接收,可是多个设备文件的时候
就出现了对应问题,那就是 :对应问题,咱们如何指定设备号对应的保存数据的变量(更加准确的是设备结构体中的变量,由于咱们封装了)。在open的时候,咱们是很是清楚当前的
设备号的由于open的时候,系统函数给咱们传递了struct inode * inodep, struct file * filep 俩个参数,第一个参数是inode号,第二个是filep(这个结构体保存了我,文件的状态,以及当前
decv。咱们经过inodep->i_cdev 获取到当前的cdev的地址,而后经过container_of(ptr, type, member)根据结构体成员的地址从而获取到整个结构体的首地址。
拿到了设备结构体的首地址,而后经过filep->private_data = dev;将地址保存到file结构体中,借助file传递,由于file在读写函数中也调用了。到了读写函数中咱们首先用一个对应的结构体指针把传来
的地址接收struct hello_device * dev = filep->private_data;哈哈哈,咱们在读写函数中拿到了咱们的数据,并且是依靠不一样的decv,拿到不一样的设备结构体。web
完美@!
函数
#include<linux/module.h>
#include<linux/init.h>
#include<linux/fs.h>
#include<linux/cdev.h>
#include<asm/uaccess.h>
/*2017年1月12日19:49:34 张飞online*/
/* 许可证声明 */
MODULE_LICENSE("GPL");
#define NUM_OF_DEV 2
int major =255;
int minor =0;
//设备结构体:将本身用的数据封装
struct hello_device {
dev_t devno;//设备号
struct cdev mycdev;// cdev
char data[64];
}hello_dev[NUM_OF_DEV];
//hello_dev[0] //设备1
//hello_dev[1] //设备2
/*##############################################
*应用程序的open调用驱动的 hello_open 依靠 设备号和cdev中的iops,具体实现
能够看上面的文字
*
##############################################*/
staticint hello_open(struct inode * inodep,struct file * filep)
{
struct hello_device * dev;
printk("-- %s called -- major = %d minor = %d\n", __FUNCTION__, imajor(inodep), iminor(inodep));
/*inodep->i_cdev指明设备结构体中的mycdev地址,最终获取到当前cdev对应的咱们本身定义的
设备结构体的地址,这是核心:就是依靠当前cdev获取当前咱们的结构体地址
*/
dev = container_of(inodep->i_cdev,struct hello_device, mycdev);
/*
用file中的私有数据传出,由于读写函数也传入了file,并且file也是一 一对应的,当前文件的file
*/
filep->private_data = dev;
return0;
}
//应用程序的close调用驱动的hello_release
staticint hello_release(struct inode * inodep,struct file * filep)
{
printk("-- %s called --\n", __FUNCTION__);
return0;
}
staticssize_t hello_read(struct file * filep,char __user * buf,size_t size,loff_t* off)
{
/*
获取到了当前file,而后用结构体指针承接,实现修改
*/
struct hello_device * dev;
dev = filep->private_data;
//check param
if(size <0|| size >64)
return-EINVAL;
if(copy_to_user(buf, dev->data, size))
{
//为真,表示失败
printk("cp err\n");
return-1;
}
else{
return size;
}
}
staticssize_t hello_write(struct file * filep,constchar __user * buf,size_t size,loff_t* off)
{
struct hello_device * dev = filep->private_data;
//check param
if(size <0|| size >64)
return-EINVAL;
if(copy_from_user(dev->data, buf, size))
{
return-1;
}
else{
return size;
}
}
struct file_operations hello_fops ={
.owner = THIS_MODULE,
.open = hello_open,
.release = hello_release,
.read = hello_read,
.write = hello_write,
};
staticint cdev_setup(struct cdev * cdev,dev_t devno)
{
int ret;
cdev_init(cdev,&hello_fops);
cdev->owner = THIS_MODULE;
ret = cdev_add(cdev, devno,1);
if(ret <0)
return-1;
return0;
}
/* 模块加载函数,当向内核中插入这个模块的时候会被调用 */
int hello_init(void)
{
int ret;
//作初始化的动做
printk("-- %s called --\n", __FUNCTION__);
//1. 注册设备号
hello_dev[0].devno = MKDEV(major, minor);
hello_dev[1].devno = MKDEV(major, minor+1);
//注册来两个设备号,255 0 和255 1
ret = register_chrdev_region(hello_dev[0].devno, NUM_OF_DEV,"hello-device");
if(ret <0)
{
printk("register_chrdev_region err\n");
goto err1;
}
//2. cdev结构体插入内核链表
ret = cdev_setup(&hello_dev[0].mycdev, hello_dev[0].devno);
if(ret <0)
{
printk("cdev_add err\n");
goto err2;
}
ret = cdev_setup(&hello_dev[1].mycdev, hello_dev[1].devno);
if(ret <0)
{
printk("cdev_add err\n");
goto err3;
}
// 初始化用户数据
strcpy(hello_dev[0].data,"000000000000000000000000000000000000");
strcpy(hello_dev[1].data,"111111111111111111111111111111111111");
return0;
/*
这里是删除内核链表中的cdev 和 释放设备号,借助goto,巧妙释放所有
*/
err3:
cdev_del(&hello_dev[0].mycdev);
err2:
unregister_chrdev_region(hello_dev[0].devno, NUM_OF_DEV);
err1:
return-1;
}
/* 模块的卸载函数,当从内核中把本模块删除的时候被调用 */
void hello_exit(void)
{
//作和init_module相反的动做
printk("-- %s called --\n", __FUNCTION__);
cdev_del(&hello_dev[1].mycdev);
cdev_del(&hello_dev[0].mycdev);
unregister_chrdev_region(hello_dev[0].devno, NUM_OF_DEV);
return;
}
//声明hello_init为模块的加载函数
module_init(hello_init);
module_exit(hello_exit);
MODULE_AUTHOR("farsight");
MODULE_DESCRIPTION("This is a simple moudles");