<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                **一、先看看設備應用程序** 1.很簡單,open設備文件,read、write、ioctl,最后close退出。如下: ~~~ intmain(int argc ,char *argv[]){ unsigned char val[1] = 1; int fd =open("/dev/LED",O_RDWR);//打開設備 write(fd,val,1);//寫入設備,這里代表LED全亮 close(fd);//關閉設備 return 0; } ~~~ **二、/dev目錄與文件系統** 2./dev是根文件系統下的一個目錄文件,/代表根目錄,其掛載的是根文件系統的yaffs格式,通過讀取/根目錄這個文件,就能分析list出其包含的各個目錄,其中就包括dev這個子目錄。即在/根目錄(也是一個文件,其真實存在于flash介質)中有一項這樣的數據: 文件屬性 文件偏移 文件大小 文件名稱 等等 ls/ 命令即會使用/掛載的yaffs文件系統來讀取出根目錄文件的內容,然后list出dev(是一個目錄)。即這時還不需要去讀取dev這個目錄文件的內容。Cd dev即會分析dev掛載的文件系統的超級塊的信息,superblock,而不再理會在flash中的dev目錄文件的數據。 3./dev在根文件系統構建的時候會掛載為tmpfs. Tmpfs是一個基于虛擬內存的文件系統,主要使用RAM和SWAP(Ramfs只是使用物理內存)。即以后讀寫dev這個目錄的操作都轉到tmpfs的操作,確切地講都是針對RAM的操作,而不再是通過yaffs文件系統的讀寫函數去訪問flash介質。Tmpfs基于RAM,所以在掉電后回消失。因此/dev目錄下的設備文件都是每次linux啟動后創建的。 掛載過程:/etc/init.d/rcS Mount –a 會讀取/etc/fstab的內容來掛載,其內容如下: ![](https://box.kancloud.cn/2016-01-11_5693908537870.png) 4./dev/NULL和/dev/console是在制作根文件系統的時候靜態創建的,其他設備文件都是系統加載根文件系統和各種驅動初始化過程中自動創建的,當然也可以通過命令行手動mknod設備文件。 **三、設備文件的創建** 5./dev目錄下的設備文件基本上都是通過mdev來動態創建的。mdev是一個用戶態的應用程序,位于busybox工具箱中。其創建過程包括: 1)驅動初始化或者總線匹配后會調用驅動的probe接口,該接口會調用device_create(設備類, 設備號, 設備名);在/sys/class/設備類目錄生成唯一的設備屬性文件(包括設備號和設備名等信息),并且發送uvent事件(KOBJ_ADD和環境變量,如路徑等信息)到用戶空間(通過socket方式)。 2)mdev是一個work_thread線程,收到事件后會分析出/sys/class/設備類的對應文件,最終調用mknod動態來創建設備文件,而這個設備文件內容主要是設備號(這個設備文件對應的inode會記錄文件的屬性是一個設備(其他屬性還包括目錄,一般文件,符號鏈接等))。應用程序open(device_name,…)最重要的一步就是通過文件系統接口來獲得該設備文件的內容—設備號。 6.如果初始化過程中沒有調用device_create接口來創建設備文件,則需要手動通過命令行調用mknod接口來創建設備文件,方可在應用程序中訪問。 7.mknod接口分析,通過系統調用后對應調用sys_mknod,其是vfs層的接口。 Sys_mknod(設備名, 設備號) vfs通過逐一路徑link_path_walk,分析出dev掛載了tmpfs,所以調用tmpfs->mknod=shmem_mknod ![](https://box.kancloud.cn/2016-01-11_5693908546ad5.jpg) shmem_mknod(structinode *dir, struct dentry *dentry, int mode, dev_t dev) inode = shmem_get_inode(dir->i_sb,dir, mode, dev, VM_NORESERVE); inode = new_inode(sb); switch (mode & S_IFMT) { default: inode->i_op =&shmem_special_inode_operations; init_special_inode(inode,mode, dev); ![](https://box.kancloud.cn/2016-01-11_569390855fc6c.jpg) break; case S_IFREG://file case S_IFDIR://DIR case S_IFLNK: //dentry填入inode信息,這時對應的dentry和inode都已經存在于內存中。 d_instantiate(dentry, inode); 8\. 可見,tmpfs的目錄和文件都是像ramfs一樣一般都存在于內存中。通過ls命令來獲取目錄的信息則由dentry數據結構的內容來獲取,而文件的信息由inode數據結構的內容來提供。Inode包括設備文件的設備號i_rdev,文件屬性(i_mode: S_ISCHR),inode操作集i_fop(對于設備文件來說就是如何open這個inode)。 **四、open設備文件** 9\. open設備文件的最終目的是為了獲取到該設備驅動的file_operations操作集,而該接口集是struct file的成員,open返回file數據結構指針: ~~~ struct file { conststruct file_operations *f_op; unsignedint f_flags;//可讀,可寫等 … ~~~ }; 以下是led設備驅動的操作接口。open("/dev/LED",O_RDWR)就是為了獲得led_fops。 static conststruct file_operations led_fops = { .owner =THIS_MODULE, .open =led_open, .write = led_write, }; 10\. 仔細看應用程序int fd =open("/dev/LED",O_RDWR),open的返回值是int,并不是file,其實是為了操作系統和安全考慮。fd位于應用層,而file位于內核層,它們都同屬進程相關概念。在Linux中,同一個文件(對應于唯一的inode)可以被不同的進程打開多次,而每次打開都會獲得file數據結構。而每個進程都會維護一個已經打開的file數組,fd就是對應file結構的數組下標。因此,file和fd在進程范圍內是一一對應的關系。 11\. open接口分析,通過系統調用后對應調用sys_open,其是vfs層的接口 ~~~ Sys_open(/dev/led) SYSCALL_DEFINE3(open,const char __user *, filename, int, flags, int, mode) do_sys_open(AT_FDCWD,/dev/tty, flags, mode); fd = get_unused_fd_flags(flags); struct file *f = do_filp_open(dfd, tmp, flags, mode, 0); //path_init返回時nd->dentry即為搜索路徑文件名的起點 path_init(dfd, pathname, LOOKUP_PARENT, &nd); //link_path_walk一步步建立打開路徑的各個目錄的dentry和inode link_path_walk(pathname, &nd); do_last(&nd, &path, open_flag, acc_mode, mode, pathname); //先處理..父目錄和.當前目錄 //通過inode節點創建file filp = nameidata_to_filp(nd); __dentry_open() //inode->i_fop=&def_chr_fops f->f_op =fops_get(inode->i_fop); if (!open && f->f_op) open = f->f_op->open; if (open) { //調用def_chr_fops->open error = open(inode, f); 其中inode->i_fop在mknod的init_special_inode調用中被賦值為def_chr_fops。以下該變量的定義,因此, open(inode, f)即調用到chrdev_open。其可以看出是字符設備所對應的文件系統接口,我們姑且稱其為字符設備文件系統。 conststruct file_operations def_chr_fops = { .open = chrdev_open, }; ~~~ 繼續分析chrdev_open: ![](https://box.kancloud.cn/2016-01-11_5693908537870.png) Kobj_lookup(cdev_map,inode->i_rdev, &idx)即是通過設備的設備號(inode->i_rdev)在cdev_map中查找設備對應的操作集file_operations.關于如何查找,我們在理解字符設備驅動如何注冊自己的file_operations后再回頭來分析這個問題。 **五、字符設備驅動的注冊** 12\. 字符設備對應cdev數據結構: ~~~ struct cdev { struct kobject kobj; // 每個 cdev 都是一個 kobject struct module*owner; // 指向實現驅動的模塊 const structfile_operations *ops; // 操縱這個字符設備文件的方法 struct list_headlist; //對應的字符設備文件的inode->i_devices 的鏈表頭 dev_t dev; // 起始設備編號 unsigned intcount; // 設備范圍號大小 }; ~~~ 13\. led設備驅動初始化和設備驅動注冊 ![](https://box.kancloud.cn/2016-01-11_5693908537870.png) * cdev_init是初始化cdev結構體,并將led_fops填入該結構。 * cdev_add ~~~ int cdev_add(struct cdev *p, dev_t dev, unsigned count) { p->dev = dev; p->count = count; return kobj_map(cdev_map, dev, count, NULL, exact_match, exact_lock, p); } ~~~ * cdev_map是一個全家指針變量,類型如下: ![](https://box.kancloud.cn/2016-01-11_5693908537870.png) * kobj_map使用hash散列表來存儲cdev數據結構。通過注冊設備的主設備號major來獲得cdev_map->probes數組的索引值i(i = major % 255),然后把一個類型為struct probe的節點對象加入到probes[i]所管理的鏈表中,probes[i]->data即是cdev數據結構,而probes[i]->dev和range代表字符設備號和范圍。 **六、再述open設備文件** 14\. 通過第五步的字符設備的注冊過程,應該對Kobj_lookup查找led_ops是很容易理解的。至此,已經獲得led設備驅動的led_ops。接著立刻調用file->f_ops->open即調用了led_open,在該函數中會對led用到的GPIO進行ioremap并設置GPIO方向、上下拉等硬件初始化。 15\. 最后,chrdev_open一步步返回,最后到 do_sys_open的struct file *f = do_filp_open(dfd, tmp, flags, mode, 0);返回。 ![](https://box.kancloud.cn/2016-01-11_5693908537870.png) Fd_install(fd, f)即是在當前進程中將存有led_ops的file指針填入進程的file數組中,下標是fd。最后將fd返回給用戶空間。而用戶空間只要傳入fd即可找到對應的file數據結構。 **七、設備操作** 15\. 這里以設備寫為例,主要是控制led的亮和滅。 write(fd,val,1)系統調用后對應sys_write,其對應所有的文件寫,包括目錄、一般文件和設備文件,一般文件有位置偏移的概念,即讀寫之后,當前位置會發生變化,所以如要跳著讀寫,就需要fseek。對于字符設備文件,沒有位置的概念。所以我們重點跟蹤vfs_write的過程。 ![](https://box.kancloud.cn/2016-01-11_5693908537870.png) 1)fget_light在當前進程中通過fd來獲得file指針 2)vfs_write ![](https://box.kancloud.cn/2016-01-11_5693908537870.png) 3) 對于led設備,file->f_op->write即是led_write。 在該接口中實現對led設備的控制。 **八、再論字符設備驅動的初始化** 綜上所述,字符設備的初始化包括兩個主要環節: 1)字符設備驅動的注冊,即通過cdev_add向系統注冊cdev數據結構,提供file_operations操作集和設備號等信息,最終file_operations存放在全局指針變量cdev_map指向的Hash表中,其可以通過設備號索引并遍歷得到。 2)通過device_create(設備類, 設備號, 設備名)在sys/class/設備類中創建設備屬性文件并發送uevent事件,而mdev利用該信息自動調用mknod在/dev目錄下創建對應的設備文件,以便應用程序訪問。 注: device_create和mdev的代碼分析請留意后續文章。本文涉及的vfs虛擬文件系統知識(如vfs框架、dentry,inode數據結構等內容)也由后續文章詳細講述。 更多原創技術分享敬請關注微信公眾號:嵌入式企鵝圈 ![](https://box.kancloud.cn/2016-01-13_5695f8f8d24b2.jpg)
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看