|
操作系統(tǒng)API: 1、API是一些函數(shù),這些函數(shù)是由linux系統(tǒng)提供支持的,由應(yīng)用層程序來(lái)使用,應(yīng)用層程序通過(guò)調(diào)用API來(lái)調(diào)用操作系統(tǒng)中的各種功能,來(lái)干活 文件操作的一般步驟: 1、在linux系統(tǒng)中要操作一個(gè)文件,一般是先open打開一個(gè)文件,得到一個(gè)文件描述符,然后對(duì)文件進(jìn)行讀寫操作(或其他操作),最后close關(guān)閉文件即可 2、文件平時(shí)是存在塊設(shè)備中的文件系統(tǒng)中的,我們把這種文件叫靜態(tài)文件。當(dāng)我們?nèi)pen打開一個(gè)文件時(shí),linux內(nèi)核做的操作包括:內(nèi)核在進(jìn)程中建立了一個(gè)打開文件的數(shù)據(jù)結(jié)構(gòu),記錄下我們打開的這個(gè)文件;內(nèi)核在內(nèi)存中申請(qǐng)一段內(nèi)存,并且將靜態(tài)文件的內(nèi)容從塊設(shè)備中讀取到內(nèi)存中特定地址管理存放(叫動(dòng)態(tài)文件)。打開文件后,以后對(duì)這個(gè)文件的讀寫操作,都是針對(duì)內(nèi)存中這一份動(dòng)態(tài)文件的,而并不是針對(duì)靜態(tài)文件的。當(dāng)我們對(duì)動(dòng)態(tài)文件進(jìn)行讀寫后,此時(shí)內(nèi)存中的動(dòng)態(tài)文件和塊設(shè)備中的靜態(tài)文件就不同步了,當(dāng)我們close關(guān)閉動(dòng)態(tài)文件時(shí),close內(nèi)部?jī)?nèi)核將內(nèi)存中的動(dòng)態(tài)文件的內(nèi)容去更新(同步)塊設(shè)備中的靜態(tài)文件。 3、為什么要這么設(shè)計(jì)? 以為塊設(shè)備本身有讀寫限制(回憶NnadFlash、SD等塊設(shè)備的讀寫特征),本身對(duì)塊設(shè)備進(jìn)行操作非常不靈活。而內(nèi)存可以按字節(jié)為單位來(lái)操作,而且可以隨機(jī)操作(內(nèi)存就叫RAM,random),很靈活。所以內(nèi)核設(shè)計(jì)文件操作時(shí)就這么設(shè)計(jì)了 文件描述符: 1、文件描述符其實(shí)實(shí)質(zhì)是一個(gè)數(shù)字,這個(gè)數(shù)字在一個(gè)進(jìn)程中表示一個(gè)特定的含義,當(dāng)我們open打開一個(gè)文件時(shí),操作系統(tǒng)在內(nèi)存中構(gòu)建了一些數(shù)據(jù)結(jié)構(gòu)來(lái)表示這個(gè)動(dòng)態(tài)文件,然后返回給應(yīng)用程序一個(gè)數(shù)字作為文件描述符,這個(gè)數(shù)字就和我們內(nèi)存中維護(hù)這個(gè)動(dòng)態(tài)文件的這些數(shù)據(jù)結(jié)構(gòu)掛鉤綁定上了,以后我們應(yīng)用程序如果要操作這一個(gè)動(dòng)態(tài)文件,只需要用這個(gè)文件描述符進(jìn)行區(qū)分。 2、文件描述符的作用域就是當(dāng)前進(jìn)程,出了當(dāng)前進(jìn)程這個(gè)文件描述符就沒(méi)有意義了 實(shí)時(shí)查man手冊(cè) (1)當(dāng)我們寫應(yīng)用程序時(shí),很多API原型都不可能記得,所以要實(shí)時(shí)查詢,用man手冊(cè) (2)man 1 xx查linux shell命令,man 2 xxx查API, man 3 xxx查庫(kù)函數(shù) 讀取文件內(nèi)容 ssize_t read(int fd, void *buf, size_t count); fd表示要讀取哪個(gè)文件,fd一般由前面的open返回得到 buf是應(yīng)用程序自己提供的一段內(nèi)存緩沖區(qū),用來(lái)存儲(chǔ)讀出的內(nèi)容 count是我們要讀取的字節(jié)數(shù) 返回值ssize_t類型是linux內(nèi)核用typedef重定義的一個(gè)類型(其實(shí)就是int),返回值表示成功讀取的字節(jié)數(shù)。 exit、_exit、_Exit退出進(jìn)程 (1)當(dāng)我們程序在前面步驟操作失敗導(dǎo)致后面的操作都沒(méi)有可能進(jìn)行下去時(shí),應(yīng)該在前面的錯(cuò)誤監(jiān)測(cè)中結(jié)束整個(gè)程序,不應(yīng)該繼續(xù)讓程序運(yùn)行下去了。 (2)我們?nèi)绾瓮顺龀绦颍?/p> 第一種;在main用return,一般原則是程序正常終止return 0,如果程序異常終止則return -1。 第一種:正式終止進(jìn)程(程序)應(yīng)該使用exit或者_(dá)exit或者_(dá)Exit之一 open函數(shù)的flag詳解: 讀寫權(quán)限:O_RDONLY O_WRONLY O_RDWR O_RDONLY就表示以只讀方式打開, O_WRONLY表示以只寫方式打開, O_RDWR表示以可讀可寫方式打開 當(dāng)我們附帶了權(quán)限后,打開的文件就只能按照這種權(quán)限來(lái)操作 打開存在并有內(nèi)容的文件時(shí):O_APPEND、O_TRUNC O_APPEND屬性去打開文件時(shí),如果這個(gè)文件中本來(lái)是有內(nèi)容的,則新寫入的內(nèi)容會(huì)接續(xù)到原來(lái)內(nèi)容的后面; O_TRUNC屬性去打開文件時(shí),如果這個(gè)文件中本來(lái)是有內(nèi)容的,則原來(lái)的內(nèi)容會(huì)被丟棄 1 fd = open('a.txt', O_RDWR | O_APPEND | O_TRUNC); 2 if (-1 == fd) // 有時(shí)候也寫成: (fd < 0) 3 { 4 printf('文件打開錯(cuò)誤\n'); 5 // return -1; 6 _exit(-1); 7 } 8 else 9 {10 printf('文件打開成功,fd = %d.\n', fd);11 } 打開不存在的文件時(shí):O_CREAT、O_EXCL open中加入O_CREAT后,不管原來(lái)這個(gè)文件存在與否都能打開成功,如果原來(lái)這個(gè)文件不存在則創(chuàng)建一個(gè)空的新文件,如果原來(lái)這個(gè)文件存在則會(huì)重新創(chuàng)建這個(gè)文件,原來(lái)的內(nèi)容會(huì)被消除掉; O_EXCL標(biāo)志和O_CREAT標(biāo)志結(jié)合使用時(shí),則沒(méi)有文件時(shí)創(chuàng)建文件,有這個(gè)文件時(shí)會(huì)報(bào)錯(cuò)提醒我們; open函數(shù)在使用O_CREAT標(biāo)志去創(chuàng)建文件時(shí),可以使用第三個(gè)參數(shù)mode來(lái)指定要?jiǎng)?chuàng)建的文件的權(quán)限。mode使用4個(gè)數(shù)字來(lái)指定權(quán)限的,其中后面三個(gè)很重要,對(duì)應(yīng)我們要?jiǎng)?chuàng)建的這個(gè)文件的權(quán)限標(biāo)志。譬如一般創(chuàng)建一個(gè)可讀可寫不可執(zhí)行的文件就用0666 1 fd = open('a.txt', O_RDWR | O_CREAT | O_EXCL, 0666); 2 if (-1 == fd) // 有時(shí)候也寫成: (fd < 0) 3 { 4 perror('文件打開錯(cuò)誤'); 5 _exit(-1); 6 } 7 else 8 { 9 printf('文件打開成功,fd = %d.\n', fd);10 } O_NONBLOCK (1)阻塞與非阻塞。如果一個(gè)函數(shù)是阻塞式的,則我們調(diào)用這個(gè)函數(shù)時(shí)當(dāng)前進(jìn)程有可能被卡住(阻塞住,實(shí)質(zhì)是這個(gè)函數(shù)內(nèi)部要完成的事情條件不具備,當(dāng)前沒(méi)法做,要等待條件成熟),函數(shù)被阻塞住了就不能立刻返回;如果一個(gè)函數(shù)是非阻塞式的那么我們調(diào)用這個(gè)函數(shù)后一定會(huì)立即返回,但是函數(shù)有沒(méi)有完成任務(wù)不一定。 (2)阻塞和非阻塞是兩種不同的設(shè)計(jì)思路,并沒(méi)有好壞??偟膩?lái)說(shuō),阻塞式的結(jié)果有保障但是時(shí)間沒(méi)保障;非阻塞式的時(shí)間有保障但是結(jié)果沒(méi)保障。 (3)操作系統(tǒng)提供的API和由API封裝而成的庫(kù)函數(shù),有很多本身就是被設(shè)計(jì)為阻塞式或者非阻塞式的,所以我們應(yīng)用程度調(diào)用這些函數(shù)的時(shí)候心里得非常清楚。 (4)我們打開一個(gè)文件默認(rèn)就是阻塞式的,如果你希望以非阻塞的方式打開文件,則flag中要加O_NONBLOCK標(biāo)志。 (5)只用于設(shè)備文件,而不用于普通文件。 errno和perror (1)errno就是error number,意思就是錯(cuò)誤號(hào)碼。linux系統(tǒng)中對(duì)各種常見錯(cuò)誤做了個(gè)編號(hào),當(dāng)函數(shù)執(zhí)行錯(cuò)誤時(shí),函數(shù)會(huì)返回一個(gè)特定的errno編號(hào)來(lái)告訴我們這個(gè)函數(shù)到底哪里錯(cuò)了。 (2)linux系統(tǒng)提供了一個(gè)函數(shù)perror(意思print error),perror函數(shù)內(nèi)部會(huì)讀取errno并且將這個(gè)不好認(rèn)的數(shù)字直接給轉(zhuǎn)成對(duì)應(yīng)的錯(cuò)誤信息字符串,然后print打印出來(lái)。 read和write的count (1)count和返回值的關(guān)系:count參數(shù)表示我們想要寫或者讀的字節(jié)數(shù),返回值表示實(shí)際完成的要寫或者讀的字節(jié)數(shù)。實(shí)現(xiàn)的有可能等于想要讀寫的,也有可能小于(說(shuō)明沒(méi)完成任務(wù)) (2)count再和阻塞非阻塞結(jié)合起來(lái),就會(huì)更加復(fù)雜。如果一個(gè)函數(shù)是阻塞式的,則我們要讀取30個(gè),結(jié)果暫時(shí)只有20個(gè)時(shí)就會(huì)被阻塞住,等待剩余的10個(gè)可以讀。 (3)有時(shí)候我們寫正式程序時(shí),我們要讀取或者寫入的是一個(gè)很龐大的文件(譬如文件有2MB),我們不可能把count設(shè)置為2*1024*1024,而應(yīng)該去把count設(shè)置為一個(gè)合適的數(shù)字(譬如2048、4096),然后通過(guò)多次讀取來(lái)實(shí)現(xiàn)全部讀完。 1 ret = write(fd, writebuf, strlen(writebuf)); 2 if (ret < 0) 3 { 4 //printf('write失敗.\n'); 5 perror('write失敗'); 6 _exit(-1); 7 } 8 else 9 {10 printf('write成功,寫入了%d個(gè)字符\n', ret);11 } 文件IO效率和標(biāo)準(zhǔn)IO (1)文件IO就指的是open、close、write、read等API函數(shù)構(gòu)成的一套用來(lái)讀寫文件的體系,這套體系可以很好的完成文件讀寫,但是效率并不是最高的。 (2)應(yīng)用層C語(yǔ)言庫(kù)函數(shù)提供了一些用來(lái)做文件讀寫的函數(shù)列表,叫標(biāo)準(zhǔn)IO。標(biāo)準(zhǔn)IO由一系列的C庫(kù)函數(shù)構(gòu)成(fopen、fclose、fwrite、fread),這些標(biāo)準(zhǔn)IO函數(shù)其實(shí)是由文件IO封裝而來(lái)的(fopen內(nèi)部其實(shí)調(diào)用的還是open,fwrite內(nèi)部還是通過(guò)write來(lái)完成文件寫入的)。標(biāo)準(zhǔn)IO加了封裝之后主要是為了在應(yīng)用層添加一個(gè)緩沖機(jī)制,這樣我們通過(guò)fwrite寫入的內(nèi)容不是直接進(jìn)入內(nèi)核中的buf,而是先進(jìn)入應(yīng)用層標(biāo)準(zhǔn)IO庫(kù)自己維護(hù)的buf中,然后標(biāo)準(zhǔn)IO庫(kù)自己根據(jù)操作系統(tǒng)單次write的最佳count來(lái)選擇好的時(shí)機(jī)來(lái)完成write到內(nèi)核中的buf(內(nèi)核中的buf再根據(jù)硬盤的特性來(lái)選擇好的實(shí)際去最終寫入硬盤中)。 |
|
|
來(lái)自: 孤獨(dú)一兵 > 《java學(xué)習(xí)》