一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

Linux設(shè)備管理(二):內(nèi)核中字符設(shè)備的管理

 懶人看書館 2021-02-06

 

正文

/************************************************************************************

*本文為個人學(xué)習(xí)記錄,如有錯誤,歡迎指正。

*本文參考資料: 

*        https://www.cnblogs.com/embedded-tzp/p/4507240.html

*        http://www./tech-qa-linux/article-5682294992603241339.html

*        https://blog.csdn.net/zhoujiaxq/article/details/7646013

*        http://www.cnblogs.com/xiaojiang1025/p/6196198.html

************************************************************************************/

1. 字符設(shè)備的管理框架

Linux內(nèi)核對設(shè)備的管理是基于kobject來進(jìn)行的,詳見Linux設(shè)備管理:kobject, kset, ktype分析。Linux對字符設(shè)備的管理框架依賴于struct kobj_map、struct cdev、dev_t dev、struct file_operations等數(shù)據(jù)結(jié)構(gòu)。如下圖所示。

2. 字符設(shè)備數(shù)據(jù)結(jié)構(gòu)

 Linux內(nèi)核中關(guān)于字符設(shè)備的操作函數(shù)存放在 "/kernel/fs/char_dev.c" 文件中。

2.1 dev_t dev

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

Linux內(nèi)核中,使用dev_t來描述設(shè)備號。

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

Linux內(nèi)核中提供以下幾個宏來操作dev_t。

#define MAJOR(dev)    ((unsigned int) ((dev) >> MINORBITS))  // 從設(shè)備號中提取主設(shè)備號
#define MINOR(dev)    ((unsigned int) ((dev) & MINORMASK))   // 從設(shè)備號中提取次設(shè)備號
#define MKDEV(ma,mi)    (((ma) << MINORBITS) | (mi))         // 將主、次設(shè)備號拼湊為設(shè)備號

2.2 struct cdev

Linux內(nèi)核中,使用struct cdev結(jié)構(gòu)體來描述一個字符設(shè)備。

復(fù)制代碼
<include/linux/cdev.h>  

struct cdev 
{   
  struct kobject kobj;              //內(nèi)嵌的內(nèi)核對象 
  struct module *owner;             //該字符設(shè)備所在的內(nèi)核模塊(所有者)的對象指針,一般為THIS_MODULE,主要用于模塊計數(shù)  
  const struct file_operations *ops;//該結(jié)構(gòu)描述了字符設(shè)備所能實現(xiàn)的操作集(打開、關(guān)閉、讀/寫、...),是極為關(guān)鍵的一個結(jié)構(gòu)體
  struct list_head list;            //用來將已經(jīng)向內(nèi)核注冊的所有字符設(shè)備形成鏈表
  dev_t dev;                        //字符設(shè)備的設(shè)備號,由主設(shè)備號和次設(shè)備號構(gòu)成(如果是一次申請多個設(shè)備號,此設(shè)備號為第一個)
  unsigned int count;               //隸屬于同一主設(shè)備號的次設(shè)備號的個數(shù)
};
復(fù)制代碼

2.3 struct file_operations

Linux內(nèi)核中,使用file_operations結(jié)構(gòu)來管理設(shè)備驅(qū)動程序的函數(shù),這個結(jié)構(gòu)的每一個成員的名字都對應(yīng)著一個函數(shù)調(diào)用。

用戶進(jìn)程利用在對設(shè)備文件進(jìn)行操作時(read/write等),系統(tǒng)調(diào)用通過設(shè)備文件的主設(shè)備號找到相應(yīng)的設(shè)備驅(qū)動程序,然后讀取其file_operations結(jié)構(gòu)相應(yīng)的函數(shù)指針,接著把控制權(quán)交給該函數(shù),這是Linux的設(shè)備驅(qū)動程序工作的基本原理。

復(fù)制代碼
struct file_operations 
{
  struct module *owner;  
    /* 模塊擁有者,一般為 THIS——MODULE */
  ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);  
    /* 從設(shè)備中讀取數(shù)據(jù),成功時返回讀取的字節(jié)數(shù),出錯返回負(fù)值(絕對值是錯誤碼) */
  ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);   
    /* 向設(shè)備發(fā)送數(shù)據(jù),成功時該函數(shù)返回寫入字節(jié)數(shù)。若為被實現(xiàn),用戶調(diào)層用write()時系統(tǒng)將返回 -EINVAL*/
  int (*mmap) (struct file *, struct vm_area_struct *);  
    /* 將設(shè)備內(nèi)存映射內(nèi)核空間進(jìn)程內(nèi)存中,若未實現(xiàn),用戶層調(diào)用 mmap()系統(tǒng)將返回 -ENODEV */
  long (*unlocked_ioctl)(struct file *filp, unsigned int cmd, unsigned long arg);  
    /* 提供設(shè)備相關(guān)控制命令(讀寫設(shè)備參數(shù)、狀態(tài),控制設(shè)備進(jìn)行讀寫...)的實現(xiàn),當(dāng)調(diào)用成功時返回一個非負(fù)值 */
  int (*open) (struct inode *, struct file *);  
    /* 打開設(shè)備 */
  int (*release) (struct inode *, struct file *);  
    /* 關(guān)閉設(shè)備 */
  int (*flush) (struct file *, fl_owner_t id);  
    /* 刷新設(shè)備 */
  loff_t (*llseek) (struct file *, loff_t, int);  
    /* 用來修改文件讀寫位置,并將新位置返回,出錯時返回一個負(fù)值 */
  int (*fasync) (int, struct file *, int);  
    /* 通知設(shè)備 FASYNC 標(biāo)志發(fā)生變化 */
  unsigned int (*poll) (struct file *, struct poll_table_struct *);  
    /* POLL機(jī)制,用于詢問設(shè)備是否可以被非阻塞地立即讀寫。當(dāng)詢問的條件未被觸發(fā)時,用戶空間進(jìn)行select()和poll()系統(tǒng)調(diào)用將引起進(jìn)程阻塞 */
};
復(fù)制代碼

