麻豆小视频在线观看_中文黄色一级片_久久久成人精品_成片免费观看视频大全_午夜精品久久久久久久99热浪潮_成人一区二区三区四区

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

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

2019-11-10 19:18:11
字體:
來(lái)源:轉(zhuǎn)載
供稿:網(wǎng)友

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

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

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

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

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

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

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

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

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

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

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

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

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

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

c -- 模塊卸載函數(shù)通過(guò)cdev_del( )來(lái)注銷cdev,通過(guò) unregister_chrdev_region( )來(lái)釋放設(shè)備號(hào);

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

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

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

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

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

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

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ù)主要對(duì)struct%20cdev結(jié)構(gòu)體做初始化最重要的就是建立cdev%20和%20file_operations之間的連接:

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

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

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

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

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

 %20 %20 該函數(shù)主要分配一個(gè)struct%20cdev結(jié)構(gòu)動(dòng)態(tài)申請(qǐng)一個(gè)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 在上面的兩個(gè)初始化的函數(shù)中,我們沒(méi)有看到關(guān)于owner成員、dev成員、count成員的初始化;其實(shí),owner成員的存在體現(xiàn)了驅(qū)動(dòng)程序與內(nèi)核模塊間的親密關(guān)系,struct%20module是內(nèi)核對(duì)于一個(gè)模塊的抽象,該成員在字符設(shè)備中可以體現(xiàn)該設(shè)備隸屬于哪個(gè)模塊,在驅(qū)動(dòng)程序的編寫中一般由用戶顯式的初始化%20.owner%20=%20THIS_MODULE,%20該成員可以防止設(shè)備的方法正在被使用時(shí),設(shè)備所在模塊被卸載。而dev成員和count成員則在cdev_add中才會(huì)賦上有效的值。

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

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

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

(1)第一個(gè)設(shè)備號(hào)%20dev,

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

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

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

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

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

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

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

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

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

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

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

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

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

  typedef%20u_long%20dev_t;

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

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

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

MAJOR(dev_t%20dev);                              

MINOR(dev_t%20dev);

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

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

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

[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è)備號(hào)(兩種方法):

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

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動(dòng)態(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);  }   通過(guò)這個(gè)函數(shù)可以看出 register_chrdev_region alloc_chrdev_region%20的區(qū)別,register_chrdev_region直接將Major%20注冊(cè)進(jìn)入,而 alloc_chrdev_region從Major%20=%200%20開始,逐個(gè)查找設(shè)備號(hào),直到找到一個(gè)閑置的設(shè)備號(hào),并將其注冊(cè)進(jìn)去;

二者應(yīng)用可以簡(jiǎn)單總結(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 可以看到,除了前面兩個(gè)函數(shù),還加了一個(gè)register_chrdev%20函數(shù),可以發(fā)現(xiàn)這個(gè)函數(shù)的應(yīng)用非常簡(jiǎn)單,只要一句就可以搞定前面函數(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;  }  可以看到這個(gè)函數(shù)不只幫我們注冊(cè)了設(shè)備號(hào),還幫我們做了cdev%20的初始化以及cdev%20的注冊(cè);

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

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);  

測(cè)試程序%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 查看,會(huì)發(fā)現(xiàn)設(shè)備號(hào)已經(jīng)申請(qǐng)成功;


發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 国产毛片在线看 | 中文在线日韩 | 久久久久一区 | 免费在线观看亚洲 | 欧产日产国产精品乱噜噜 | 91亚洲免费视频 | 亚洲一区二区三区精品在线观看 | 色网站免费观看 | 欧美成人精品 | 亚洲91网 | 国产精品久久久久久久久粉嫩 | 轻点插视频 | 久久久久久久久亚洲精品 | 日韩字幕在线 | 黄色大片在线观看 | 国产一区视频观看 | 少妇的肉体k8经典 | 国产日韩在线观看一区 | 国产精品99久久久久久久女警 | 成人羞羞在线观看网站 | 2019亚洲日韩新视频 | 伊人亚洲精品 | 国产精品视频一区二区噜噜 | 免费高潮在线国 | 日本a级免费 | 国产精品视频久 | 伊久在线| 免费激情网址 | 少妇一级淫片免费放正片 | a一级黄色毛片 | 欧美在线成人影院 | h视频免费观看 | 国产在线精品一区二区不卡 | 免费视频a | 亚欧美一区二区 | 操网| 欧美亚洲黄色片 | 99激情| 久久精品国产清自在天天线 | 精品在线视频播放 | 久久国产28|