kernel module编程(四):设备属性和与上层应用的联系

奋斗吧
奋斗吧
擅长邻域:未填写

标签: kernel module编程(四):设备属性和与上层应用的联系 Python博客 51CTO博客

2023-05-08 18:24:13 32浏览

kernel module编程(四):设备属性和与上层应用的联系, 本文也即是《LinuxDeviceDrivers》一书第三章CharDrivers的读书笔记之二。 这部分开始有些觉得阴涩难懂。我上网去查,没能找到这本书的Example的例子,所以决定还是靠自己。我先写一个应用层的例子,通过这个例子来触发kernelmodule的一些操作,这样比较容易理解。#include<stdlib.h>#i


   本文也即是《Linux Device Drivers》一书第三章Char Drivers的读书笔记之二。

   这部分开始有些觉得阴涩难懂。我上网去查,没能找到这本书的Example的例子,所以决定还是靠自己。我先写一个应用层的例子,通过这个例子来触发kernel module的一些操作,这样比较容易理解。

#include <stdlib.h>
 #include <stdio.h>

 int main(int argc ,char * argv[])
 {
         FILE * file = NULL;

         printf("************** TEST ACCESS DRIVER**************/n");
         file = fopen("/dev/scull0","w");
         if(file == NULL){
                 printf("Open scull0 error!/n");
                 exit(-1);
         }

         fclose(file);
         return 0;
 }

  我们已经向成功请求分配了设备号码,一个内核模块,我们希望上层能够使用它的能力或者资源。有下面的重要的数据结构。

  struct file_operations,里面的参数处理strcut module外都是一些函数,这些函数和上层应用操作kernel的触发相关,内核模块可以提供不同的通信方式,例如BLOCK,NOBLOCK等等。应用某些函数的参数操作,触发内核模块对应函数的操作,例如测试效力中,fopen触发设备struct file_operations中open函数。文件操作,在上层应用的输入输出,包括socket中是经常使用的概念。例如fd:file descripor文件描述字。

  strcut file,给出设备的属性,例如是否可写等等。在用户触发时,kernel也会将该用户对设备的操作属性一起送下来。

  struct inode,在应用通过open或者其他方式,和内核模块建立连接后,他需要进一步操作,这是内核模块收到触发参数中一个重要的数据,他使得我们的内核模块可以详细对应具体使用的device。一般我们只关心dev_t i_rdev和struct cdev *i_cdev,这两个参数。

  继续在scull的例子。先解析一下scull对于内存空间的使用。如下图所示。

kernel module编程(四):设备属性和与上层应用的联系_数据结构

[wei@wei ~]$ cat scull.h
 #ifndef _WEI_SCULL_H
 #define _WEI_SCULL_H

 #define SCULL_MAJOR          0
 #define SCULL_MINOR_MIN 0
 #define SCULL_DEV_NUM    4 

/*这是根据内存组织情况给的链表结构*/ 
 struct scull_qset{
         void             ** data;
         struct scull_qset * next;
 };
/* 这是根据内存组织结构给的一个是Quantum的大小,一个是Data中具有多少个Quantum的队列长度*/ 
 #define SCULL_QUANTUM 1024
 #define SCULL_QSET      64

/*这是我们为内核模块对应的设备提供的结构,*/ 
 struct scull_dev {
         struct scull_qset     * data;
         int                           quantum;/* the quantum size */ 
         int                           qset;        /* the array size */ 

         struct  cdev            cdev;         /* Char device structure */ 
 };

 static void scull_setup_cdev(struct scull_dev * dev, int index);

 int scull_trim(struct scull_dev * dev);

 int scull_open(struct inode * inode , struct file * file);
 int scull_release(struct inode * inode , struct file * file);#endif


  在我们自定义的device结构中,最后一个参数struct cdev是个非常重要的参数,它是char device struct。通过下面三个系统函数去初始化它,将它和某个文件操作联系起来。将它加入kernel,以及从kernel中删除。

void cdev_init(struct cdev * cdev, strcut file_operations *fops);
 int  cdev_add (strcut cdev * dev, dev_t num, unsigned int count);
 void cdev_del (strcut cdev * dev);

  下面是scull.c,我们分析一下如何填写和使用上述三个重要的数据结构

[wei@wei ~]$ cat scull.c
 #include <linux/init.h>
 #include <linux/module.h> 
 #include <linux/fs.h>
 #include <linux/cdev.h>

 #include "scull.h"

MODULE_LICENSE("Dual BSD/GPL"); 

dev_t   dev;
 int     is_get_dev = -1; 

static int      scull_major = SCULL_MAJOR; 

/*这是文件操作结构,我们的四个scull0-3都具备同样的属性,应此可以对应到同一个数据结构中,请注意这个写法,我们在这里列出,使得程序更已读和便于修改。这在这个例子中,我们只是简单地进行打开和关闭的操作,所以我们只提供open和release两个函数映射*/ 
struct file_operations scull_fops  = {
         .owner          = THIS_MODULE,   /*这个参数不是一个操作,用于防止模块在操作过程中被卸载。*/ 
         .open           = scull_open,
         .release        = scull_release,  
 };

 /* strcut scull_dev是我们为每个设备设定的所有信息,四个设备放置在一个数组中 */ 
 struct scull_dev mydev[SCULL_DEV_NUM];

