linux驅(qū)動(dòng)開(kāi)發(fā)(二):Linux字符設(shè)備驅(qū)動(dòng)程序(設(shè)備號(hào)、cdev、設(shè)備節(jié)點(diǎn)、file_operations)

Linux系統(tǒng)將設(shè)備分成字符設(shè)備、塊設(shè)備、網(wǎng)絡(luò)設(shè)備三類(lèi)。


用戶(hù)程序調(diào)用硬件的過(guò)程如下。

一、用戶(hù)級(jí)、內(nèi)核級(jí)和系統(tǒng)調(diào)用

Linux/Unix系統(tǒng)下的進(jìn)程運(yùn)行分為用戶(hù)態(tài)進(jìn)程態(tài)兩種狀態(tài)。我們的應(yīng)用程序通常僅在用戶(hù)態(tài)下運(yùn)行,出于保護(hù)內(nèi)核資源的需要,用戶(hù)態(tài)下運(yùn)行的程序在只能訪問(wèn)有限的資源,例如不能訪問(wèn)內(nèi)核的數(shù)據(jù)結(jié)構(gòu)和程序。

內(nèi)核的一個(gè)重要功能就是協(xié)調(diào)和管理硬件資源,包括CPU、內(nèi)存、I/O設(shè)備等,從而為上層運(yùn)行的諸多應(yīng)用程序提供更好的運(yùn)行環(huán)境。因此,驅(qū)動(dòng)程序通常都是在內(nèi)核態(tài)下運(yùn)行。我們的進(jìn)程大多數(shù)時(shí)間都是運(yùn)行在用戶(hù)態(tài)下的,一旦它需要操作系統(tǒng)幫助它完成一些自己沒(méi)有權(quán)限和能力完成的工作,就會(huì)切換到內(nèi)核態(tài)。而系統(tǒng)調(diào)用就是進(jìn)程主動(dòng)申請(qǐng)從用戶(hù)態(tài)切換到內(nèi)核態(tài)的方式。

除了系統(tǒng)調(diào)用以外,發(fā)生異常或外圍設(shè)備的中斷也會(huì)讓進(jìn)程從用戶(hù)態(tài)進(jìn)入內(nèi)核態(tài)。但是,系統(tǒng)調(diào)用是應(yīng)用程序主動(dòng)轉(zhuǎn)入內(nèi)核態(tài),而異常和中斷是被動(dòng)轉(zhuǎn)入內(nèi)核態(tài)。
異常又稱(chēng)內(nèi)中斷,來(lái)源于操作系統(tǒng)的內(nèi)部,通常是進(jìn)程執(zhí)行時(shí)引發(fā)了故障,如缺頁(yè)、地址越界、算術(shù)溢出、除數(shù)為零等。異常產(chǎn)生后,操作系統(tǒng)會(huì)奪回內(nèi)核的使用權(quán),對(duì)異常進(jìn)行處理。
外圍設(shè)備的中斷是指在外圍設(shè)備在處理完用戶(hù)要求的操作后,會(huì)向操作系統(tǒng)發(fā)出中斷信號(hào),此時(shí)操作系統(tǒng)會(huì)暫停執(zhí)行當(dāng)前正在執(zhí)行的指令,轉(zhuǎn)而處理中斷信號(hào)。這個(gè)過(guò)程需要在內(nèi)核態(tài)下進(jìn)行,因此會(huì)由用戶(hù)態(tài)進(jìn)入內(nèi)核態(tài)。

二、虛擬文件系統(tǒng)(VFS)

1. 什么是虛擬文件系統(tǒng)

我們?cè)赨nix/Linux系統(tǒng)下用戶(hù)程序操作普通文件,即txt、pdf、mp4等,使用open函數(shù)打開(kāi)文件,使用read、write函數(shù)對(duì)文件進(jìn)行讀寫(xiě),使用close函數(shù)關(guān)閉文件。但除了普通文件外,Unix/Linux的創(chuàng)作者提出“一切皆文件”的思想,希望將目錄、字符設(shè)備、塊設(shè)備、套接字等也被等同于文件對(duì)待,使用普通文件使用相同的文件操作接口,如open、read、write,對(duì)這些設(shè)備進(jìn)行操作。

