1. 程式人生 > >linux一切裝置皆檔案的實現(二)

linux一切裝置皆檔案的實現(二)

首先struct device是裝置模型中的概念,這個結構體中儲存的是具體板子上裝置的資訊,比如基暫存器地址,暫存器範圍,終端號等等,目的就是使驅動與具體的板子硬體連線剝離,使驅動更具有通用性和移植性,這樣不同板子上驅動都不需要改變,struct device註冊裝置放在架構相關的程式碼中硬編碼或者使用device tree。struct cdev是一個使用者介面相關的結構體,linux上一切裝置皆檔案,通過開啟裝置檔案就可以操作相對應的裝置,這個實現的具體過程為:
  1. 通過register_chrdev()函式註冊字元裝置struct cdev, struct cdev中保護kobject機構體,註冊的過程就是讓major 裝置號與struct cdev中的kobject建立對映關係(kobj_map(cdev_map, dev,...))。
  2. 在開啟裝置檔案時,通過儲存在磁碟中的inode中的i_rdev取得裝置檔案對應的裝置號,裝置號包括major(12bit)和minor(20bit),通過major取得kobject(register_chrdev已經建立好的kobject和major對映關係),container_of()可以取得struct cdev。
  3. 填充開啟的struct file的操作方法(const struct file_operations *ops)指向struct cdev中定義好的方法,這樣通struct file就可以直接操作到對應的裝置了。
那麼struct cdev和struct device怎麼對應起來呢顯然struct cdev中的方法操作的是具體的裝置,所以肯定是要找到struct device的,具體怎麼聯絡起來這個沒有統一的方法,在驅動程式碼中可以靈活實現。下面以spidev為例進行說明:static int __init spidev_init(void){    ...
   status = register_chrdev(SPIDEV_MAJOR
, "spi", &spidev_fops);    status = spi_register_driver(&spidev_spi_driver);}static const struct file_operations spidev_fops = {    .owner =    THIS_MODULE,    /* REVISIT switch to aio primitives, so that userspace     * gets more complete API coverage.  It'll simplify things     * too, except for the locking.     */    .write =    spidev_write,    .read =        spidev_read,    .unlocked_ioctl = spidev_ioctl,    .compat_ioctl = spidev_compat_ioctl,    .open =        spidev_open,    .release =    spidev_release,    .llseek =    no_llseek,};static struct spi_driver spidev_spi_driver = {    .driver = {        .name =        "spidev",        .owner =    THIS_MODULE,    },    .probe =    spidev_probe,    .remove =    __devexit_p(spidev_remove),};static int __devinit spidev_probe(struct spi_device
*spi)          //spi_device包含struct device{    struct spidev_data    *spidev;    int            status;    unsigned long        minor;    /* Allocate driver data */    spidev = kzalloc(sizeof(*spidev), GFP_KERNEL);    if (!spidev)        return -ENOMEM;    /* Initialize the driver data */    spidev->spi = spi;            spin_lock_init(&spidev->spi_lock);    mutex_init(&spidev->buf_lock);    INIT_LIST_HEAD(&spidev->device_entry);    /* If we can allocate a minor number, hook up this device.     * Reusing minors is fine so long as udev or mdev is working.     */    mutex_lock(&device_list_lock);    minor = find_first_zero_bit(minors, N_SPI_MINORS);    if (minor < N_SPI_MINORS) {        struct device *dev;        spidev->devt = MKDEV(SPIDEV_MAJOR, minor);        dev = device_create(spidev_class, &spi->dev, spidev->devt,                    spidev, "spidev%d.%d",                    spi->master->bus_num, spi->chip_select);        status = IS_ERR(dev) ? PTR_ERR(dev) : 0;    } else {        dev_dbg(&spi->dev, "no minor number available!\n");        status = -ENODEV;    }    if (status == 0) {        set_bit(minor, minors);        list_add(&spidev->device_entry, &device_list);       //放入device_list連結串列中    }    mutex_unlock(&device_list_lock);    if (status == 0)        spi_set_drvdata(spi, spidev);    else        kfree(spidev);    return status;}static int spidev_open(struct inode *inode, struct file *filp){    struct spidev_data    *spidev;    int            status = -ENXIO;    mutex_lock(&device_list_lock);    list_for_each_entry(spidev, &device_list, device_entry) {        if (spidev->devt == inode->i_rdev) {    //找到對應的裝置            status = 0;            break;        }    }    if (status == 0) {        if (!spidev->buffer) {            spidev->buffer = kmalloc(bufsiz, GFP_KERNEL);            if (!spidev->buffer) {                dev_dbg(&spidev->spi->dev, "open/ENOMEM\n");                status = -ENOMEM;            }        }        if (status == 0) {            spidev->users++;            filp->private_data = spidev;        //儲存裝置操作的資訊            nonseekable_open(inode, filp);        }    } else        pr_debug("spidev: nothing for minor %d\n", iminor(inode));    mutex_unlock(&device_list_lock);    return status;}對於塊裝置, struct block_device和struct cdev的作用相似,但是這個對映過程複雜了不少,struct block_device表示一個塊裝置,可以是一個分割槽或者整個磁碟, struct gendisk表示整個磁碟:struct block_device {    dev_t            bd_dev;  /* not a kdev_t - it's a search key */    int            bd_openers;    struct inode *        bd_inode;    /* will die */    struct super_block *    bd_super;    struct list_head    bd_inodes;    void *            bd_claiming;    void *            bd_holder;    int            bd_holders;    bool            bd_write_holder;    struct block_device *    bd_contains;    unsigned        bd_block_size;    struct hd_struct *    bd_part;    unsigned        bd_part_count;    int            bd_invalidated;    struct gendisk *    bd_disk;    struct request_queue *  bd_queue;    struct list_head    bd_list;    unsigned long        bd_private;    ...
}struct gendisk {    int major;            /* major number of driver */    int first_minor;    int minors;                     /* maximum number of minors, =1 for                                         * disks that can't be partitioned. */    char disk_name[DISK_NAME_LEN];    /* name of major driver */    char *(*devnode)(struct gendisk *gd, umode_t *mode);    struct disk_part_tbl __rcu *part_tbl;    struct hd_struct part0;             //描述整個磁碟    const struct block_device_operations *fops;             //操作方法    struct request_queue *queue;    void *private_data;    ...
}struct hd_struct {    sector_t start_sect;    sector_t nr_sects;    sector_t alignment_offset;    unsigned int discard_alignment;    struct device __dev;    struct kobject *holder_dir;    int policy, partno;    struct partition_meta_info *info;    ...
}
首先來看看塊裝置的註冊過程:
  1. 驅動呼叫add_disk()來註冊整個磁碟