2.4 struct kobj_map

Linux內(nèi)核中,所有的字符設(shè)備都會記錄在一個cdev_map 變量中。cdev_map是一個struct kobj_map類型的指針,其中包含著一個struct probe*類型、大小為255的數(shù)組,數(shù)組的每個元素指向的一個probe結(jié)構(gòu)封裝了一個設(shè)備號和相應(yīng)的設(shè)備對象(cdev)。

struct kobj_map

字符設(shè)備驅(qū)動程序通過調(diào)用cdev_add把它所管理的字符設(shè)備對象的指針嵌入到一個類型為struct probe的節(jié)點之中,然后再把該節(jié)點加入到cdev_map所實現(xiàn)的哈希鏈表中。對系統(tǒng)而言,當(dāng)設(shè)備驅(qū)動程序成功調(diào)用了cdev_add之后,就意味著一個字符設(shè)備對象已經(jīng)加入到了系統(tǒng),在需要的時候,系統(tǒng)就可以找到它。對用戶態(tài)的程序而言,cdev_add調(diào)用之后,就已經(jīng)可以通過文件系統(tǒng)的接口調(diào)用該設(shè)備的驅(qū)動程序(具體調(diào)用流程詳見Linux字符設(shè)備:應(yīng)用程序調(diào)用字符設(shè)備驅(qū)動程序的流程)。

復(fù)制代碼
int cdev_add(struct cdev *p, dev_t dev, unsigned count)
{
    p->dev = dev;
    p->count = count;

   /*申請并填充struct probe,再通過要加入系統(tǒng)的設(shè)備的主設(shè)備號major(major=MAJOR(dev))來獲得probes數(shù)組的索引值i(i = major % 255),
     然后把一個類型為struct probe的節(jié)點對象加入到probes[i]所管理的鏈表中*/ 
    return kobj_map(cdev_map, dev, count, NULL, exact_match, exact_lock, p);
}
復(fù)制代碼

cdev_map對字符設(shè)備的管理方式有兩種:

(1)一個cdev對象對應(yīng)這一個/多個設(shè)備號的情況

在cdev_map中, 一個probes對象就對應(yīng)一個主設(shè)備號;多個設(shè)備號對應(yīng)一個cdev時,其實只是次設(shè)備號在變,主設(shè)備號還是一樣的,所以是同一個probes對象。

(2)主設(shè)備號超過255的情況

當(dāng)主設(shè)備號超過255時,會進(jìn)行probe復(fù)用,此時probe->next就派上了用場,比如probe[200]可以表示設(shè)備號200,455...3895等所有對255取余是200的數(shù)字。

 

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多

    国产毛片对白精品看片| 日本人妻丰满熟妇久久| 成人免费观看视频免费| 国产三级黄片在线免费看 | 中文字幕人妻日本一区二区 | 最好看的人妻中文字幕| 日本不卡在线一区二区三区| 久久福利视频在线观看| 日韩精品一区二区不卡| 久久香蕉综合网精品视频| 欧美字幕一区二区三区| 熟妇人妻av中文字幕老熟妇| 成人精品亚洲欧美日韩| 搡老熟女老女人一区二区| 欧美一区二区三区99| 亚洲精品深夜福利视频| 丝袜视频日本成人午夜视频| 欧洲精品一区二区三区四区| 亚洲高清一区二区高清| 区一区二区三中文字幕| 欧美精品中文字幕亚洲| 日本加勒比中文在线观看| 九九久久精品久久久精品| 黄片免费播放一区二区| 国产精品日韩精品一区| 黄片免费观看一区二区| 久久99夜色精品噜噜亚洲av| 日本黄色高清视频久久| 国产三级欧美三级日韩三级| 国产在线小视频你懂的| 日本黄色高清视频久久| 欧美尤物在线视频91| 国产精品熟女在线视频| 91精品国产综合久久福利| 亚洲精品黄色片中文字幕| 欧美不雅视频午夜福利| 丰满人妻一二三区av| 免费大片黄在线观看国语| 激情国产白嫩美女在线观看| 精品欧美日韩一区二区三区 | 国产亚洲欧美日韩精品一区|