虛擬文件系統(tǒng)(Virtual File System,VFS)是Linux系統(tǒng)中的一個(gè)軟件抽象層,它就是實(shí)現(xiàn)上述功能的關(guān)鍵。它為用戶(hù)空間的程序提供了文件系統(tǒng)接口,同時(shí)定義了所有文件系統(tǒng)都支持的基本的、概念上的接口和數(shù)據(jù)結(jié)構(gòu)。例如,讀寫(xiě)普通的文本文件和讀寫(xiě)I/O設(shè)備的具體實(shí)現(xiàn)方法必然是不同的,但VFS提供了統(tǒng)一的接口read和write,開(kāi)發(fā)人員需要編寫(xiě)這些接口的具體的不同的實(shí)現(xiàn)。因此,在VFS層和內(nèi)核的其他部分看來(lái),所有文件都只需調(diào)用read和write函數(shù)就可以完成讀寫(xiě)功能,具體的實(shí)現(xiàn)過(guò)程它們并不關(guān)心。

2. 文件接口結(jié)構(gòu)體file_operation

Linux內(nèi)核定義了結(jié)構(gòu)體file_operation,作為提供給VFS的文件接口。該結(jié)構(gòu)體的定義如下。

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);
};

file_operations的成員變量基本上都是函數(shù)指針,驅(qū)動(dòng)開(kāi)發(fā)者需要根據(jù)不同設(shè)備的要求,為這些函數(shù)指針編寫(xiě)具體的實(shí)現(xiàn)。以open函數(shù)為例,不同設(shè)備的打開(kāi)方式不同,因此open函數(shù)的實(shí)現(xiàn)顯然應(yīng)該是不同的,但從VFS層面上看,都是只需要調(diào)用open函數(shù)就可以滿(mǎn)足打開(kāi)設(shè)備的需求。

三、設(shè)備號(hào)

1. 什么是設(shè)備號(hào)

Linux系統(tǒng)使用設(shè)備號(hào)來(lái)區(qū)分各個(gè)設(shè)備。設(shè)備號(hào)是一個(gè)32位的無(wú)符號(hào)整型數(shù),它的高12位是主設(shè)備號(hào),低20位是次設(shè)備號(hào)。

為什么要區(qū)分主設(shè)備號(hào)和次設(shè)備號(hào)呢?

舉個(gè)例子,在一個(gè)硬件上可能有多個(gè)串口,系統(tǒng)會(huì)將每個(gè)串口都作為一個(gè)設(shè)備處理。但很多時(shí)候,這些串口發(fā)揮的作用是相同的,只有地址不同。我們希望這些串口都由同一個(gè)驅(qū)動(dòng)管理,而不是為每個(gè)串口都寫(xiě)一個(gè)驅(qū)動(dòng)程序,因此Linux將設(shè)備號(hào)分為主設(shè)備號(hào)和次設(shè)備號(hào)。我們將這些串口分配相同的主設(shè)備號(hào)和不同的次設(shè)備號(hào),這樣只要使用同一個(gè)驅(qū)動(dòng)程序即可。

/proc/devices文件下記錄著每個(gè)設(shè)備和它對(duì)應(yīng)的設(shè)備號(hào),注意,字符設(shè)備和塊設(shè)備是分開(kāi)獨(dú)立編號(hào)的。(/proc目錄用來(lái)存放系統(tǒng)進(jìn)程運(yùn)行時(shí)使用的文件)

2. 與設(shè)備號(hào)有關(guān)的宏

Linux內(nèi)核定義了一些與設(shè)備號(hào)有關(guān)的宏,常見(jiàn)的宏如下。

#define MKDEV(major, minor) //將主設(shè)備號(hào)和次設(shè)備號(hào)拼接成設(shè)備號(hào)
#define MAJOR(devno) //從設(shè)備號(hào)中提取主設(shè)備號(hào)
#define MINOR(devno) //從設(shè)備號(hào)中提取次設(shè)備號(hào)