void add_disk(struct gendisk *disk){    struct backing_dev_info *bdi;    dev_t devt;    int retval;    disk->flags |= GENHD_FL_UP;        retval = blk_alloc_devt(&disk->part0, &devt);         //分配裝置號    disk_to_dev(disk)->devt = devt;          //儲存裝置號    /* ->major and ->first_minor aren't supposed to be     * dereferenced from here on, but set them just in case.     */    disk->major = MAJOR(devt);    disk->first_minor = MINOR(devt);    ...
    blk_register_region(disk_devt(disk), disk->minors, NULL,                exact_match, exact_lock, disk);      //通過struct hd_struct中的struct device中的kobject與裝置號建立對映,這也就是說struct gendisk和裝置號之間建立了對映關係    register_disk(disk);    blk_register_queue(disk);    ...
}void blk_register_region(dev_t devt, unsigned long range, struct module *module,             struct kobject *(*probe)(dev_t, int *, void *),             int (*lock)(dev_t, void *), void *data){    kobj_map(bdev_map, devt, range, module, probe, lock, data);}static void register_disk(struct gendisk *disk){    struct device *ddev = disk_to_dev(disk);    struct block_device *bdev;    struct disk_part_iter piter;    struct hd_struct *part;    int err;    ddev->parent = disk->driverfs_dev;    dev_set_name(ddev, disk->disk_name);    if (device_add(ddev))        return;    ...    /* No minors to use for partitions */    if (!disk_part_scan_enabled(disk))     //判斷是否要掃描磁碟上的分割槽        goto exit;    /* No such device (e.g., media were just removed) */    if (!get_capacity(disk))        goto exit;    bdev = bdget_disk(disk, 0);       //建立整個磁碟對應的struct block_device    if (!bdev)        goto exit;    bdev->bd_invalidated = 1;    err = blkdev_get(bdev, FMODE_READ, NULL);    if (err < 0)        goto exit;    blkdev_put(bdev, FMODE_READ);exit:    /* announce disk after possible partitions are created */    dev_set_uevent_suppress(ddev, 0);    kobject_uevent(&ddev->kobj, KOBJ_ADD);    /* announce possible partitions */    disk_part_iter_init(&piter, disk, 0);    while ((part = disk_part_iter_next(&piter)))        kobject_uevent(&part_to_dev(part)->kobj, KOBJ_ADD);    disk_part_iter_exit(&piter);}struct block_device *bdget_disk(struct gendisk *disk, int partno){    struct hd_struct *part;    struct block_device *bdev = NULL;    part = disk_get_part(disk, partno);    if (part)        bdev = bdget(part_devt(part));   //首次呼叫時,建立裝置號與bdev檔案系統中的indoe建立對映關係,首次呼叫分配bdev_inode結構體,也就是說同時分配了 struct block_device, 通過inode可以得到struct block_device,這也就是說裝置號和struct block_device之間也建立了對映關係    disk_put_part(part);    return bdev;}int blkdev_get(struct block_device *bdev, fmode_t mode, void *holder){    ...
    res = __blkdev_get(bdev, mode, 0);    ...
}static int __blkdev_get(struct block_device *bdev, fmode_t mode, int for_part){        ...
        disk = get_gendisk(bdev->bd_dev, &partno); //通過之前建立好的對映關係,由裝置號得到struct gendisk        bdev->bd_disk = disk;        if (!partno) {        ...
        rescan_partitions(disk, bdev);      //掃描分割槽    }
    ...
}struct block_device *bdget(dev_t dev){    struct block_device *bdev;    struct inode *inode;    inode = iget5_locked(blockdev_superblock, hash(dev),            bdev_test, bdev_set, &dev);    if (!inode)        return NULL;    bdev = &BDEV_I(inode)->bdev;    if (inode->i_state & I_NEW) {        ...
        inode->i_mode = S_IFBLK;        inode->i_rdev = dev;     //裝置號與inode之間建立對映        inode->i_bdev = bdev;        ...
    }    return bdev;}struct inode *iget5_locked(struct super_block *sb, unsigned long hashval,        int (*test)(struct inode *, void *),        int (*set)(struct inode *, void *), void *data){    struct hlist_head *head = inode_hashtable + hash(sb, hashval);    struct inode *inode;    spin_lock(&inode_hash_lock);    inode = find_inode(sb, head, test, data);    spin_unlock(&inode_hash_lock);    if (inode) {        wait_on_inode(inode);        return inode;    }    inode = alloc_inode(sb);    //首次呼叫時分配bdev_inode結構體    if (inode) {        ...
       if (set(inode, data))        ...
    }    return inode;set_failed:    spin_unlock(&inode_hash_lock);    destroy_inode(inode);    return NULL;}static int bdev_set(struct inode *inode, void *data){    BDEV_I(inode)->bdev.bd_dev = *(dev_t *)data;     //儲存裝置號    return 0;}struct bdev_inode {          struct block_device bdev;    struct inode vfs_inode;};struct gendisk *get_gendisk(dev_t devt, int *partno){    struct gendisk *disk = NULL;    kobj = kobj_lookup(bdev_map, devt, partno);        if (kobj)
            disk = dev_to_disk(kobj_to_dev(kobj));}
  1. open開啟裝置檔案, 通過儲存在inode中的i_rdev在bdev檔案系統中進行查詢,找到inode,然後通過inode找到block_device,得到gendisk,gendisk存放著操作方法,覆蓋struct file的操作方法,這樣就可以操作裝置了。
