應用層如何內核.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");