3. 分配和釋放設(shè)備號(hào)的函數(shù)

register_chrdev_region函數(shù)用來(lái)將一系列字符設(shè)備號(hào)分配給字符設(shè)備。這一系列字符設(shè)備有相同的主設(shè)備號(hào),次設(shè)備號(hào)是連續(xù)的。

int register_chrdev_region(dev_t from, unsigned count, const char *name)

各個(gè)參數(shù)的含義。

參數(shù) 含義
from 要分配的這一系列設(shè)備號(hào)的第一個(gè)設(shè)備號(hào)
count 分配的設(shè)備號(hào)的個(gè)數(shù)
name 設(shè)備的名字

返回值。設(shè)備分配成功時(shí)返回0,分配失敗時(shí),例如要分配的設(shè)備號(hào)已經(jīng)被占用了,則返回一個(gè)負(fù)值的錯(cuò)誤碼。

unregister_chrdev_region函數(shù)用來(lái)將設(shè)備號(hào)釋放掉。由于設(shè)備號(hào)是臨界資源,同一個(gè)設(shè)備號(hào)不能被多個(gè)設(shè)備共有,因此設(shè)備使用結(jié)束后一定要及時(shí)釋放,以免不必要的資源浪費(fèi)。

void unregister_chrdev_region(dev_t from, unsigned count)

該函數(shù)參數(shù)的含義與register_chrdev_region參數(shù)的含義相同。

4. 設(shè)備號(hào)分配的原理

在Linux內(nèi)核中定義了一個(gè)全局指針數(shù)組chrdevs,用來(lái)管理分配出去的設(shè)備號(hào)。chrdevs的每一個(gè)元素都是一個(gè)指向char_device_struct結(jié)構(gòu)體的指針。它的具體定義如下。

fs/char_dev.c
-----------------------------------------------------------------------------------------------
#define CHRDEV_MAJOR_HASH_SIZE 255

static struct char_device_struct {
    struct char_device_struct *next;
    unsigned int major;
    unsigned int baseminor;
    int minorct;
    char name[64];
    struct cdev *cdev;
} *chrdevs[CHRDEV_MAJOR_HASH_SIZE];

每個(gè)成員的含義如下。

成員 含義
next 鏈表中指向下一個(gè)節(jié)點(diǎn)的指針
major 主設(shè)備號(hào)
baseminor 次設(shè)備號(hào)的第一個(gè)
minorct 申請(qǐng)的次設(shè)備號(hào)的數(shù)量
name 設(shè)備的名字
cdev 下文中會(huì)具體介紹cdev結(jié)構(gòu)體

chrdevs實(shí)際上是一個(gè)哈希表,它的關(guān)鍵字為主設(shè)備號(hào)major,散列函數(shù)為index = major % 255。初始狀態(tài)下,沒(méi)有設(shè)備號(hào)被分配出去,chrdevs數(shù)組為空。一旦有設(shè)備號(hào)被分配,則首先為該設(shè)備創(chuàng)建一個(gè)char_device_struct結(jié)構(gòu)體對(duì)象,然后根據(jù)主設(shè)備號(hào)計(jì)算得到散列結(jié)果,將結(jié)構(gòu)體對(duì)象掛載在chrdevs中的對(duì)應(yīng)位置上。如果該位置已經(jīng)有節(jié)點(diǎn),則根據(jù)次設(shè)備號(hào)由小到大的尋找到合適的位置,掛載在對(duì)應(yīng)節(jié)點(diǎn)的next指針上,構(gòu)成有序列表。

下面舉個(gè)例子說(shuō)明這個(gè)過(guò)程。

初始狀態(tài)下的的chrdevs表,實(shí)際上是255個(gè)char_device_struct *類(lèi)型指針。