static int __init scull_init(void)
 {
         int i =0;

         printk("Scull module init enter/n");
         if(scull_major){
                 dev = MKDEV(scull_major,SCULL_MINOR_MIN);
                 is_get_dev = register_chrdev_region(dev, SCULL_DEV_NUM,"scull");
         }else{
                 is_get_dev = alloc_chrdev_region(&dev,SCULL_MINOR_MIN, SCULL_DEV_NUM,"scull");
                 scull_major = MAJOR(dev);
         }
         if(is_get_dev < 0){
                 printk(KERN_WARNING "scull: can't get device major number %d/n",scull_major);
                 return is_get_dev;
         } 

     /* 为这四个设备分别进行初始化 */ 
         for(i = 0 ; i < SCULL_DEV_NUM; i ++){
                 scull_setup_cdev(&mydev[i],i);
         }
        return 0;
 } 

static void __exit scull_exit(void)
 {
         if(is_get_dev < 0){
                 return ;
         }else{
                 int i = 0; 
                 /* 对于任何进行了cdev_add操作的设备,应该在卸载模块的时候进行cdev_del,将它从kernel中删除。*/ 
                 for(i = 0; i < SCULL_DEV_NUM; i ++){
                         cdev_del( & mydev[i].cdev  ); 
                 }
                 unregister_chrdev_region(dev,SCULL_DEV_NUM);
                 printk("Scull module exit/n");
         }
 } 

module_init(scull_init);
 module_exit(scull_exit); 

/* 根据我们为设备定义的结构,进行初始化*/ 
 static void scull_setup_cdev(struct scull_dev * dev, int index)
 {
         int err;
         /* 通过第二个参数index,我们可以知道具体是那个scullx,可以对应到相应的设备上。*/ 
         int devno = MKDEV(scull_major,SCULL_MINOR_MIN + index);
         printk("scull%d %d,%d is %d/n", index,scull_major, SCULL_MINOR_MIN + index, devno);/*进行char device的初始化,和file operation相关联,设置其属性,并将通过对应设备将其加入kernel中*/  
         cdev_init (& dev->cdev, & scull_fops ); 
         dev->cdev.owner = THIS_MODULE;
         dev->cdev.ops   = & scull_fops;  
         err = cdev_add(&dev->cdev,devno,1 ); 
         if(err){
                 printk(KERN_NOTICE "scull : Err %d adding scull %d/n", err,index);
         }
 }

/*当用户程序打开设备是触发,例如测试小程序中的fopen(),kernel将送来inode和该用户的文件属性,不同用户有不同的操作权限。*/ 
 int scull_open(struct inode * inode ,struct file *filp)
 {
         struct scull_dev * dev;
         
        /*这里给出了通过inode获得对应设备的方式,第一种通过获取设备号码来进行检索,第二中是个很有趣的函数contianer_of,据ldd3这本书说这是kernel hackers提供的。container_of(pointer,container_type,container_field),则是太方便了。方便得有些怀疑是否保险。*/ 
         printk("scull_open is called, node %d %d/n",imajor(inode ) , iminor(inode ) );
         dev = container_of(inode->i_cdev,struct scull_dev,cdev ); 
        。 */ 
         filp->private_data = dev;
         
         /* 查看一下用户权限。我们可以在fopen中使用"r"和"w"分别查看。如果给出的是只写操作,将清空原来的所有数据(scull_trim())。*/ 
         printk("scull: open mode is %x/n",filp->f_mode);
         if((filp->f_flags & O_ACCMODE) == O_WRONLY){
                 printk("scull: only write: free all data/n");
                 scull_trim(dev);
         }

         return 0;
 }
/* 我们在用户程序fclose()时调用。*/ 
 int scull_release(struct inode * inode, struct file * filp)
 {       printk("scull: release is called./n");  
      return 0;
 }

/* 清空所有数据 */ 
 int scull_trim(struct scull_dev * dev)
 {
         struct scull_qset * next ,*dptr;
         int qset = dev-> qset;
         int i ;

         for (dptr = dev->data; dptr != NULL; dptr = next){
                 if(dptr->data){
                         for(i = 0; i < qset; i ++)
                                 kfree(dptr->data[i]);
                         kfree(dptr->data);
                         dptr->data = NULL;
                 }
                 next = dptr->next;
                 kfree(dptr);
         }
         dev->size = 0;
         dev->quantum = SCULL_QUANTUM;
         dev->qset = SCULL_QSET;
         dev->data = NULL;
         return 0;
 }


好博客就要一起分享哦!分享海报

此处可发布评论

评论(0展开评论

暂无评论,快来写一下吧

展开评论

您可能感兴趣的博客

客服QQ 1913284695