在這一節中,我們來一起學習和完成文件樹中最后一個關鍵性內容——一切皆文件的設計理念。
所謂一切皆文件就是指計算機操作系統將一切計算機的可用資源都映射成文件形式向使用者提供統一的操作方式。我們在第一節時已經有了明確的講述,在這一節中我們來看一下具體的設計理念和實現方法。我們在操作系統中為用戶構建的虛擬系統中,樹的每一個節點都是一個文件,而這些文件雖然有著不同的類型和功能,如普通文件、鍵盤、鼠標、打印機、顯示器、內存頁、操作系統調度狀態等等,但卻有著相同的文件操作接口。對于用戶而言普通文件的操作通常只有“打開”、“關閉”、“讀取”、“寫入”這幾個操作,而對于較為特殊的文件,通常還需要加入“輸入輸出控制”、“嘗試獲取數據”這兩個操作,因此對于虛擬文件系統中的文件我們可以為其定義這樣6個通用的操作:
- open()
- close()
- read()
- write()
- ioctl()
- poll()
`
這6個函數是目前操作系統中對文件操作的函數,當然有的操作系統還實現了一些其它的操作函數,我們不一一列舉,只是針對這6個最具有代表性的函數進行說明。用戶對文件樹中的文件進程上面6個操作,也就是說每一個文件節點中應該有這6個函數的實現方法。具體來說,我們在虛擬文件樹中注冊一個設備節點時,這個設備節點就是虛擬文件樹中的一個文件,我們可以為此設備節點的結構體加入上述6個函數指針:
typedef struct file_operations_s
{
int (*open)(void);
int (*close)(void);
size_t (*read)(void *, size_t);
size_t (*write)(const void *, size_t);
int (*ioctl)(unsigned int, unsigned long);
int (*poll)(unsigned int);
} file_operations_s;
typedef struct vfs_node_s
{
struct vfs_node_s *sibling;
struct vfs_node_s *child;
char name[NODE_NAME_SIZE];
struct file_operations_s ops;
} vfs_node_s;
在這里我們定義了一個結構體struct file_operations_s,這個結構體中定義了6個函數指針,用于表示這個文件的通用的6個操作。而具體的實現由注冊這個設備節點的具體驅動程序來實現。接下來我們來實現當用戶對某一個文件進行這6個操作時,虛擬文件系統的具體實現方法:
//打開文件
int open(char *path, int oflag, int mode)
{
vfs_node_s *node = fs_get_node(path);
pcb_s *pcb = sche_curr_pcb();
node- >ops.open();
uint32_t ind = fcntl_first_empty(pcb);
//申請節點
fcntl_alloc(pcb, ind);
pcb- >fnodes[ind] = node;
return ind;
}
//關閉文件
int close(int fd)
{
pcb_s *pcb = sche_curr_pcb();
vfs_node_s *node = pcb- >fnodes[fd];
int ret = node- >ops.close();
pcb- >fnodes[fd] = NULL;
fcntl_free(pcb, fd);
return ret;
}
//讀取文件內容
size_t read(int fd, void *buf, size_t count)
{
pcb_s *pcb = sche_curr_pcb();
vfs_node_s *node = pcb- >fnodes[fd];
return node- >ops.read(NULL, buf, count);
}
//寫入文件內容
size_t write(int fd, void *buf, size_t count)
{
pcb_s *pcb = sche_curr_pcb();
vfs_node_s *node = pcb- >fnodes[fd];
return node- >ops.write(NULL, buf, count);
}
//輸入輸出控制
int ioctl(int fd, unsigned int cmd, unsigned long arg)
{
pcb_s *pcb = sche_curr_pcb();
vfs_node_s *node = pcb- >fnodes[fd];
return node- >ops.ioctl(NULL, cmd, arg);
}
//嘗試獲取資源
int poll(int fd, unsigned int ms)
{
pcb_s *pcb = sche_curr_pcb();
vfs_node_s *node = pcb- >fnodes[fd];
return node- >ops.poll(ms);
}
最后,我們來具體看一下如何編寫一個驅動程序,并將這個驅動程序注冊為虛擬文件系統中的一個設備節點,即文件。用戶又如何通過通用的操作來實現對此設備的控制。
最簡單的,我們可以通過對一個GPIO引腳的拉高拉低來實現一個LED燈的亮和滅,使用Cortex-M3處理下的實現方式有兩步,第一步:初始化GPIO引腳;第二步:對GPIO進行拉高或拉低,從而達到LED亮和滅的操作。具體實現程序如下:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOC, &GPIO_InitStructure);
接下來,當我們需要點亮LED燈時,對PC4引腳拉低;當需要熄滅時,對PC4引腳拉高,于是程序如下:
//點亮,拉低
GPIO_WriteBit(GPIOC, GPIO_Pin_4, 0);
//熄滅,拉高
GPIO_WriteBit(GPIOC, GPIO_Pin_4, 1);
對于驅動程序而言,我們不希望用戶在使用LED時編寫較為復雜的、直接操作硬件的代碼,而是希望操作系統為用戶提供一個通用的操作接口函數,于是我們就可以編寫一個驅動程序,并向操作系統注冊一個"/dev/led"設備節點,而"/dev/led"這個文件就是一個設備文件,它的內部由驅動開發人員完成與硬件交互的功能,對使用人員則只提供open()、close()、read()、write()、ioctl()、poll()等操作函數。對于LED燈來說,驅動程序比較簡單我們只實現其open()、close()、ioctl()這3個函數。有興趣的讀者可以自行實現read()、write()、poll()等函數:
#define LED_IOCTL_ON (0)
#define LED_IOCTL_OFF (0)
void led_drv_init(void)
{
file_operations_s ops = {0};
ops.open = led_open;
ops.close = led_close;
ops.read = NULL;
ops.write = NULL;
ops.ioctl = led_ioctl;
ops.poll = NULL;
fs_register_dev("/dev/led", ops);
}
int led_open(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOC, &GPIO_InitStructure);
return 0;
}
int led_close(void)
{
return 0;
}
int led_ioctl(void, unsigned int cmd, unsigned long arg)
{
switch (cmd)
{
case LED_IOCTL_ON:
GPIO_WriteBit(GPIOC, GPIO_Pin_4, 0);
break;
case LED_IOCTL_OFF:
GPIO_WriteBit(GPIOC, GPIO_Pin_4, 1);
break;
default:
break;
}
return 0;
}
這樣我們就編寫了一個LED燈的驅動程序,并在操作系統中注冊為"/dev/led"設備節點,即文件。用戶可以通過以下方式來操作這個LED設備:
int fd = open("/dev/led");
//點亮
ioctl(fd, LED_IOCTL_ON);
//熄滅
ioctl(fd, LED_IOCTL_OFF);
close(fd);
對于用戶而言,操作這個硬件LED燈就與操作普通文件一樣,通過open()函數打開這個文件,通過ioctl這個函數對這個文件進行相關的控制,使用完畢之后再通過close()函數關閉此文件,于是,硬件與用戶之間就減少了很多特定的功能操作,用戶也不必關心硬件設備的具體實現細節,只需要對這個設備文件進行通用操作即可。而對于編寫驅動程序的人員來說,只需要將硬件相關的程序和操作封裝到驅動程序內部即可,無需暴露給用戶。這樣就實現了“一切皆文件”的設計理念。
-
led燈
+關注
關注
22文章
1592瀏覽量
108026 -
計算機
+關注
關注
19文章
7500瀏覽量
88018 -
Cortex-M3
+關注
關注
9文章
270瀏覽量
59489 -
GPIO
+關注
關注
16文章
1204瀏覽量
52123 -
PCB
+關注
關注
1文章
1808瀏覽量
13204
發布評論請先 登錄
相關推薦
評論