使用register_chrdev_region函數(shù),向系統(tǒng)注冊(cè)一個(gè)主設(shè)備號(hào)major = 257、次設(shè)備號(hào)為0到3的設(shè)備、名稱(chēng)為“dev1”的設(shè)備,則需要首先創(chuàng)建一個(gè)char_device_struct結(jié)構(gòu)體,并對(duì)結(jié)構(gòu)體中的成員進(jìn)行初始化,然后計(jì)算散列值i = 257 % 255 = 2,并讓chrdevs[2]這個(gè)指針指向這個(gè)結(jié)構(gòu)體。

如果我們?cè)傧蛳到y(tǒng)注冊(cè)一個(gè)主設(shè)備號(hào)為2、次設(shè)備號(hào)為0到1、名稱(chēng)為“dev2”的設(shè)備,則同樣創(chuàng)建一個(gè)char_device_struct結(jié)構(gòu)體并初始化。這次我們計(jì)算得到的散列值i = 2 % 255依舊為2,與上一個(gè)設(shè)備在哈希表上產(chǎn)生了沖突,這時(shí)結(jié)構(gòu)體中的指針發(fā)揮了作用,我們將新結(jié)構(gòu)體掛在老結(jié)構(gòu)體的前面或后面,形成一個(gè)鏈表即可。掛載的位置由主設(shè)備號(hào)的大小決定,我們要求形成的鏈表是一個(gè)major由小到大排序的有序鏈表。

四、字符設(shè)備的內(nèi)核抽象

1. 字符設(shè)備結(jié)構(gòu)體cdev

內(nèi)核為字符設(shè)備抽象出一個(gè)結(jié)構(gòu)體cdev,定義如下:

struct cdev {
    struct kobject      kobj;
    struct module       *owner;
    const struct file_operation *ops;
    struct list_head    list;
    dev_t               dev;
    unsigned int        count;
};

每個(gè)成員的含義如下。

成員 含義
kobj 內(nèi)嵌的內(nèi)核對(duì)象,此處不展開(kāi)討論
owner 字符設(shè)備驅(qū)動(dòng)程序所在的內(nèi)核模塊指針,此處不展開(kāi)討論
ops 提供給VFS的文件接口結(jié)構(gòu)體
list 將系統(tǒng)中的字符設(shè)備形成鏈表
dev 字符設(shè)備的設(shè)備號(hào)
count 隸屬于同一主設(shè)備號(hào)的次設(shè)備號(hào)的數(shù)量,表示當(dāng)前設(shè)備驅(qū)動(dòng)程序控制的設(shè)備的數(shù)量

2. 字符設(shè)備的初始化

Linux內(nèi)核為初始化cdev對(duì)象提供了cdev_init函數(shù),定義如下。

fs/char_dev.c
-----------------------------------------------------------------------------------------------
void cdev_init(struct cdev *cdev, const struct file_operations *fops)
{
    memset(cdev, 0, sizeof *cdev);
    INIT_LIST_HEAD(&cdev->list);
    kobject_init(&cdev->kobj, &ktype_cdev_default);
    cdev->ops = fops;
}

這個(gè)函數(shù)的作用就是針對(duì)一個(gè)cdev結(jié)構(gòu)體里面的成員進(jìn)行初始化,其中最重要的一步就是把cdev和fops連接在一起,因?yàn)槊恳粋€(gè)設(shè)備都會(huì)有一個(gè)自己的文件操作邏輯,即需要實(shí)現(xiàn)一個(gè)自己的file_operations結(jié)構(gòu)體。

3. 向系統(tǒng)添加或刪除字符設(shè)備

將字符設(shè)備加入到系統(tǒng)中,簡(jiǎn)單地說(shuō),就是將上文中我們初始化的cdev結(jié)構(gòu)體添加到Linux系統(tǒng)維護(hù)的一個(gè)全局哈希鏈表cdev_map中,這樣其他模塊才能使用通過(guò)cdev_map找到它。Linux為了完成這個(gè)操作,提供了cdev_add函數(shù),實(shí)現(xiàn)如下。

