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