集團(tuán)網(wǎng)站開(kāi)發(fā)公司百度app下載官方
Linux文件系統(tǒng)與設(shè)備文件
文章目錄
- Linux文件系統(tǒng)與設(shè)備文件
- Linux文件操作
- 文件操作系統(tǒng)調(diào)用
- C庫(kù)文件操作
- Linux文件系統(tǒng)
- Linux文件系統(tǒng)目錄結(jié)構(gòu)
- Linux文件系統(tǒng)與設(shè)備驅(qū)動(dòng)
- file結(jié)構(gòu)體
- inode結(jié)構(gòu)體
- file結(jié)構(gòu)體和inode結(jié)構(gòu)體的區(qū)別
- devfs
- udev用戶空間設(shè)備管理
- sysfs文件系統(tǒng)與Linux設(shè)備模型
字符設(shè)備和塊設(shè)備體現(xiàn)了Linux中的一切皆文件的設(shè)計(jì)思想,驅(qū)動(dòng)通過(guò)文件操作相關(guān)的系統(tǒng)調(diào)用和C庫(kù)函數(shù)(本質(zhì)也屬于系統(tǒng)調(diào)用)被訪問(wèn),其次驅(qū)動(dòng)工程師在設(shè)備驅(qū)動(dòng)中不可避免會(huì)與設(shè)備文件系統(tǒng)打交道,這也引出了我們需要掌握設(shè)備文件系統(tǒng)的相關(guān)知識(shí)。
Linux文件操作
文件操作系統(tǒng)調(diào)用
涉及到創(chuàng)建打開(kāi)關(guān)閉等操作
- 創(chuàng)建
int creat(const char *filename, mode_t mode)
其中參數(shù)filename指的是文件名,mode指的是文件的權(quán)限,它同umask決定了文件的最終權(quán)限,umask代表了文件在創(chuàng)建時(shí)要去除的一些存取權(quán)限
int umask(int newmask)
將umask設(shè)置為新的mask,然后返回舊的umask,只影響讀寫(xiě)執(zhí)行的權(quán)限
- 打開(kāi)
int open(const char *pathname, int flags, mode_t mode);
int open(const char *pathname, int flags);
pathname默認(rèn)的是當(dāng)前文件夾的下面
文件的打開(kāi)標(biāo)志:
標(biāo)志 | 含義 |
---|---|
O_RDONLY | |
O_WRONLY | |
O_RDWR | rdonly,wronly和rdwr這三個(gè)標(biāo)志只能選取其中一個(gè),不能同時(shí)存在 |
O_APPEND | |
O_CREAT | |
O_EXEC | 如果使用了O_CREAT而且文件已經(jīng)存在,就會(huì)發(fā)生一個(gè)錯(cuò)誤 |
O_NOBLOCK | 以非阻塞的方式打開(kāi)一個(gè)文件 |
O_TRUNC | 如果文件已經(jīng)存在,則刪除文件的內(nèi)容 |
如果使用了mode_t,表示打開(kāi)時(shí)有文件的訪問(wèn)權(quán)限:
標(biāo)志 | 含義 |
---|---|
S_IRUSR | 用戶可以讀 |
S_IWUSR | |
S_IXUSR | |
S_IRGRP | 組可以讀 |
S_IWGRP | |
S_IXGRP | |
S_IROTH | 其他人可以讀 |
S_IWOTH | |
S_IXOTH | |
S_IRWXO | 其他人可以讀寫(xiě)執(zhí)行 |
S_ISUID | 設(shè)置用戶執(zhí)行ID(set UID) |
S_ISGID | 設(shè)置組執(zhí)行ID |
除了上述宏來(lái)表示的產(chǎn)生標(biāo)志之外,我們自己也可以用數(shù)字來(lái)表示
文件權(quán)限共5位:從左往右數(shù)第一位用戶ID、第二位組ID、第三位自己的權(quán)限、第四位組權(quán)限、第五位其他人的權(quán)限;
數(shù)字表示1(執(zhí)行權(quán)限)、2(寫(xiě)權(quán)限)、4(讀權(quán)限)、0(無(wú)權(quán)限)
-
例如要?jiǎng)?chuàng)建一個(gè)用戶可讀、可寫(xiě)、可執(zhí)行、但是組沒(méi)有權(quán)限,其他人可以讀、可以執(zhí)行的文件,并設(shè)置用戶ID。
open(filename, O_CREAT, 10 705);
open(filename, O_CREAT, S_IRWXU|S_IROTH|S_XOTH|S_ISUID);
如果文件打開(kāi)成功會(huì)返回一個(gè)文件描述符,以后對(duì)于文件的所有操作都可以通過(guò)對(duì)這個(gè)文件描述符進(jìn)行操作來(lái)實(shí)現(xiàn)。
- 讀寫(xiě)
int read(int fd, const void *buf, size_t length);
buf為緩沖區(qū)名稱,length為緩沖區(qū)的大小,單位為字節(jié),表示在fd中讀取length個(gè)字節(jié)寫(xiě)入buf中,返回的是實(shí)際讀取的字節(jié)數(shù)。
int write(int fd, const void *buf, size_t length);
表示從buf中讀取length字節(jié)數(shù)寫(xiě)入fd中,返回實(shí)際寫(xiě)入的字節(jié)數(shù)
- 定位lseek
int lseek(int fd, offset_t offset, int whence);
lseek將文件的讀寫(xiě)指針相對(duì)whence移動(dòng)了offset個(gè)字節(jié),返回文件指針相對(duì)于文件開(kāi)發(fā)的位置
SEEK_SET | 相對(duì)文件開(kāi)頭 |
---|---|
SEEK_CUR | 相對(duì)當(dāng)前位置 |
SEEK_END | 相對(duì)文件末尾 |
lseek(fd, 0, SEEK_END)
表示文件的大小
也就是文件指針是從wherece的位置處開(kāi)始移動(dòng),結(jié)果移動(dòng)了0個(gè)位置,指針就停在了whence的位置,而函數(shù)返回值是文件指針相對(duì)于文件開(kāi)頭的位置,因此這個(gè)返回值就是文件的長(zhǎng)度。
- 關(guān)閉
int close(int fd);
例如,編寫(xiě)一個(gè)程序,在當(dāng)前目錄下面創(chuàng)建用戶可讀寫(xiě)文件hello.txt,在其中寫(xiě)入“hello,software weekly”,關(guān)閉該文件,再次打開(kāi)該文件,讀取其中的內(nèi)容輸出在屏幕上
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>#define BUFF_SIZE 100int man()
{int ret;char buff[BUFF_SIZE];int fd = open("hello.txt", O_CREAT|O_RDWR, S_IRUSR|S_IWUSR);if(fd){ret = write(fd, "hello,software weekly", strlen(hello,software weekly));if(ret < 0)printf("write failed\n");close(fd);
}else{printf("creat failed\n");
}fd = open("hello.txt", O_RDONLY);ret = read(fd, buff, sizeof(buff));if(ret){printf("read failed\n");close(fd);
}buff[ret] = "\0";printf("%s\n", buff);close(fd);
}
C庫(kù)文件操作
- 創(chuàng)建和打開(kāi)
FILE *fopen(const char *path, const char *mode);
mode為C庫(kù)函數(shù)打開(kāi)的標(biāo)志:
標(biāo)志 | 含義 |
---|---|
r、rb | 讀 |
r+、r+b | 讀寫(xiě) |
w | 寫(xiě),不存在則創(chuàng)建 |
w+、w+b | 讀寫(xiě),不存在則創(chuàng)建 |
a | 追加,不存在則創(chuàng)建 |
a+、a+b | 讀寫(xiě)且追加,不存在則創(chuàng)建 |
b為二進(jìn)制文件,在window下面是有區(qū)分的,但是Linux下面則沒(méi)有
- 讀寫(xiě)
int fgetc(FILE *stream);
int fputc(int c, FILE *stream);
char *fgets(char *s, int size, FILE *stream);
int fputs(const char *s, FILE *stream);
size_t fread(void *ptr, size_t size, size_t n, FILE *stream);
size_t fwrite(const void *ptr, size_t size, size_t nmemb,FILE *stream);
fread表示從stream流中讀取n個(gè)字段,每個(gè)字段大小為size個(gè)字節(jié),將讀取到的數(shù)據(jù)存到ptr中,返回已經(jīng)讀取的字段數(shù)。當(dāng)讀取的字段數(shù)小于n時(shí),可能是在函數(shù)調(diào)用時(shí)出現(xiàn)了錯(cuò)誤,也可能是讀到了文件的末尾。因此要通過(guò)調(diào)用feof()和ferror()來(lái)判斷。
另外C庫(kù)還提供了定位函數(shù):
int fseek(FILE *stream, long offset, int whence);
int fgetpos(FILE *stream, fpos_t *pos);
int fsetpos(FILE *stream, const fpos_t *pos);
- 關(guān)閉
int fclose(FILE *stream);
例如,編寫(xiě)一個(gè)程序,在當(dāng)前目錄下面創(chuàng)建用戶可讀寫(xiě)文件hello.txt,在其中寫(xiě)入“hello,software weekly”,關(guān)閉該文件,再次打開(kāi)該文件,讀取其中的內(nèi)容輸出在屏幕上
#include <stdio.h>#define BUFF_SIZE 100
int main()
{char buff[BUFF_SIZE];FILE *fd = fopen("hello.txt", w+);if (fd){fputs("hello,software weekly", fd);fclose(fd);
}fd = fopen("hello.txt", r+);if(fd){fgets(buff, sizeof(buff), fd);printf("%s\n", buff); //fgets函數(shù)會(huì)默認(rèn)在字符串后面補(bǔ)"\0"fclose(fd);
}
}
Linux文件系統(tǒng)
Linux文件系統(tǒng)目錄結(jié)構(gòu)
linux文件系統(tǒng)目錄結(jié)構(gòu)
Linux文件系統(tǒng)與設(shè)備驅(qū)動(dòng)
應(yīng)用程序與文件系統(tǒng)直接的接口是系統(tǒng)調(diào)用,文件系統(tǒng)與設(shè)備文件之間的接口是file_operations結(jié)構(gòu)體成員函數(shù)。
由于字符設(shè)備的上層沒(méi)有類似于文件系統(tǒng),所以字符設(shè)備的file_operations成員函數(shù)就直接由設(shè)備驅(qū)動(dòng)提供了
塊設(shè)備有兩種訪問(wèn)方法:
-
一種是不通過(guò)文件系統(tǒng)直接訪問(wèn)裸設(shè)備,在Linux內(nèi)核實(shí)現(xiàn)了統(tǒng)一的def_blk_fops這一file_operations
類似于”dd if=/dev/sdb1 of=sdb1.img”這一指令是不通過(guò)文件系統(tǒng),直接訪問(wèn)的驅(qū)動(dòng)設(shè)備文件
-
第二種是通過(guò)文件系統(tǒng)訪問(wèn)塊設(shè)備,file_operations的實(shí)現(xiàn)則位于文件系統(tǒng)內(nèi),文件系統(tǒng)會(huì)把針對(duì)文件的讀寫(xiě)轉(zhuǎn)換為針對(duì)塊設(shè)備原始扇區(qū)的讀寫(xiě)
在設(shè)備驅(qū)動(dòng)程序的設(shè)計(jì)種,一般而言,會(huì)關(guān)心file和inode這兩個(gè)結(jié)構(gòu)體
file結(jié)構(gòu)體
系統(tǒng)中每個(gè)打開(kāi)的文件在內(nèi)核空間都有一個(gè)關(guān)聯(lián)的struct file,在內(nèi)核和驅(qū)動(dòng)源代碼中,struct file的指針通常被命名為file或者filep
文件結(jié)構(gòu)體struct file中讀寫(xiě)模式mode、標(biāo)志f_flags都是設(shè)備驅(qū)動(dòng)關(guān)心得內(nèi)容,而私有指針private_data在設(shè)備驅(qū)動(dòng)中被廣泛使用大多數(shù)指向設(shè)備驅(qū)動(dòng)自定義以**用于描述設(shè)備的結(jié)構(gòu)體,**是更高級(jí)的文件描述,依賴于低層的struct inode數(shù)據(jù)結(jié)構(gòu)
inode結(jié)構(gòu)體
VFS inode是Linux管理文件系統(tǒng)的最基本的單位,也是文件系統(tǒng)連接任何子目錄,文件的橋梁,只與操作系統(tǒng)相關(guān),用于保存文件或者目錄信息。
表示設(shè)備文件的inode結(jié)構(gòu),i_rdev字段包含設(shè)備編號(hào)。Linux的設(shè)備編號(hào)分為主設(shè)備編號(hào)和次設(shè)備編號(hào),前者為dev_t的高12位,后者位dev_t的低20位
unsigned int imajor(struct inode *inode);
獲取主設(shè)備號(hào),主設(shè)備號(hào)是驅(qū)動(dòng)對(duì)應(yīng)的概念,同一類設(shè)備一般使用相同的主設(shè)備號(hào),序號(hào)一般從0開(kāi)始
unsigned int imanor(struct inode *inode);
獲取次設(shè)備號(hào)
查看 /proc/devices
可以獲知系統(tǒng)中所注冊(cè)的設(shè)備
file結(jié)構(gòu)體和inode結(jié)構(gòu)體的區(qū)別
struct inode和struct file的區(qū)別在于inode只關(guān)心操作系統(tǒng)找出底層文件結(jié)構(gòu)的內(nèi)容(例如什么設(shè)備文件),而不去具體的跟蹤文件的當(dāng)前位置和當(dāng)前模式。struct file是個(gè)基本結(jié)構(gòu),實(shí)際上持有一個(gè)struct inode的指針,他代表打開(kāi)的文件,并且提供一組函數(shù),他們與底層文件結(jié)構(gòu)執(zhí)行方法有關(guān)
也就是說(shuō)struct inode只代表內(nèi)核中的文件,而struct file表示實(shí)際打開(kāi)的文件,同一個(gè)文件被打開(kāi)時(shí)可能有多個(gè)文件描述符,但他們都指向同一個(gè)inode。
devfs
devfs(設(shè)備文件系統(tǒng))是由Linux 2.4內(nèi)核出現(xiàn)的,使得設(shè)備驅(qū)動(dòng)程序能夠自主地管理自己的設(shè)備文件
- 可以在程序初始化的時(shí)候在/dev目錄下創(chuàng)建設(shè)備文件,卸載設(shè)備時(shí)將它刪除
- 設(shè)備驅(qū)動(dòng)程序可以指定設(shè)備名、所有者和權(quán)限位
- 不再需要為設(shè)備驅(qū)動(dòng)程序分配主設(shè)備號(hào)以及處理次設(shè)備號(hào),在程序中可以直接給
register_chrdev()
傳遞0主設(shè)備號(hào)以獲得可用的主設(shè)備號(hào),并在devfs_register()
中指定次設(shè)備號(hào)
udev用戶空間設(shè)備管理
- udev是在Linux 2.6的時(shí)候被引入,與devfs不同的是,就像談戀愛(ài),udev系統(tǒng)可以使得用戶在上層自由選擇和誰(shuí)談戀愛(ài),而不能在內(nèi)核空間限制和誰(shuí)談戀愛(ài)。對(duì)于devfs而言,第一個(gè)相親女孩被命名為/dev/girl0,第二個(gè)相親女孩被命名為/dev/girl1。而在用戶空間實(shí)現(xiàn)的udev則可以,不管你中意的女孩是第幾個(gè),只要符合要求,都是/dev/mygirl。
- udev完全工作在用戶態(tài),利用設(shè)備的熱插拔事件來(lái)工作,詳細(xì)的設(shè)備信息會(huì)由內(nèi)核通過(guò)netlink套接字發(fā)送出來(lái),發(fā)出的事情叫uevent。
- 那冷插拔事件怎么辦呢,設(shè)備當(dāng)主機(jī)開(kāi)機(jī)時(shí)就已經(jīng)存在了,Linux內(nèi)核提供了sysfs下面的一個(gè)uevent節(jié)點(diǎn),往該節(jié)點(diǎn)寫(xiě)一個(gè)”add”,(具體怎么寫(xiě)我也不是很懂)導(dǎo)致內(nèi)核重新發(fā)送netlink,之后udev就可以收到冷插拔的netlink消息了
- 對(duì)于devfs,當(dāng)一個(gè)并不存在的/dev節(jié)點(diǎn)被訪問(wèn)時(shí),系統(tǒng)會(huì)自動(dòng)加載相對(duì)應(yīng)的設(shè)備驅(qū)動(dòng),而udev卻不是這樣,是在產(chǎn)生熱插拔事件時(shí)才會(huì)加載相應(yīng)的驅(qū)動(dòng),并且創(chuàng)建的對(duì)應(yīng)節(jié)點(diǎn)
sysfs文件系統(tǒng)與Linux設(shè)備模型
udev和sysfs都是Linux系統(tǒng)的一部分,但它們有不同的功能和用途。sysfs是一個(gè)虛擬文件系統(tǒng),用于從用戶空間訪問(wèn)內(nèi)核對(duì)象的屬性。它將內(nèi)核對(duì)象的屬性表示為文件和目錄,使得用戶空間的程序能夠通過(guò)標(biāo)準(zhǔn)的文件系統(tǒng)接口查詢和更改內(nèi)核對(duì)象的屬性。包括展示設(shè)備驅(qū)動(dòng)模型中各組件的層次關(guān)系。
在/sys/目錄下的頂級(jí)目錄:
block | 包含所有的塊設(shè)備 |
---|---|
devices | 包含系統(tǒng)所有的設(shè)備 |
bus | 包含所有的總線類型 |
class | 包含系統(tǒng)中的設(shè)備類型 |
他們實(shí)際上都會(huì)被認(rèn)為是kobject的派生類,一個(gè)kobject對(duì)應(yīng)sysfs中的一個(gè)目錄。
在/sys/bus/pci的目錄下又會(huì)出現(xiàn)devices和drivers,而這個(gè)devices目錄下的文件是對(duì)/sys/devices下的文件的符號(hào)鏈接
與此不同,udev是一個(gè)設(shè)備管理器,它管理/dev目錄下的設(shè)備節(jié)點(diǎn)。當(dāng)內(nèi)核檢測(cè)到新設(shè)備時(shí),它將發(fā)送一個(gè)uevent,udev將接收到這個(gè)事件并對(duì)其進(jìn)行處理。這可能包括加載設(shè)備驅(qū)動(dòng),創(chuàng)建或刪除設(shè)備節(jié)點(diǎn),甚至更改設(shè)備的權(quán)限和所有權(quán)。udev的工作依賴于sysfs,因?yàn)樗褂胹ysfs提供的信息來(lái)確定如何處理設(shè)備。
總的來(lái)說(shuō),sysfs是一個(gè)提供有關(guān)內(nèi)核對(duì)象的信息的接口,而udev則使用這些信息來(lái)管理設(shè)備。就好比udev是做飯的方法,而sys是菜和米飯
-
在Linux內(nèi)核中,分別使用bus_type、device_driver和device來(lái)描述總線、驅(qū)動(dòng)和設(shè)備,這三個(gè)結(jié)構(gòu)體定義在include/linux/device.h中
- device_driver和device分別代表驅(qū)動(dòng)和設(shè)備,他們都會(huì)掛載在總線上面,因此結(jié)構(gòu)體中也都包含struct bus_type指針
- 驅(qū)動(dòng)和設(shè)備注冊(cè)的時(shí)候不需要都存在,注冊(cè)設(shè)備時(shí)不需要對(duì)應(yīng)的驅(qū)動(dòng)被注冊(cè),注冊(cè)驅(qū)動(dòng)時(shí)不需要對(duì)應(yīng)的設(shè)備被注冊(cè),而都注冊(cè)之后,又如何連接起來(lái)呢
- 通過(guò)bus_type結(jié)構(gòu)體的match()成員將兩者捆綁在一起,一旦捆綁成功bus_driver的probe()就被執(zhí)行
- 驅(qū)動(dòng)、設(shè)備、總線的attribute都會(huì)落實(shí)為sys中的一個(gè)文件,會(huì)伴隨著show()和store()這兩個(gè)函數(shù)進(jìn)行讀寫(xiě)操作
- sysfs中的目錄來(lái)源于device、device_driver、bus_type,而目錄中的文件就來(lái)源于attribute
- 定義了一些快捷方式以方便attribute的創(chuàng)建工作
#define DRIVER_ATTR(_name, _mode, _show, _store) \struct driver_attribute driver_attr_##_name = __ATTR(_name, _ mode, _show, _store) #define DRIVER_ATTR_RW(_name) \struct driver_attribute driver_attr_##_name = __ATTR_RW(_name) #define DRIVER_ATTR_RO(_name) \struct driver_attribute driver_attr_##_name = __ATTR_RO(_name) #define DRIVER_ATTR_WO(_name) \struct driver_attribute driver_attr_##_name = __ATTR_WO(_name)#define BUS_ATTR(_name, _mode, _show, _store) \struct bus_attribute bus_attr_##_name = __ATTR(_name, _mode, _show, _ store) #define BUS_ATTR_RW(_name) \struct bus_attribute bus_attr_##_name = __ATTR_RW(_name) #define BUS_ATTR_RO(_name) \struct bus_attribute bus_attr_##_name = __ATTR_RO(_name)#define DEVICE_ATTR(_name, _mode, _show, _store) \struct device_attribute dev_attr_##_name = __ATTR(_name, _mode, _show, _store) #define DEVICE_ATTR_RW(_name) \struct device_attribute dev_attr_##_name = __ATTR_RW(_name) #define DEVICE_ATTR_RO(_name) \struct device_attribute dev_attr_##_name = __ATTR_RO(_name) #define DEVICE_ATTR_WO(_name) \struct device_attribute dev_attr_##_name = __ATTR_WO(_name) #define DEVICE_ULONG_ATTR(_name, _mode, _var) \struct dev_ext_attribute dev_attr_##_name = \{ __ATTR(_name, _mode, device_show_ulong, device_store_ulong), &(_var) } #define DEVICE_INT_ATTR(_name, _mode, _var) \struct dev_ext_attribute dev_attr_##_name = \{ __ATTR(_name, _mode, device_show_int, device_store_int), &(_var) } #define DEVICE_BOOL_ATTR(_name, _mode, _var) \struct dev_ext_attribute dev_attr_##_name = \{ __ATTR(_name, _mode, device_show_bool, device_store_bool), &(_var) } #define DEVICE_ATTR_IGNORE_LOCKDEP(_name, _mode, _show, _store) \struct device_attribute dev_attr_##_name = \__ATTR_IGNORE_LOCKDEP(_name, _mode, _show, _store)
撰寫(xiě)不易,留下您的關(guān)注和點(diǎn)贊,我們一起進(jìn)步!