fs/char_dev.c
-----------------------------------------------------------------------------------------------
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);
}

函數(shù)參數(shù)的含義如下。

參數(shù) 含義
p 要加入系統(tǒng)的字符設(shè)備對(duì)象的指針
dev 該設(shè)備的設(shè)備號(hào)
count 被注冊(cè)的設(shè)備數(shù)量

該函數(shù)的核心功能在kobj_map函數(shù)中實(shí)現(xiàn),該函數(shù)的實(shí)現(xiàn)略微有些復(fù)雜,此處不再贅述,有興趣的可以閱讀附錄

相應(yīng)的,刪除設(shè)備需要使用cdev_del函數(shù)。該函數(shù)的作用就是將對(duì)應(yīng)的設(shè)備從系統(tǒng)中移除,即從cdev_map鏈表中刪除節(jié)點(diǎn)并釋放內(nèi)存空間。其定義如下。

fs/char_dev.c
-----------------------------------------------------------------------------------------------
void cdev_del(struct cdev *p)
{
    cdev_unmap(p->dev, p->count);
    kobject_put(&p-> kobj);
}

五、設(shè)備節(jié)點(diǎn)

1. 什么是設(shè)備節(jié)點(diǎn)

設(shè)備節(jié)點(diǎn)也可以稱(chēng)為設(shè)備文件,是一種特殊類(lèi)型的文件,其作用是溝通用戶(hù)空間的應(yīng)用程序和內(nèi)核空間的驅(qū)動(dòng)程序的節(jié)點(diǎn)。應(yīng)用程序如果想使用驅(qū)動(dòng)程序提供的服務(wù),則必須經(jīng)過(guò)設(shè)備節(jié)點(diǎn)實(shí)現(xiàn)。

2. 設(shè)備節(jié)點(diǎn)的創(chuàng)建

設(shè)備節(jié)點(diǎn)有靜態(tài)創(chuàng)建和動(dòng)態(tài)創(chuàng)建兩種方式,我們這里只介紹靜態(tài)創(chuàng)建方法。Linux系統(tǒng)使用mknod命令靜態(tài)創(chuàng)建設(shè)備節(jié)點(diǎn),該命令需要列出設(shè)備節(jié)點(diǎn)的設(shè)備節(jié)點(diǎn)名、主設(shè)備號(hào)和次設(shè)備號(hào)。通常情況下,Linux系統(tǒng)將所有設(shè)備節(jié)點(diǎn)都放在/dev目錄下,因此我們也將設(shè)備節(jié)點(diǎn)創(chuàng)建在/dev目錄下。該命令具體格式如下。

mknod /dev/<設(shè)備節(jié)點(diǎn)名> c <主設(shè)備號(hào)> <次設(shè)備號(hào)>

參數(shù)c表示節(jié)點(diǎn)的類(lèi)型為字符設(shè)備。

mknod命令是通過(guò)調(diào)用mknod函數(shù)實(shí)現(xiàn),它會(huì)通過(guò)系統(tǒng)調(diào)用sys_mknod進(jìn)入內(nèi)核空間,并生成一個(gè)inode。

inode是Linux文件管理系統(tǒng)維護(hù)的一個(gè)結(jié)構(gòu)體,每一個(gè)文件(不限于設(shè)備節(jié)點(diǎn))都會(huì)有一個(gè)自己inode,用來(lái)存儲(chǔ)文件的靜態(tài)信息,如文件訪問(wèn)權(quán)限、屬主、組、大小、生成時(shí)間、訪問(wèn)時(shí)間、最后修改時(shí)間等。

由于inode結(jié)構(gòu)體中的成員非常多,我們此處不再一一列舉,只列舉幾個(gè)常用的。

struct inode {
    kuid_t      i_uid;   //屬主的ID(UID)
    kgid_t      i_gid;   //屬主的組ID(GID)
    loff_t      i_size;  //文件大小
    dev_t       i_rdev;
    struct cdev *i_cdev;
    ......
};

