国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學(xué)院 > 開發(fā)設(shè)計(jì) > 正文

Linux 字符設(shè)備驅(qū)動結(jié)構(gòu)(一)—— cdev 結(jié)構(gòu)體、設(shè)備號相關(guān)知識解析

2019-11-10 22:00:47
字體:
供稿:網(wǎng)友

一、字符設(shè)備基礎(chǔ)知識

1、設(shè)備驅(qū)動分類

      linux系統(tǒng)將設(shè)備分為3類:字符設(shè)備、塊設(shè)備、網(wǎng)絡(luò)設(shè)備。使用驅(qū)動程序:

字符設(shè)備:是指只能一個字節(jié)一個字節(jié)讀寫的設(shè)備,不能隨機(jī)讀取設(shè)備內(nèi)存中的某一數(shù)據(jù),讀取數(shù)據(jù)需要按照先后數(shù)據(jù)。字符設(shè)備是面向流的設(shè)備,常見的字符設(shè)備有鼠標(biāo)、鍵盤、串口、控制臺和LED設(shè)備等。

塊設(shè)備:是指可以從設(shè)備的任意位置讀取一定長度數(shù)據(jù)的設(shè)備。塊設(shè)備包括硬盤、磁盤、U盤和SD卡等。

每一個字符設(shè)備或塊設(shè)備都在/dev目錄下對應(yīng)一個設(shè)備文件linux用戶程序通過設(shè)備文件(或稱設(shè)備節(jié)點(diǎn))來使用驅(qū)動程序操作字符設(shè)備和塊設(shè)備

2、字符設(shè)備、字符設(shè)備驅(qū)動與用戶空間訪問該設(shè)備的程序三者之間的關(guān)系

     如圖,在Linux內(nèi)核中:

a -- 使用cdev結(jié)構(gòu)體來描述字符設(shè)備;

b -- 通過其成員dev_t來定義設(shè)備號(分為主、次設(shè)備號)以確定字符設(shè)備的唯一性;

c -- 通過其成員file_Operations來定義字符設(shè)備驅(qū)動提供給VFS的接口函數(shù),如常見的open()、read()、write()等;

     在Linux字符設(shè)備驅(qū)動中:

a -- 模塊加載函數(shù)通過 register_chrdev_region( ) 或 alloc_chrdev_region( )來靜態(tài)或者動態(tài)獲取設(shè)備號;

b -- 通過 cdev_init( ) 建立cdev與 file_operations之間的連接,通過 cdev_add( ) 向系統(tǒng)添加一個cdev以完成注冊;

c -- 模塊卸載函數(shù)通過cdev_del( )來注銷cdev,通過 unregister_chrdev_region( )來釋放設(shè)備號;

     用戶空間訪問該設(shè)備的程序:

a -- 通過Linux系統(tǒng)調(diào)用,如open( )、read( )、write( ),來“調(diào)用”file_operations來定義字符設(shè)備驅(qū)動提供給VFS的接口函數(shù);

3、字符設(shè)備驅(qū)動模型

二、cdev 結(jié)構(gòu)體解析

      在Linux內(nèi)核中,使用cdev結(jié)構(gòu)體來描述一個字符設(shè)備,cdev結(jié)構(gòu)體的定義如下:

