姓名:李萌怡? 學(xué)號:19020100103? 學(xué)院:電子工程學(xué)院
轉(zhuǎn)自:https://blog.csdn.net/weixin_44839882/article/details/108468131
【嵌牛導(dǎo)讀】:嵌入式系統(tǒng)分為硬件層、驅(qū)動層、操作系統(tǒng)層和應(yīng)用層。要深入學(xué)習(xí)嵌入式系統(tǒng),應(yīng)用層開發(fā)是非常重要的部分。
【嵌牛鼻子】:應(yīng)用層? 文件結(jié)構(gòu)
【嵌牛提問】:Linux的文件結(jié)構(gòu)分為哪幾部分?
【嵌牛正文】
1 應(yīng)用層與驅(qū)動層
要想學(xué)習(xí)嵌入式Linux應(yīng)用層的開發(fā),首先要區(qū)分好應(yīng)用層和驅(qū)動層之間的關(guān)系。我們在本科階段學(xué)習(xí)51等較簡單的單片機(jī)時(shí),都是把應(yīng)用層和驅(qū)動層混在一個(gè)文件里寫的。比如拿下面的I2C程序?yàn)槔?/p>
#include<reg51.h>
#include<intrins.h>
#define uchar unsigned char
#define nop _nop_()
sbit sda = P2^1;//sda接在P2.1 ?
sbit scl = P2^0;//sda接在P2.0
void delay1()//用于每個(gè)語句間的延時(shí)
{
;//空語句
;
;
}
void delay(unsigned int i )//用于LED亮燈延時(shí)
{
while(i--);
}
/*scl在高電平期間,sda由高電平變成低電平時(shí),I2C啟動*/
void start_24c02() ? //用于24C02的啟動
{
scl = 1;
delay1(); ?
sda = 1;
delay1();
sda = 0;
delay1();
}
/*scl在高電平期間,sda由低電平變成高電平時(shí),I2C停止*/
void stop_24c02() //用于24c02啟動
{
sda = 0;
delay1();
scl = 1;
delay1();
sda = 1;
delay1();
}
/*scl在高電平期間,sda變成低電平表示應(yīng)答*/
void ack_24c02() //24C02的應(yīng)答
{
uchar i = 0;
scl = 1;
delay1();
while((sda ==1)&&(i<200)) //(i<200)表示:如果期間不給應(yīng)答信號,則程序會一直停在這里,為了避免長時(shí)間的等待應(yīng)答,在此處增加了一個(gè)延時(shí)
{
i++;
}
scl = 0;
delay1();
}
/*I2C總線初始化,將I2C總線全部設(shè)置為高電平,來釋放總線*/
void init_24c02()//I2C總線的初始化
{
sda = 1;
delay1();? ?
scl = 0;
delay1();
}
/*讀取一個(gè)字節(jié)*/
uchar read_onebyte_24c02()
{
uchar i,date;
scl=0;
delay1();
sda=1;
delay1();
for(i = 0;i<8;i++) ? ? ? ? ? ?
{
scl = 1; ?
delay1();
date=(date<<1)|sda; ? ?
scl = 0;
delay1();
}
delay1();
return date;
}
/*寫一個(gè)字節(jié)*/
void write_onebyte_24c02(uchar date)
{
uchar i,temp;
temp = date;
for(i=0;i<8;i++)
{
temp = temp<<1;
scl = 0;
delay1();
sda =CY;? //temp 左移一位,將移出的最高位字節(jié)放到PSW的CY中
delay1();
scl = 1;
delay1();
}
scl = 0;
delay1();
sda = 1;
delay1();
}
/*對從機(jī)的某一地址的某一位置寫入一個(gè)字節(jié)
1、找到要操作地址的從機(jī),同時(shí)進(jìn)行寫操作0xA0
2、要寫入的位置add
3、要寫入的數(shù)據(jù)dat
*/
void write_add_dat_24c02(uchar add,uchar dat)
{
start_24c02();
write_onebyte_24c02(0xA0); //進(jìn)行寫操作0XA0,24C02 的高四位地址為1010;后三位地址A2、A1、A0全部接地,所以為000,;由于為寫操作,所以最后一位也為0
ack_24c02();
write_onebyte_24c02(add);
ack_24c02();
write_onebyte_24c02(dat);
ack_24c02();
stop_24c02();
}
/*讀取某地址的一個(gè)字節(jié)
1、找到要操作地址的從機(jī),同時(shí)進(jìn)行寫操作0xA0
2、要寫入的地址add
3、對選中地址的從機(jī)進(jìn)行讀操作 0xA1
4、調(diào)用讀取一個(gè)字節(jié)函數(shù)read_onebyte_24c02(),進(jìn)行數(shù)據(jù)讀取
*/
uchar read_add_dat_24c02(uchar add)
{
uchar dat;
start_24c02();
write_onebyte_24c02(0xa0);
ack_24c02();
write_onebyte_24c02(add);
ack_24c02();
start_24c02();
write_onebyte_24c02(0xa1); //進(jìn)行讀操作0XA0,24C02 的高四位地址為1010;后三位地址A2、A1、A0全部接地,所以為000,;由于為讀操作,所以最后一位為1
ack_24c02();
dat = read_onebyte_24c02();
ack_24c02();
stop_24c02();
return (dat);
}
void main()
{
init_24c02(); //初始化I2C
while(1)
{
write_add_dat_24c02(3,0x0f); //對地址3進(jìn)行寫入數(shù)據(jù)
delay(200);
write_add_dat_24c02(4,0xf0); //對地址4進(jìn)行寫入數(shù)據(jù)
delay(200);
P3 = read_add_dat_24c02(3); //讀取地址3的數(shù)據(jù)
delay(55000); //保持燈亮
P3 = read_add_dat_24c02(4); //讀取地址4的數(shù)據(jù)
delay(55000); //保持燈亮
}
}
該程序所要實(shí)現(xiàn)的功能是將數(shù)據(jù)0x0f和0xf0分別寫入地址3和地址4。這個(gè)目標(biāo)可以分為兩部分執(zhí)行:第一部分是計(jì)算出要存儲的數(shù)據(jù)(由于該程序要存儲的數(shù)據(jù)已經(jīng)給出來了,就不用算了,但在實(shí)際工程中,這些數(shù)據(jù)一般是要自己獲取的),第二部分是按照I2C協(xié)議的時(shí)序?qū)⑦@些數(shù)據(jù)發(fā)送給硬件。于是,上面提到的第一部分被稱為應(yīng)用層開發(fā),第二部分被稱為驅(qū)動層開發(fā)。
我們觀察以上示例代碼,發(fā)現(xiàn)作者還是很規(guī)矩地把驅(qū)動層程序編程了函數(shù)的形式(例如write_add_dat_24c02()),但應(yīng)用層和驅(qū)動層的程序終究還是存儲在同一個(gè)c文件中。而在Linux系統(tǒng)中,驅(qū)動層和應(yīng)用層分的很清楚,它們分別保存在兩個(gè)不同的文件中。并且應(yīng)用層的程序運(yùn)行在用戶空間中,驅(qū)動層的程序則是被編進(jìn)了Linux內(nèi)核里。在實(shí)際調(diào)用程序的過程中,由應(yīng)用層程序執(zhí)行數(shù)據(jù)計(jì)算等任務(wù),任務(wù)執(zhí)行完后通過一個(gè)接口將算好的數(shù)據(jù)發(fā)送給驅(qū)動層程序,最后由驅(qū)動層程序進(jìn)行硬件實(shí)現(xiàn)。這樣的分層有一個(gè)好處,就是對于調(diào)用同一個(gè)硬件的應(yīng)用程序,他的驅(qū)動層可以不用修改,只修改應(yīng)用層程序就可以了。
由于Linux操作系統(tǒng)現(xiàn)在發(fā)展的已經(jīng)比較完善了,很多硬件設(shè)備都能在網(wǎng)上找到寫好的驅(qū)動,因此在進(jìn)行嵌入式Linux開發(fā)的過程中,一般更多地進(jìn)行應(yīng)用層的開發(fā),只是當(dāng)找不到合適的驅(qū)動時(shí),才會在現(xiàn)有驅(qū)動程序的基礎(chǔ)上進(jìn)行適當(dāng)改寫,以使其適應(yīng)我們的硬件。
2 Linux的文件結(jié)構(gòu)
Linux下一切皆文件。在Linux系統(tǒng)中,不僅像文本文檔等傳統(tǒng)文件是文件,各種設(shè)備也可以被映射成一個(gè)文件。通過對設(shè)備文件進(jìn)行操作就可以實(shí)現(xiàn)對設(shè)備的操作。因此,了解Linux的文件結(jié)構(gòu)非常重要。
2.1 目錄
目錄是用來保存其他文件節(jié)點(diǎn)(inode)號和名字的文件,目錄文件中的每項(xiàng)數(shù)據(jù)都指向一個(gè)文件的節(jié)點(diǎn),刪除一個(gè)文件就相當(dāng)于刪除了目錄文件中對應(yīng)的節(jié)點(diǎn)項(xiàng)。
那么問題來了,什么是文件的節(jié)點(diǎn)(inode)呢?文件節(jié)點(diǎn)其實(shí)就是保存了文件的屬性的一個(gè)東西,這些屬性包括文件的創(chuàng)建/修改日期、訪問權(quán)限、文件位置、文件長度等等。Linux在尋找某文件或?qū)?,某個(gè)文件進(jìn)行操作時(shí),不是去尋找文件的名字,而是去尋找文件對應(yīng)的節(jié)點(diǎn)。
Linux系統(tǒng)中最常用的目錄之一就是家目錄。Linux系統(tǒng)會為每個(gè)用戶創(chuàng)建一個(gè)家目錄。比如你的用戶名叫neil,那你的家目錄就是/home/neil/。很多Linux系統(tǒng),比如Ubuntu,都允許用~ 符號代替用戶的家目錄。然而,標(biāo)準(zhǔn)庫函數(shù)不能識別文件參數(shù)中的~ 符號,因此在程序中不要使用波浪線符號代替家目錄。
所有目錄的最頂層是根目錄。在它下面包含了存放系統(tǒng)程序的/bin目錄、存放系統(tǒng)配置文件的/etc目錄、存放系統(tǒng)函數(shù)庫的/lib目錄和存放代表物理設(shè)備的設(shè)備文件的/dev目錄等等。
2.2 文件和設(shè)備
在Linux中,設(shè)備可以被映射成一個(gè)文件。例如,我們可以使用以下命令將CD-ROM驅(qū)動掛載為一個(gè)文件:
mount -t iso9660 /dev/hdc /mnt/cdrom
cd /mnt/cdrom
這時(shí)我們進(jìn)入到/mnt/cdrom下就可以查看CD-ROM中包含的目錄,只不過其中的目錄都是只讀的。
Linux中有3個(gè)比較重要的設(shè)備文件:/dev/console、/dev/tty和/dev/null。
/dev/console
這個(gè)設(shè)備代表系統(tǒng)控制臺。錯(cuò)誤信息和診斷信息通常會被發(fā)到這個(gè)設(shè)備。
/dev/tty
如果一個(gè)進(jìn)程有控制終端的話,文件/dev/tty就會作為控制終端的別名。但對于系統(tǒng)自動運(yùn)行的進(jìn)程和腳本,它們就沒有控制終端,不需要訪問設(shè)備/dev/tty。
/dev/null
這個(gè)設(shè)備是“空”設(shè)備,所有寫向這個(gè)設(shè)備的輸出都會被丟棄,而讀這個(gè)設(shè)備會返回一個(gè)文件尾標(biāo)志。通常用于作復(fù)制空文件的源文件,或?qū)⒉恍枰妮敵鲋囟ㄏ虻酱宋募?/p>
設(shè)備可以分為字符設(shè)備和塊設(shè)備,兩者的區(qū)別在于訪問設(shè)備時(shí)是否需要一次讀寫一整塊。字符設(shè)備通常是普通的設(shè)備,塊設(shè)備通常是硬盤、SD卡等存儲設(shè)備。
————————————————
版權(quán)聲明:本文為CSDN博主「畢浩然」的原創(chuàng)文章,遵循CC 4.0 BY-SA版權(quán)協(xié)議,轉(zhuǎn)載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/weixin_44839882/article/details/108468131