之前做應用程序的開發,就知道Linux系統的一大特點就是一切皆文件
,一直以來對所有設備的操作都是使用系統函數open read write close
來實現的,就沒關心過系統裏面是怎麼實現對各種設備的區分和控制櫃,直到開始看Linux設備驅動方面的知識以後,感覺像發現了新大陸一樣的神奇,故把相關知識記錄下來,方便以後自己查找。
文件系統調用
文件的打開
int open(const char *pathname, int flags);
int open(const char *pathname, int falgs, mode_t mode);
open函數有兩個形式,其中第一個參數是需要打開的文件路徑及文件名(默認是當前路徑),flags是一下值的一個或者幾個的組合:
標誌(flag) | 含義 |
---|---|
O_RDONLY | 只讀方式 |
O_WRONLY | 只寫方式 |
O_RDWR | 可讀可寫 |
O_APPEND | 追加的方式 |
O_CREAT | 創建一個文件 |
O_EXEC | 如果使用了O_CREAT而且文件已經存在,就會發生一個錯誤 |
O_NOBLOCK | 以非阻塞的方式 |
O_TRUNC | 如果文件已經存在就刪除文件內容 |
O_RDONLY ,O_WRONLY ,O_RDWR
這三個標誌位只能使用其中的一個
如果使用了O_CREAT
標誌,那麼調用的函數需要指定第三個參數mode標誌,以表示文件的訪問權限:
標誌(flag) | 含義 |
---|---|
S_IRUSR | 用戶可以讀 |
S_IWUSR | 用戶可以寫 |
S_IXUSR | 用戶可以執行 |
S_IRWXU | 用戶可讀,可寫,可執行 |
S_IRGRP | 組可讀 |
S_IWGRP | 組可寫 |
S_IXGRP | 組可執行 |
S_IRWXG | 組可讀,可寫,可執行 |
S_IROTH | 其他人可以讀 |
S_IWOTH | 其他人可寫 |
S_IXOTH | 其他人可執行 |
S_IRWXO | 其他人可讀,可寫,可執行 |
S_ISUID | 設置用戶執行ID |
S_ISGID | 設置組ID |
除了通過以上宏第一或運算產生標誌位以外,可以自己用數字生產標誌位規則如下:
byte位 | 含義 | 備註 |
---|---|---|
第一位 (byte 4) | 設置用戶ID | |
第二位 (byte 3) | 設置組ID | |
第三位 (byte 2) | 用戶自己的權限 | 可取 1(可執行),2 (可寫),4(可讀),0(無)或者這些值的和 |
第四位 (byte 1) | 組的權限 | |
第五位 (byte 0) | 其他人權限 |
open("test", O_CREAT, 10705);
open("test", O_CREAT, S_IRWXU | S_IROTH | S_IXOTH | S_ISUID);
上面兩個函數等價:當前路徑下創建一個用戶可讀可寫可執行,組沒有權限,其他人可讀可執行並設置用戶ID名爲test的文件
文件的讀寫
int read(int fd, const void *buf, size_t length);
int write(int fd, const void *buf, size_t length);
參數 fd 爲open函數返回的文件描述符,buf爲指向緩衝區的指針,length爲緩衝區大小;
read()從fd所指的文件裏面讀取length個字節到buf緩衝區,返回實際讀取到的字節數;
write()把length個字節從緩衝區buf中寫到fd指向的文件去,返回實際寫進去的字節數;
文件定位
int lseek(int fd, offset_t offset, int whence);
將文件指針相對whence移動offset個字節,操作成功時返回文件指針相對於文件頭的位置,whence可以取:
- SEEK_SET:文件頭
- SEEK_CUR:當前位置
- SEEK_END:文件末尾
lseek(fd, 0, SEEK_END);
返回的就是文件的長度
lseek(fd, -5 , SEEK_CUR);
將文件指針相對當前位置向前移動5字節
文件關閉
int close(int fd);
文件系統與設備驅動
Linux應用程序和設置之間的關係如下圖所示:
應用程序通過系統調用,操作系統通過虛擬文件系統中的file_operations
結構體去調用相應的設備驅動,file_operations
結構體定義了一系列函數指針,相應的設備驅動實現這些函數,並把函數賦值給file_operations
的函數指針,然後把驅動註冊到內核。file_operations
結構體定義如下(include/linux/fs.h):
struct file_operations {
struct module *owner;
loff_t (*llseek) (struct file *, loff_t, int);
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
ssize_t (*read_iter) (struct kiocb *, struct iov_iter *);
ssize_t (*write_iter) (struct kiocb *, struct iov_iter *);
int (*iopoll)(struct kiocb *kiocb, bool spin);
int (*iterate) (struct file *, struct dir_context *);
int (*iterate_shared) (struct file *, struct dir_context *);
__poll_t (*poll) (struct file *, struct poll_table_struct *);
long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
int (*mmap) (struct file *, struct vm_area_struct *);
unsigned long mmap_supported_flags;
int (*open) (struct inode *, struct file *);
int (*flush) (struct file *, fl_owner_t id);
int (*release) (struct inode *, struct file *);
int (*fsync) (struct file *, loff_t, loff_t, int datasync);
int (*fasync) (int, struct file *, int);
int (*lock) (struct file *, int, struct file_lock *);
ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
int (*check_flags)(int);
int (*flock) (struct file *, int, struct file_lock *);
ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
int (*setlease)(struct file *, long, struct file_lock **, void **);
long (*fallocate)(struct file *file, int mode, loff_t offset,
loff_t len);
void (*show_fdinfo)(struct seq_file *m, struct file *f);
#ifndef CONFIG_MMU
unsigned (*mmap_capabilities)(struct file *);
#endif
ssize_t (*copy_file_range)(struct file *, loff_t, struct file *,
loff_t, size_t, unsigned int);
loff_t (*remap_file_range)(struct file *file_in, loff_t pos_in,
struct file *file_out, loff_t pos_out,
loff_t len, unsigned int remap_flags);
int (*fadvise)(struct file *, loff_t, loff_t, int);
} __randomize_layout;
file_operations內核函數 | 用戶空間函數 | 備註 |
---|---|---|
llseek() | lseek() | 用來修改一個文件的當前讀寫位置,並將新位置返回,出錯時返回一個負數 |
read() | read() fread() | 從設備中讀取數據,成功返回讀取的字節數,失敗返回負數,0暗示end-of-file |
write() | write() fwrite() | 向設備發送數據,成功返回發送的字節數,失敗返回負數,如果此函數未被實現,用戶調研write()以後講收到-EINVAL 返回值 |
unlocked_ioctrl() | ioctrl() fcntl() | 提供設備相關控制命令調用,成功返回非負值,失敗返回負數 |
mmap() | mmap() | 幀緩衝被映射到用戶空間,應用程序可以直接訪問,不需要再內核空間和用戶空間進行內存複製,如果此函數未被實現,用戶調研mmap()以後講收到-EINVAL 返回值 |
poll() | select() poll() | 用於查詢設備是否可被非阻塞的進行讀寫,當查詢的調節未觸發時,用戶空間進行select() 和 poll()查詢系統調用時將引起進程的阻塞 |
aio_read() aio_write | 對設備進行異步讀寫操作,當該函數實現以後,用戶空間可以對設備執行SYS_io_setup、SYS_io_submit、SYS_io_getevents、SYS_io_destory 等系統調用進行讀寫 |