[cpp] view plain copy 在CODE上查看代碼片<include/linux/cdev.h>    struct cdev {       struct kobject kobj;                  //內(nèi)嵌的內(nèi)核對象.      struct module *owner;                 //該字符設(shè)備所在的內(nèi)核模塊的對象指針.      const struct file_operations *ops;    //該結(jié)構(gòu)描述了字符設(shè)備所能實(shí)現(xiàn)的方法,是極為關(guān)鍵的一個結(jié)構(gòu)體.      struct list_head list;                //用來將已經(jīng)向內(nèi)核注冊的所有字符設(shè)備形成鏈表.      dev_t dev;                            //字符設(shè)備的設(shè)備號,由主設(shè)備號和次設(shè)備號構(gòu)成.      unsigned int count;                   //隸屬于同一主設(shè)備號的次設(shè)備號的個數(shù).  };  內(nèi)核給出的操作struct%20cdev結(jié)構(gòu)的接口主要有以下幾個:

a%20--%20void%20cdev_init(struct%20cdev%20*,%20const%20struct%20file_operations%20*);

其源代碼如代碼清單如下:

[cpp]%20view%20plain%20copy%20void cdev_init(struct cdev *cdev, const struct file_operations *fops)  {      memset(cdev, 0, sizeof *cdev);      INIT_LIST_HEAD(&cdev->list);      kobject_init(&cdev->kobj, &ktype_cdev_default);      cdev->ops = fops;  }   %20 %20 %20該函數(shù)主要對struct%20cdev結(jié)構(gòu)體做初始化最重要的就是建立cdev%20和%20file_operations之間的連接:

(1)%20將整個結(jié)構(gòu)體清零;

(2)%20初始化list成員使其指向自身;

(3)%20初始化kobj成員;

(4)%20初始化ops成員;

 b%20--struct%20cdev%20*cdev_alloc(void);

 %20 %20 該函數(shù)主要分配一個struct%20cdev結(jié)構(gòu)動態(tài)申請一個cdev內(nèi)存,并做了cdev_init中所做的前面3步初始化工作(第四步初始化工作需要在調(diào)用cdev_alloc后,顯式的做初始化即:%20.ops=xxx_ops).

其源代碼清單如下:

[cpp]%20view%20plain%20copy%20struct cdev *cdev_alloc(void)  {      struct cdev *p = kzalloc(sizeof(struct cdev), GFP_KERNEL);      if (p) {          INIT_LIST_HEAD(&p->list);          kobject_init(&p->kobj, &ktype_cdev_dynamic);      }      return p;  }  

 %20 %20 在上面的兩個初始化的函數(shù)中,我們沒有看到關(guān)于owner成員、dev成員、count成員的初始化;其實(shí),owner成員的存在體現(xiàn)了驅(qū)動程序與內(nèi)核模塊間的親密關(guān)系,struct%20module是內(nèi)核對于一個模塊的抽象,該成員在字符設(shè)備中可以體現(xiàn)該設(shè)備隸屬于哪個模塊,在驅(qū)動程序的編寫中一般由用戶顯式的初始化%20.owner%20=%20THIS_MODULE,%20該成員可以防止設(shè)備的方法正在被使用時,設(shè)備所在模塊被卸載。而dev成員和count成員則在cdev_add中才會賦上有效的值。

 c%20--%20int%20cdev_add(struct%20cdev%20*p,%20dev_t%20dev,%20unsigned%20count);

 %20 %20 %20 該函數(shù)向內(nèi)核注冊一個struct%20cdev結(jié)構(gòu),即正式通知內(nèi)核由struct%20cdev%20*p代表的字符設(shè)備已經(jīng)可以使用了。

當(dāng)然這里還需提供兩個參數(shù):

(1)第一個設(shè)備號%20dev,

(2)和該設(shè)備關(guān)聯(lián)的設(shè)備編號的數(shù)量。

這兩個參數(shù)直接賦值給struct%20cdev%20的dev成員和count成員。

d%20--%20void%20cdev_del(struct%20cdev%20*p);

 %20 %20 該函數(shù)向內(nèi)核注銷一個struct%20cdev結(jié)構(gòu),即正式通知內(nèi)核由struct%20cdev%20*p代表的字符設(shè)備已經(jīng)不可以使用了。

 %20 %20 從上述的接口討論中,我們發(fā)現(xiàn)對于struct%20cdev的初始化和注冊的過程中,我們需要提供幾個東西

(1)%20struct%20file_operations結(jié)構(gòu)指針;

(2)%20dev設(shè)備號;

(3)%20count次設(shè)備號個數(shù)。

但是我們依舊不明白這幾個值到底代表著什么,而我們又該如何去構(gòu)造這些值!

三、設(shè)備號相應(yīng)操作

1%20--%20主設(shè)備號和次設(shè)備號(二者一起為設(shè)備號):

 %20 %20 %20一個字符設(shè)備或塊設(shè)備都有一個主設(shè)備號和一個次設(shè)備號。主設(shè)備號用來標(biāo)識與設(shè)備文件相連的驅(qū)動程序,用來反映設(shè)備類型。次設(shè)備號被驅(qū)動程序用來辨別操作的是哪個設(shè)備,用來區(qū)分同類型的設(shè)備。

  linux內(nèi)核中,設(shè)備號用dev_t來描述,2.6.28中定義如下:

  typedef%20u_long%20dev_t;

  在32位機(jī)中是4個字節(jié),高12位表示主設(shè)備號,低20位表示次設(shè)備號。

內(nèi)核也為我們提供了幾個方便操作的宏實(shí)現(xiàn)dev_t:

1)%20-- 從設(shè)備號中提取major和minor

MAJOR(dev_t%20dev);                              

MINOR(dev_t%20dev);

2)%20-- 通過major和minor構(gòu)建設(shè)備號

MKDEV(int%20major,int%20minor);

注:這只是構(gòu)建設(shè)備號。并未注冊,需要調(diào)用 register_chrdev_region 靜態(tài)申請;

[cpp]%20view%20plain%20copy%20//宏定義:  #define MINORBITS    20  #define MINORMASK    ((1U << MINORBITS) - 1)  #define MAJOR(dev)    ((unsigned int) ((dev) >> MINORBITS))  #define MINOR(dev)    ((unsigned int) ((dev) & MINORMASK))  #define MKDEV(ma,mi)    (((ma) << MINORBITS) | (mi))</span>  

