Download presentation
Presentation is loading. Please wait.
1
檔案輸入與輸出
2
檔案在Linux內是什麼樣子
3
檔案(file) 檔案是一堆數據的有序集合 對作業系統而言,可以由「目錄系統」找到一個檔案在硬碟上的 位置
對程式而言,必須先告訴作業系統,準備「使用」哪些檔案,作 業系統會「開啟」該檔案,並給該檔案一個代碼(file descriptor),隨後該程式使用該「代碼」操作該檔案
4
檔案(file) 對每一個行程,每一個開啟的檔案都會有一個檔案指標 檔案指標代表目前正在對「該位置」做操作
read、write、lseek會改變檔案指標的位置 檔案指標 檔案
5
檔案(file) 對每一個行程,每一個開啟的檔案都會有一個檔案指標 檔案指標代表目前正在對「該位置」做操作
read、write、lseek會改變檔案指標的位置 檔案內部可能有空洞 這些空洞在邏輯意義上都是0 某些檔案系統不支援有空洞的檔案 檔案指標 檔案 空洞 空洞
6
為什麼檔案系統需要支援「空洞」 例如一間公司,員工編號共五碼,第1XXXX代表製造部、2XXXX代 表研發部、3XXXX代表行銷部
如果檔案系統支援「洞」,那麼可以直接使用員工編號當index, 而不需要擔心浪費磁碟空間的問題,如: 資料 洞 資料 洞 資料 洞 研發部 行銷部 製造部
7
用一個例子開始:mycp
8
mycp.c
9
一堆的#include <xxx.h>
問題 舉例 記得函數的名稱就好 如果忘記或者不知道include某 個.h檔案,編譯器會告訴你某 函數未定義 針對該函數使用man查詢他需 要include哪些 $man perror NAME perror - print a system error message SYNOPSIS #include <stdio.h> void perror(const char *s);
10
mycp.c
11
open的傳回值 int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode); open的傳回值是file descriptor(檔案描述子),在系統中從0開始編號 如果前妙的號碼有缺號,open會優先使用最小的號碼當file descriptor 如:系統已經使用了0, 2, 3, 4,當使用open再開啟一個檔案時,file descriptor會 是「1」 一個行程能夠開啟的檔案是有限的 可以使用getrlimit()的RLIMIT_FSIZE查看 當回傳值為-1代表發生了錯誤,例如:超出RLIMIT_FSIZE
12
open() open (argv [1], O_RDONLY); 第一個參數是“路徑名”
為了讀 為了寫 open (argv [1], O_RDONLY); 第一個參數是“路徑名” 第二個參數告訴OS開啟這個檔 案的目的是「只讀取」 open(argv[2], O_WRONLY | O_CREAT, S_IRUSR| S_IWUSR); 第一個參數是“路徑名” 第二個參數告訴OS這個檔案只 用來寫入(O_WRONLY),如 果檔案不存在,就建立檔案 (O_CREAT) 第三個參數代表新建立的檔案 的讀寫屬性(owner可讀寫)
13
mycp.c
14
read() ssize_t read(int fd, void *buf, size_t count);
會從fd所代表的檔案讀取「最多」count個byte到指標buf所指向的記憶體 當回傳值大於1,代表讀取了多少個byte 回傳值等於0代表讀到了EOF(檔案結尾) 回傳值-1,代表讀取發生了錯誤
15
write() ssize_t write(int fd, const void *buf, size_t count);
將buf指向的資料共count個byte,寫入fd所代表的檔案 傳回值代表總共寫入了多少個byte 當傳回值為-1,代表發生了錯誤
16
mycp.c
17
perror void perror(const char *s); 依照1. 依照errno印出訊息 2. 字串S
假設errno是1,perror(“the error is”)會印出「the error is: Operation not permitted」
18
什麼是errno errno是系統內的錯誤訊息代碼 如果呼叫一個C函數時發生了錯誤,則errno會被設定為該錯誤所 代表的號碼
所有errno對應的錯誤訊息在sys_errlist
19
mycp.c
20
close() int close(int fd); 使用完一個檔案,使用close告訴作業系統使用完畢
作業系統會依照當時的狀況(最後一個存取該檔案的行程),決 定是否釋放相關資源 成功回傳0,失敗回傳-1
21
lseek & file holes
22
hole.c
23
lseek() off_t lseek(int fd, off_t offset, int whence);
將檔案fd的檔案指標移動到從whence起算,偏移offset的位置 傳統上UNIX支援的whence有三種選擇 SEEK_SET:絕對位置 SEEK_CUR:從現在位置起算 SEEK_END:從結束位置起算 傳回值為從檔案開始的偏移值
24
hole.c 因此hole.c會產生一個名為 myHole的檔案,在開始位置寫 入1,往後移動10000 byte在寫 入2,往後移動10000 byte在寫 入3 $ls myHole -lhs 12K -rw shiwulo shiwulo 196K Jan 13 04:24 myHole /*檔案大小為196K,佔據磁碟空間12K*/
25
使用mycp複製myhole $ ./mycp myHole myHole2 $ ls myH* -lhs 12K -rw shiwulo shiwulo 196K Jan 13 04:24 myHole 196K -rw shiwulo shiwulo 196K Jan 13 04:31 myHole2 /*檔案大小都是196K,但是myHole2佔據磁碟空間196K而非12K*/ $cmp myHole myHole2 /*使用cmp比較二者無差異*/
26
myHole內部構造 10000個0 10000個0 1 0000…0000 2 3
27
進階版的mycp.c,mycp2.c(第一部分)
一開始要宣告_GNU_SOURCE才可以使用進階版的lseek()
28
man lseek
29
SEEK_HOLE & SEEK_DATA 在新版的UNIX提供這二個新的選項,但必須手動打開,即#define _GNU_SOURCE
洞的最前面 資料的最前面 1 0000…0000 2 3
30
進階版的mycp.c,mycp2.c(第二部分)
取得每個資料區段的位置及大小 移動到該區段的開頭位置 進行該區段的複製
31
結果 $./mycp myHole myHole2 $ ./mycp2 myHole myHole3 $ ls myH* -lhs 12K -rw shiwulo shiwulo 196K Jan 13 04:24 myHole 196K -rw shiwulo shiwulo 196K Jan 13 05:08 myHole2 12K -rw shiwulo shiwulo 196K Jan 13 05:09 myHole3
32
mycp2有錯誤,試著修改這個錯誤
33
協調式鎖定檔案flock
34
lock.c
35
執行結果 $ ./lock myHole e fd = 3 is opened end
先執行 後執行 $ ./lock myHole e fd = 3 is opened end $ ./lock myHole e fd = 3 is opened /*被鎖住了,除非另外一個行程unlock或者結束*/
36
flock() int flock(int fd, int operation);
LOCK_SH:分享鎖,除了互斥鎖,可以多個人同時編譯 LOCK_EX:互斥鎖,只可以這個行程進行編譯 LOCK_UN:解開這個鎖 請注意,如果另外一個行程並未使用flock,那麼另一個行程就不需要遵照這些「鎖」
37
強制鎖
38
強制鎖 在Solaris, HP-UX, and Linux上可以使用set-grup-id,讓一個檔案只 可以由一個行程開啟
具體的做法是在chmod前加上2,例如: chmod 2644 test
39
確保寫入 sync & fsync & fdatasync
40
三個類似的函數 void sync(void); int fsync(int fd); int fdatasync(int fd);
將所有的資料(包含meta-data)寫回磁碟 int fsync(int fd); 將fd代表的檔案的所有的資料(包含meta-data)寫回磁碟 int fdatasync(int fd); 將fd代表的檔案的所有的資料(「不」包含meta-data)寫回磁 碟
41
sync.c int main() { int fd; int num;
fd = open("./hello1",O_WRONLY | O_CREAT, 0644); for(num=0; num <=100000; num++) { write(fd, "1234", sizeof("1234")); fsync(fd); if (num%10000==1) { write(1, "*", sizeof("*")); fsync(1); } return 0;
42
datasync.c int main() { int fd; int num;
fd = open("./hello3",O_WRONLY | O_CREAT, 0644); for(num=0; num <=100000; num++) { write(fd, "1234", sizeof("1234")); fdatasync(fd); if (num%10000==1) { write(1, "*", sizeof("*")); fsync(1); } return 0;
43
nsync.c int main() { int fd; int num;
fd = open("./hello2",O_WRONLY | O_CREAT, 0644); for(num=0; num <=100000; num++) { write(fd, "1234", sizeof("1234")); if (num%10000==1) { write(1, "*", sizeof("*")); fsync(1); } return 0;
44
sync.c的執行結果 $ time ./sync ********** real 0m21.215s user 0m0.136s sys 0m5.272s
45
比較 sync fdatasync no sync real 0m21.215s 0m17.545s 0m0.076s user
sys 0m5.272s 0m3.980s 0m0.068s
Similar presentations