(kobj_map(bdev_map, devt, ...))
通過上面的分析可以看出,在設備註冊過程中首先建立起裝置號和裝置操作方法間的對映,然後通過裝置檔案中的裝置號得到操作方法覆蓋掉普通檔案操作方法,這樣就可以實現裝置的訪問了。

相關推薦

linux一切裝置檔案實現

首先struct device是裝置模型中的概念,這個結構體中儲存的是具體板子上裝置的資訊,比如基暫存器地址,暫存器範圍,終端號等等,目的就是使驅動與具體的板子硬體連線剝離,使驅動更具有通用性和移植性,這樣不同板子上驅動都不需要改變,struct device註冊裝置放在架構

Linux 核心程式設計之檔案系統

1.為了方便查詢,VFS引入了 目錄 項,每個dentry代表路徑中的一個特定部分。目錄項也可包括安裝點。 2.目錄項物件由dentry結構體表示 ,定義在檔案linux/dcache.h 標頭檔案中。   89struct dentry {  90        atomic_t d_count;     

Linux 檔案系統與裝置檔案系統 —— sysfs 檔案系統與Linux裝置模型

      提到 sysfs 檔案系統 ,必須先需要了解的是Linux裝置模型,什麼是Linux裝置模型呢? 一、Linux 裝置模型 1、裝置模型概述      從2.6版本開始,Linux開發團隊便為核心建立起一個統一的裝置模型。在以前的核心中沒有獨立的資料結構用來讓核

linux系統 使用者身份與檔案許可權

0.檔案的許可權與歸屬     儘管在Linux系統中一切都是檔案,但是每個檔案的型別不盡相同,因此Linux系統使用了不同的字元來加以區分,常見的字元如下所示。  -:普通檔案 d:目錄檔案 l:連結檔案 b:塊裝置檔案 c:字元

基於IE核心的多媒體檔案視覺化程式實現

文章目錄 使用命令列引數 命令列引數獲取和解析程式碼 幫助系統 執行測試 WSEE的功能還不只如此 現在的結果 上回說道,我們需要使用命令列引數方法將要瀏覽的檔名傳入wsee.exe 程式,然後由

一個簡單的BitTorrent客戶端實現:種子檔案解析及資訊儲存

關於種子檔案 BT的種子檔案一般是以.torrent作為字尾的。關於種子檔案的編碼,這裡不再做任何介紹。本程式採用的測試種子檔案為ubuntu-14.04.3-desktop-i386.torrent,各位可以到http://mirrors.163.com/u

基於Huffman樹的檔案壓縮原理及C語言實現

在上文基於Huffman樹的檔案壓縮原理及C語言實現(一)中已經介紹並實現瞭如何建立Huffman樹,得到Huffman編碼,這篇我們將會接著說。如何通過Huffman樹,實現檔案壓縮。 實現思路 任何檔案都可以看作是由位元組組成的位元組塊,將位元組看作

例說linux核心與應用資料通訊:proc虛擬檔案系統

下面是一個簡單使用proc的示例,應用上面傳入資料,核心經過處理之後,應用再獲取經過處理的資料:#include <linux/init.h> #include <linux/module.h> #include <linux/kernel.h> #include <

Linux下庫檔案學習如何設定庫的路徑

最近在Linux下使用第三方庫Protobuf時,遇到一個問題:編譯出的可執行程式在執行時總報錯:“error while loading shared libraries: libprotobuf.so.7: cannot open shared object file

OpenCV3計算機視覺Python語言實現:處理檔案、攝像頭和圖形使用者介面

2.1 基本I/O指令碼 2.1.1 讀/寫影象檔案 2.1.2 影象和原始位元組之間的轉換 2.1.3使用numpy.array()訪問影象資料 2.1.4 視訊檔案的讀寫 2.1.5 捕獲攝像頭的幀 2.1.6 在視窗顯示影象 2.1.7 在視窗顯示攝像

Dji Mobile SDK 基礎實現

stat one 透傳 pub != exceptio rom tick ann Dji Mobile SDK 基礎實現(二) 本文簡要介紹如何通過調用DJI Mobile SDK,實現獲取和釋放無人機的控制權限、模擬遙控器按鈕控制無人機的飛行、獲取無人機的回傳視頻、獲取

Linux下MySQL主從復制

mysql 鄰居 MySQL的一主多從模式原理結構圖配置步驟1.主服務器: 1. 改變server id 2. 啟用二進制日誌文件 3. 創建具有復制的用戶123123具體操作請看:http://www.crazyrui.pw:8088/index.php/2017/07/23/linux_m

實現自定義查詢的數據庫設計及實現

表名 table abr bigint sts 處理 update 關聯表 creat 上部分大概講了一下表設計,這部分講一下處理。 處理的結構 處理結構的內容比較多,分為幾個部分分別講解一下。首先講解一下尋找關系表。 尋找關系表 尋找關系表根據“表間關系登記表”進行處

linux學習之進程篇

通過 passwd 查看 現在 替換 cnblogs exe -1 stdio.h 進程原語 1.fork #include<unistd.h> pid_t fork(void);   fork   子進程復制父進程,子進程和父進程的PID是不一樣的,在

Linux系統管理----存儲管理

運維 系統 創建文件系統----分區格式化 格式化 低級格式化:磁盤初始化,磁道劃分; 高級格式化:創建或重建文件系統;按照某種特定的標準,將整個分區劃分為大小相同的若幹小的邏輯編址單元,每個這樣的單元,稱為塊(Block);window中稱為:簇 劃分

KVM虛擬化的四種簡單網絡模型介紹及實現

str drive 51cto -c water -a return dfa 模型 接上篇,介紹NAT網絡模型和橋接模型。 三、NAT模型 NAT模型其實就是SNAT的實現,路由中虛擬機能將報文發送給外部主機,但是外部主機因找不到通往虛擬機的路由因而無法回應請求。但是外部

SpringBoot在Kotlin中的實現

文件中 open 代碼 rabl delete ons list any data 根據現在的開發模式和網上的一些資料,SpringBoot需要對業務和操作進行分層,通常分為controller、entity、service、respository等結構。下面以Kotlin

Linux文本處理三劍客awk

退出 action ram out scrip 設計 default dup 性能   前一篇文章分享了awk的一些基本概念和用法,這篇分享的是awk的一些高級用法。在學習awk的過程中,對比我之前學過的C語言,我發現awk裏的while,for,cas

【原始碼剖析】tornado-memcached-sessions —— Tornado session 支援的實現

     客官您終於回頭了!讓我們本著探(zuo)索(si)精神把 session.py 看完吧...       首先看看需要的庫:       pickle 一個用於序列化反序列化的庫(聽

Linux學習之shell 程式設計基礎

一、bash環境變數 HOME、MAIL、SHELL、PATH 等,環境變數大都用大寫字母組成 [[email protected] dalianmao]# echo $SHELL /bin/bash [[email protected] dalianmao]# echo