2、分配設(shè)備號(兩種方法):

a%20--%20靜態(tài)申請

int%20register_chrdev_region(dev_t%20from,%20unsigned%20count,%20const%20char%20*name);

其源代碼清單如下:

[cpp]%20view%20plain%20copy%20int register_chrdev_region(dev_t from, unsigned count, const char *name)  {      struct char_device_struct *cd;      dev_t to = from + count;      dev_t n, next;        for (n = from; n < to; n = next) {          next = MKDEV(MAJOR(n)+1, 0);          if (next > to)              next = to;          cd = __register_chrdev_region(MAJOR(n), MINOR(n),                     next - n, name);          if (IS_ERR(cd))              goto fail;      }      return 0;  fail:      to = n;      for (n = from; n < to; n = next) {          next = MKDEV(MAJOR(n)+1, 0);          kfree(__unregister_chrdev_region(MAJOR(n), MINOR(n), next - n));      }      return PTR_ERR(cd);  }  b%20--%20動態(tài)分配:

int%20alloc_chrdev_region(dev_t%20*dev,%20unsigned%20baseminor,%20unsigned%20count,%20const%20char%20*name);

其源代碼清單如下:

[cpp]%20view%20plain%20copy%20int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,              const char *name)  {      struct char_device_struct *cd;      cd = __register_chrdev_region(0, baseminor, count, name);      if (IS_ERR(cd))          return PTR_ERR(cd);      *dev = MKDEV(cd->major, cd->baseminor);      return 0;  }  

可以看到二者都是調(diào)用了__register_chrdev_region%20函數(shù),其源代碼如下:

[cpp]%20view%20plain%20copy%20static struct char_device_struct *  __register_chrdev_region(unsigned int major, unsigned int baseminor,                 int minorct, const char *name)  {      struct char_device_struct *cd, **cp;      int ret = 0;      int i;        cd = kzalloc(sizeof(struct char_device_struct), GFP_KERNEL);      if (cd == NULL)          return ERR_PTR(-ENOMEM);        mutex_lock(&chrdevs_lock);        /* temporary */      if (major == 0) {          for (i = ARRAY_SIZE(chrdevs)-1; i > 0; i--) {              if (chrdevs[i] == NULL)                  break;          }            if (i == 0) {              ret = -EBUSY;              goto out;          }          major = i;          ret = major;      }        cd->major = major;      cd->baseminor = baseminor;      cd->minorct = minorct;      strlcpy(cd->name, name, sizeof(cd->name));        i = major_to_index(major);        for (cp = &chrdevs[i]; *cp; cp = &(*cp)->next)          if ((*cp)->major > major ||              ((*cp)->major == major &&               (((*cp)->baseminor >= baseminor) ||                ((*cp)->baseminor + (*cp)->minorct > baseminor))))              break;        /* Check for overlapping minor ranges.  */      if (*cp && (*cp)->major == major) {          int old_min = (*cp)->baseminor;          int old_max = (*cp)->baseminor + (*cp)->minorct - 1;          int new_min = baseminor;          int new_max = baseminor + minorct - 1;            /* New driver overlaps from the left.  */          if (new_max >= old_min && new_max <= old_max) {              ret = -EBUSY;              goto out;          }            /* New driver overlaps from the right.  */          if (new_min <= old_max && new_min >= old_min) {              ret = -EBUSY;              goto out;          }      }        cd->next = *cp;      *cp = cd;      mutex_unlock(&chrdevs_lock);      return cd;  out:      mutex_unlock(&chrdevs_lock);      kfree(cd);      return ERR_PTR(ret);  }   通過這個函數(shù)可以看出 register_chrdev_region alloc_chrdev_region%20的區(qū)別,register_chrdev_region直接將Major%20注冊進(jìn)入,而 alloc_chrdev_region從Major%20=%200%20開始,逐個查找設(shè)備號,直到找到一個閑置的設(shè)備號,并將其注冊進(jìn)去;