這里我們主要用到i_rdev和i_cdev兩個(gè)成員,i_rdev是該設(shè)備的設(shè)備號(hào),i_cdev是該設(shè)備的cdev結(jié)構(gòu)體。

3. 設(shè)備節(jié)點(diǎn)的打開(kāi)

在創(chuàng)建了設(shè)備節(jié)點(diǎn)和它的inode后,應(yīng)用程序就可以像打開(kāi)普通文件一樣使用open函數(shù)打開(kāi)設(shè)備節(jié)點(diǎn)。用戶(hù)空間下的應(yīng)用程序使用open系統(tǒng)調(diào)用,其函數(shù)原型如下。

int open(const char *filename, int flags, mode_t mode);

參數(shù)的含義

參數(shù) 含義
filename 要打開(kāi)的文件的文件名
flags 該文件的打開(kāi)模式或創(chuàng)建模式
mode 僅在創(chuàng)建一個(gè)新文件時(shí)使用,用于指定新建文件的訪問(wèn)權(quán)限
返回值 成功時(shí)返回該文件的文件描述符,失敗時(shí)返回-1

文件描述符(File Discriptor, fd)本質(zhì)是一個(gè)int型變量(非負(fù)整數(shù)),可以看作Linux系統(tǒng)為每個(gè)打開(kāi)的文件分配的一個(gè)索引值。在后續(xù)對(duì)該文件的read、write、close等操作時(shí),都要通過(guò)這個(gè)索引值,來(lái)找到這個(gè)打開(kāi)的文件。

系統(tǒng)調(diào)用open通過(guò)層層復(fù)雜的機(jī)制,最終會(huì)調(diào)用到我們?yōu)樵撛O(shè)備編寫(xiě)的file_operations結(jié)構(gòu)體中的.open函數(shù)中,最終實(shí)現(xiàn)該設(shè)備的打開(kāi)操作。

Linux會(huì)為每一個(gè)打開(kāi)的文件維護(hù)一個(gè)file結(jié)構(gòu)體,它在文件打開(kāi)時(shí)被創(chuàng)建,直到該文件關(guān)閉時(shí)被釋放。與inode結(jié)構(gòu)體不同,一個(gè)文件只能有一個(gè)inode結(jié)構(gòu)體,但如果它被同時(shí)打開(kāi)很多次,那么Linux會(huì)為它創(chuàng)建多個(gè)file結(jié)構(gòu)體,且file結(jié)構(gòu)體最終指向同一個(gè)inode。

file結(jié)構(gòu)體中的成員較多,下面列舉一些常用的成員。

struct file {
    struct inode        *f_inode;
    const struct file_operations    *f_op;
    atomic_long_t       f_count;
    unsigned int        f_flags;
    fmode_t             f_mode;
    loff_t              f_pos;
    void                *private_data;
    ......
};

幾個(gè)常用的成員及其含義如下。

成員 含義
f_op 與文件關(guān)聯(lián)的各種操作file_operations
f_count 記錄該文件對(duì)象被引用的次數(shù),也就是有多少個(gè)進(jìn)程正在使用該文件
f_flags 文件打開(kāi)時(shí)指定的標(biāo)志,驅(qū)動(dòng)程序中常用O_NONBLOCK來(lái)檢查是否是非阻塞請(qǐng)求
f_mode 文件的讀寫(xiě)模式,通過(guò)置位FMODE_READ和FMODE_WRITE,來(lái)確定文件是可讀、可寫(xiě)或者既可讀又可寫(xiě)的
f_pos 文件當(dāng)前的讀寫(xiě)位置,本質(zhì)是一個(gè)long long類(lèi)型的值
private_data 用來(lái)保存自定義設(shè)備結(jié)構(gòu)體的地址

以上就是字符設(shè)備中常用的數(shù)據(jù)結(jié)構(gòu)和算法,在下一篇博客中,我會(huì)舉一個(gè)實(shí)例,來(lái)使用上述內(nèi)容完成一個(gè)應(yīng)用程序與驅(qū)動(dòng)程序交互的實(shí)例。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容