在這一節(jié)中,我們來(lái)一起學(xué)習(xí)和完成文件樹(shù)中最后一個(gè)關(guān)鍵性內(nèi)容——一切皆文件的設(shè)計(jì)理念。
所謂一切皆文件就是指計(jì)算機(jī)操作系統(tǒng)將一切計(jì)算機(jī)的可用資源都映射成文件形式向使用者提供統(tǒng)一的操作方式。我們?cè)诘谝还?jié)時(shí)已經(jīng)有了明確的講述,在這一節(jié)中我們來(lái)看一下具體的設(shè)計(jì)理念和實(shí)現(xiàn)方法。我們?cè)诓僮飨到y(tǒng)中為用戶構(gòu)建的虛擬系統(tǒng)中,樹(shù)的每一個(gè)節(jié)點(diǎn)都是一個(gè)文件,而這些文件雖然有著不同的類型和功能,如普通文件、鍵盤(pán)、鼠標(biāo)、打印機(jī)、顯示器、內(nèi)存頁(yè)、操作系統(tǒng)調(diào)度狀態(tài)等等,但卻有著相同的文件操作接口。對(duì)于用戶而言普通文件的操作通常只有“打開(kāi)”、“關(guān)閉”、“讀取”、“寫(xiě)入”這幾個(gè)操作,而對(duì)于較為特殊的文件,通常還需要加入“輸入輸出控制”、“嘗試獲取數(shù)據(jù)”這兩個(gè)操作,因此對(duì)于虛擬文件系統(tǒng)中的文件我們可以為其定義這樣6個(gè)通用的操作:
- open()
- close()
- read()
- write()
- ioctl()
- poll()
`
這6個(gè)函數(shù)是目前操作系統(tǒng)中對(duì)文件操作的函數(shù),當(dāng)然有的操作系統(tǒng)還實(shí)現(xiàn)了一些其它的操作函數(shù),我們不一一列舉,只是針對(duì)這6個(gè)最具有代表性的函數(shù)進(jìn)行說(shuō)明。用戶對(duì)文件樹(shù)中的文件進(jìn)程上面6個(gè)操作,也就是說(shuō)每一個(gè)文件節(jié)點(diǎn)中應(yīng)該有這6個(gè)函數(shù)的實(shí)現(xiàn)方法。具體來(lái)說(shuō),我們?cè)谔摂M文件樹(shù)中注冊(cè)一個(gè)設(shè)備節(jié)點(diǎn)時(shí),這個(gè)設(shè)備節(jié)點(diǎn)就是虛擬文件樹(shù)中的一個(gè)文件,我們可以為此設(shè)備節(jié)點(diǎn)的結(jié)構(gòu)體加入上述6個(gè)函數(shù)指針:
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;
在這里我們定義了一個(gè)結(jié)構(gòu)體struct file_operations_s,這個(gè)結(jié)構(gòu)體中定義了6個(gè)函數(shù)指針,用于表示這個(gè)文件的通用的6個(gè)操作。而具體的實(shí)現(xiàn)由注冊(cè)這個(gè)設(shè)備節(jié)點(diǎn)的具體驅(qū)動(dòng)程序來(lái)實(shí)現(xiàn)。接下來(lái)我們來(lái)實(shí)現(xiàn)當(dāng)用戶對(duì)某一個(gè)文件進(jìn)行這6個(gè)操作時(shí),虛擬文件系統(tǒng)的具體實(shí)現(xiàn)方法:
//打開(kāi)文件
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);
//申請(qǐng)節(jié)點(diǎn)
fcntl_alloc(pcb, ind);
pcb- >fnodes[ind] = node;
return ind;
}
//關(guān)閉文件
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;
}
//讀取文件內(nèi)容
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);
}
//寫(xiě)入文件內(nèi)容
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);
}
最后,我們來(lái)具體看一下如何編寫(xiě)一個(gè)驅(qū)動(dòng)程序,并將這個(gè)驅(qū)動(dòng)程序注冊(cè)為虛擬文件系統(tǒng)中的一個(gè)設(shè)備節(jié)點(diǎn),即文件。用戶又如何通過(guò)通用的操作來(lái)實(shí)現(xiàn)對(duì)此設(shè)備的控制。
最簡(jiǎn)單的,我們可以通過(guò)對(duì)一個(gè)GPIO引腳的拉高拉低來(lái)實(shí)現(xiàn)一個(gè)LED燈的亮和滅,使用Cortex-M3處理下的實(shí)現(xiàn)方式有兩步,第一步:初始化GPIO引腳;第二步:對(duì)GPIO進(jìn)行拉高或拉低,從而達(dá)到LED亮和滅的操作。具體實(shí)現(xiàn)程序如下:
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);
接下來(lái),當(dāng)我們需要點(diǎn)亮LED燈時(shí),對(duì)PC4引腳拉低;當(dāng)需要熄滅時(shí),對(duì)PC4引腳拉高,于是程序如下:
//點(diǎn)亮,拉低
GPIO_WriteBit(GPIOC, GPIO_Pin_4, 0);
//熄滅,拉高
GPIO_WriteBit(GPIOC, GPIO_Pin_4, 1);
對(duì)于驅(qū)動(dòng)程序而言,我們不希望用戶在使用LED時(shí)編寫(xiě)較為復(fù)雜的、直接操作硬件的代碼,而是希望操作系統(tǒng)為用戶提供一個(gè)通用的操作接口函數(shù),于是我們就可以編寫(xiě)一個(gè)驅(qū)動(dòng)程序,并向操作系統(tǒng)注冊(cè)一個(gè)"/dev/led"設(shè)備節(jié)點(diǎn),而"/dev/led"這個(gè)文件就是一個(gè)設(shè)備文件,它的內(nèi)部由驅(qū)動(dòng)開(kāi)發(fā)人員完成與硬件交互的功能,對(duì)使用人員則只提供open()、close()、read()、write()、ioctl()、poll()等操作函數(shù)。對(duì)于LED燈來(lái)說(shuō),驅(qū)動(dòng)程序比較簡(jiǎn)單我們只實(shí)現(xiàn)其open()、close()、ioctl()這3個(gè)函數(shù)。有興趣的讀者可以自行實(shí)現(xiàn)read()、write()、poll()等函數(shù):
#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;
}
這樣我們就編寫(xiě)了一個(gè)LED燈的驅(qū)動(dòng)程序,并在操作系統(tǒng)中注冊(cè)為"/dev/led"設(shè)備節(jié)點(diǎn),即文件。用戶可以通過(guò)以下方式來(lái)操作這個(gè)LED設(shè)備:
int fd = open("/dev/led");
//點(diǎn)亮
ioctl(fd, LED_IOCTL_ON);
//熄滅
ioctl(fd, LED_IOCTL_OFF);
close(fd);
對(duì)于用戶而言,操作這個(gè)硬件LED燈就與操作普通文件一樣,通過(guò)open()函數(shù)打開(kāi)這個(gè)文件,通過(guò)ioctl這個(gè)函數(shù)對(duì)這個(gè)文件進(jìn)行相關(guān)的控制,使用完畢之后再通過(guò)close()函數(shù)關(guān)閉此文件,于是,硬件與用戶之間就減少了很多特定的功能操作,用戶也不必關(guān)心硬件設(shè)備的具體實(shí)現(xiàn)細(xì)節(jié),只需要對(duì)這個(gè)設(shè)備文件進(jìn)行通用操作即可。而對(duì)于編寫(xiě)驅(qū)動(dòng)程序的人員來(lái)說(shuō),只需要將硬件相關(guān)的程序和操作封裝到驅(qū)動(dòng)程序內(nèi)部即可,無(wú)需暴露給用戶。這樣就實(shí)現(xiàn)了“一切皆文件”的設(shè)計(jì)理念。
-
led燈
+關(guān)注
關(guān)注
22文章
1592瀏覽量
107950 -
計(jì)算機(jī)
+關(guān)注
關(guān)注
19文章
7488瀏覽量
87849 -
Cortex-M3
+關(guān)注
關(guān)注
9文章
269瀏覽量
59463 -
GPIO
+關(guān)注
關(guān)注
16文章
1204瀏覽量
52051 -
PCB
+關(guān)注
關(guān)注
1文章
1795瀏覽量
13204
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論