二者應(yīng)用可以簡單總結(jié)如下:

 %20 %20 %20 %20 %20 %20 %20 %20 %20 %20 %20 %20 %20 %20 %20 %20 %20 %20 register_chrdev_region  %20 %20 %20 %20 %20 %20 %20 %20 %20 %20 %20 %20 %20 %20 %20 %20 %20 %20 %20 %20 %20 %20 %20 alloc_chrdev_region 

   %20devno = MKDEV(major,minor);    ret = register_chrdev_region(devno, 1, "hello");     cdev_init(&cdev,&hello_ops);    ret = cdev_add(&cdev,devno,1); %20  alloc_chrdev_region(&devno, minor, 1, "hello"); %20 %20major = MAJOR(devno); %20 %20cdev_init(&cdev,&hello_ops); %20 %20ret = cdev_add(&cdev,devno,1)register_chrdev(major,"hello",&hello

 %20 %20 可以看到,除了前面兩個函數(shù),還加了一個register_chrdev%20函數(shù),可以發(fā)現(xiàn)這個函數(shù)的應(yīng)用非常簡單,只要一句就可以搞定前面函數(shù)所做之事;

下面分析一下register_chrdev%20函數(shù),其源代碼定義如下:

[cpp]%20view%20plain%20copy%20static inline int register_chrdev(unsigned int major, const char *name,                    const struct file_operations *fops)  {      return __register_chrdev(major, 0, 256, name, fops);  }  調(diào)用了 __register_chrdev(major,%200,%20256,%20name,%20fops)%20函數(shù):[cpp]%20view%20plain%20copy%20int __register_chrdev(unsigned int major, unsigned int baseminor,                unsigned int count, const char *name,                const struct file_operations *fops)  {      struct char_device_struct *cd;      struct cdev *cdev;      int err = -ENOMEM;        cd = __register_chrdev_region(major, baseminor, count, name);      if (IS_ERR(cd))          return PTR_ERR(cd);        cdev = cdev_alloc();      if (!cdev)          goto out2;        cdev->owner = fops->owner;      cdev->ops = fops;      kobject_set_name(&cdev->kobj, "%s", name);        err = cdev_add(cdev, MKDEV(cd->major, baseminor), count);      if (err)          goto out;        cd->cdev = cdev;        return major ? 0 : cd->major;  out:      kobject_put(&cdev->kobj);  out2:      kfree(__unregister_chrdev_region(cd->major, baseminor, count));      return err;  }  可以看到這個函數(shù)不只幫我們注冊了設(shè)備號,還幫我們做了cdev%20的初始化以及cdev%20的注冊;

3、注銷設(shè)備號:

void%20unregister_chrdev_region(dev_t%20from,%20unsigned%20count);

4、創(chuàng)建設(shè)備文件:

 %20 %20 利用cat%20/#include <linux/module.h>  #include <linux/fs.h>  #include <linux/cdev.h>  static int major = 250;  static int minor = 0;  static dev_t devno;  static struct cdev cdev;  static int hello_open (struct inode *inode, struct file *filep)  {      printk("hello_open /n");      return 0;  }  static struct file_operations hello_ops=  {      .open = hello_open,           };    static int hello_init(void)  {      int ret;          printk("hello_init");      devno = MKDEV(major,minor);      ret = register_chrdev_region(devno, 1, "hello");      if(ret < 0)      {          printk("register_chrdev_region fail /n");          return ret;      }      cdev_init(&cdev,&hello_ops);      ret = cdev_add(&cdev,devno,1);      if(ret < 0)      {          printk("cdev_add fail /n");          return ret;      }         return 0;  }  static void hello_exit(void)  {      cdev_del(&cdev);      unregister_chrdev_region(devno,1);      printk("hello_exit /n");  }  MODULE_LICENSE("GPL");  module_init(hello_init);  module_exit(hello_exit);  

測試程序%20test.c

[cpp]%20view%20plain%20copy%20#include <sys/types.h>  #include <sys/stat.h>  #include <fcntl.h>  #include <stdio.h>    main()  {      int fd;        fd = open("/dev/hello",O_RDWR);      if(fd<0)      {          perror("open fail /n");          return ;      }        close(fd);  }  makefile:[cpp]%20view%20plain%20copy%20派生到我的代碼片ifneq  ($(KERNELRELEASE),)  obj-m:=hello.o  $(info "2nd")  else  KDIR := /lib/modules/$(shell uname -r)/build  PWD:=$(shell pwd)  all:      $(info "1st")      make -C $(KDIR) M=$(PWD) modules  clean:      rm -f *.ko *.o *.symvers *.mod.c *.mod.o *.order  endif  

編譯成功后,使用 insmod 命令加載:

然后用cat /proc/devices 查看,會發(fā)現(xiàn)設(shè)備號已經(jīng)申請成功;


上一篇:算法提高 01背包

下一篇:PAT甲級1040

發(fā)表評論 共有條評論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 庆安县| 闽侯县| 莱芜市| 凤台县| 龙岩市| 温宿县| 塘沽区| 阿图什市| 寿光市| 正定县| 赫章县| 博客| 枞阳县| 沁源县| 饶河县| 凤翔县| 贵州省| 恩施市| 芦山县| 洛隆县| 资源县| 尖扎县| 顺义区| 黄骅市| 天津市| 迭部县| 杂多县| 乌拉特中旗| 富宁县| 澳门| 遂川县| 绵阳市| 海门市| 富顺县| 海兴县| 谷城县| 奉贤区| 苗栗县| 莒南县| 太白县| 河曲县|