檔案輸入與輸出.

Slides:



Advertisements
Similar presentations
Linux 环境及 Shell 程序 操作系统实验 1. 二、 Shell 编程与进程通信 常用 shell 命令 文件及文件属性操作 ls 、 cp 、 mv 、 rm ln 、 ln –s 、 chmod 、 groupadd 、 useradd 输入输出操作 echo 、 cat >> 、
Advertisements

第一單元 建立java 程式.
計算機程式語言實習課.
第1单元 操作系统概论 第一节 绪论 操作系统定义.
行程(process).
操作系统原理 Principles of Operating System
補充: Input from a text file
Linux File System Li-Shien Chen.
第 5 章 文件I/O操作.
File Access 井民全製作.
Project 2 JMVC code tracing
複習 struct score_Type{ int chinese,english; }; struct my_Type{
C File System.
Visual C++ introduction
chapter 1-Introduction
计算概论 第二十一讲 文件操作 北京大学信息学院.
JDK 安裝教學 (for Win7) Soochow University
第七章. 文件系统 (lab5).
第7章 Linux环境编程.
第10章 文件操作.
第十一章 文件 文件概述 文件操作 文件操作实例 本章小结 作业: 练习:
4B冊 認識公倍數和最小公倍數 公倍數和最小公倍數的關係.
第10章 文件操作.
C 程式設計— 檔案處理 台大資訊工程學系 資訊系統訓練班.
類別(class) 類別class與物件object.
SQL Stored Procedure SQL 預存程序.
2017 Operating Systems 作業系統實習 助教:陳主恩、林欣穎 實驗室:720A.
fp=fopen("CD2.dat","wb"); fwrite(&min,8,1,fp); fclose(fp);
程式設計 博碩文化出版發行.
|13 檔案資料讀寫.
檔案與磁碟的基本介紹.
第二章 SPSS的使用 2.1 啟動SPSS系統 2.2 結束SPSS系統 2.3 資料分析之相關檔案 2.4 如何使用SPSS軟體.
作業系統實習課(四) -檔案管理- 實驗室:720A 助教:鄧執中.
第七章 檔案處理.
THE C PROGRAMMING LANGUAGE
雲端計算.
私立南山高中 信息組 電腦研習 電腦資料的備份 中華民國 99年4月20日 星期二.
Linux 文件操作——系统调用和标准 IO 库
Chap3 Linked List 鏈結串列.
第一單元 建立java 程式.
实验一、进程控制 一、实验目的 1、加深对进程的理解,进一步认识并发执行的实质; 2、分析进程争用资源现象,学习解决进程互斥的方法;
挑戰C++程式語言 ──第8章 進一步談字元與字串
檔案輸入與輸出.
SOCKET( ).
C標準輸出入函數庫 與 作業系統.
引用檔案.
C qsort.
File Input and Output Chap. 11: 施威銘的書 Chap. 7: K&R.
第二章 类型、对象、运算符和表达式.
挑戰C++程式語言 ──第7章 輸入與輸出.
Pthread.
程式移植.
第13章 文 件.
2018 Operating Systems 作業系統實習 助教:林欣穎 實驗室:720A.
C 程式設計— 檔案處理 台大資訊工程學系 資訊系統訓練班.
实验二:添加Linux系统调用及熟悉常见系统调用
MultiThread Introduction
作業一: 安裝Linux於btrfs上 中正大學 作業系統實驗室 指導教授:羅習五
第四章 陣列、指標與參考 4-1 物件陣列 4-2 使用物件指標 4-3 this指標 4-4 new 與 delete
作業系統實習課(二) -Scheduler-Related System Calls-
ABAP Basic Concept (2) 運算子 控制式與迴圈 Subroutines Event Block
String類別 在C語言中提供兩種支援字串的方式 可以使用傳統以null結尾的字元陣列 使用string類別
Chapter 4 Multi-Threads (多執行緒).
Unix指令4-文字編輯與程式撰寫.
Develop and Build Drives by Visual C++ IDE
《操作系统设计与实现》 第5章 文件系统.
《操作系统设计与实现》 Linux系统编程.
方法(Method) 函數.
ABAP Basic Concept (2) 運算子 控制式與迴圈 Subroutines Event Block
Presentation transcript:

檔案輸入與輸出

檔案在Linux內是什麼樣子

檔案(file) 檔案是一堆數據的有序集合 對作業系統而言,可以由「目錄系統」找到一個檔案在硬碟上的 位置 對程式而言,必須先告訴作業系統,準備「使用」哪些檔案,作 業系統會「開啟」該檔案,並給該檔案一個代碼(file descriptor),隨後該程式使用該「代碼」操作該檔案

檔案(file) 對每一個行程,每一個開啟的檔案都會有一個檔案指標 檔案指標代表目前正在對「該位置」做操作 read、write、lseek會改變檔案指標的位置 檔案指標 檔案

檔案(file) 對每一個行程,每一個開啟的檔案都會有一個檔案指標 檔案指標代表目前正在對「該位置」做操作 read、write、lseek會改變檔案指標的位置 檔案內部可能有空洞 這些空洞在邏輯意義上都是0 某些檔案系統不支援有空洞的檔案 檔案指標 檔案 空洞 空洞

為什麼檔案系統需要支援「空洞」 例如一間公司,員工編號共五碼,第1XXXX代表製造部、2XXXX代 表研發部、3XXXX代表行銷部 如果檔案系統支援「洞」,那麼可以直接使用員工編號當index, 而不需要擔心浪費磁碟空間的問題,如: 資料 洞 資料 洞 資料 洞 研發部 行銷部 製造部

用一個例子開始:mycp

mycp.c

一堆的#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);

mycp.c

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

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可讀寫)

mycp.c

read() ssize_t read(int fd, void *buf, size_t count); 會從fd所代表的檔案讀取「最多」count個byte到指標buf所指向的記憶體 當回傳值大於1,代表讀取了多少個byte 回傳值等於0代表讀到了EOF(檔案結尾) 回傳值-1,代表讀取發生了錯誤

write() ssize_t write(int fd, const void *buf, size_t count); 將buf指向的資料共count個byte,寫入fd所代表的檔案 傳回值代表總共寫入了多少個byte 當傳回值為-1,代表發生了錯誤

mycp.c

perror void perror(const char *s); 依照1. 依照errno印出訊息 2. 字串S 假設errno是1,perror(“the error is”)會印出「the error is: Operation not permitted」

什麼是errno errno是系統內的錯誤訊息代碼 如果呼叫一個C函數時發生了錯誤,則errno會被設定為該錯誤所 代表的號碼 所有errno對應的錯誤訊息在sys_errlist

mycp.c

close() int close(int fd); 使用完一個檔案,使用close告訴作業系統使用完畢 作業系統會依照當時的狀況(最後一個存取該檔案的行程),決 定是否釋放相關資源 成功回傳0,失敗回傳-1

lseek & file holes

hole.c

lseek() off_t lseek(int fd, off_t offset, int whence); 將檔案fd的檔案指標移動到從whence起算,偏移offset的位置 傳統上UNIX支援的whence有三種選擇 SEEK_SET:絕對位置 SEEK_CUR:從現在位置起算 SEEK_END:從結束位置起算 傳回值為從檔案開始的偏移值

hole.c 因此hole.c會產生一個名為 myHole的檔案,在開始位置寫 入1,往後移動10000 byte在寫 入2,往後移動10000 byte在寫 入3 $ls myHole -lhs 12K -rw------- 1 shiwulo shiwulo 196K Jan 13 04:24 myHole /*檔案大小為196K,佔據磁碟空間12K*/

使用mycp複製myhole $ ./mycp myHole myHole2 $ ls myH* -lhs 12K -rw------- 1 shiwulo shiwulo 196K Jan 13 04:24 myHole 196K -rw------- 1 shiwulo shiwulo 196K Jan 13 04:31 myHole2 /*檔案大小都是196K,但是myHole2佔據磁碟空間196K而非12K*/ $cmp myHole myHole2 /*使用cmp比較二者無差異*/

myHole內部構造 10000個0 10000個0 1 0000…0000 2 3

進階版的mycp.c,mycp2.c(第一部分) 一開始要宣告_GNU_SOURCE才可以使用進階版的lseek()

man lseek

SEEK_HOLE & SEEK_DATA 在新版的UNIX提供這二個新的選項,但必須手動打開,即#define _GNU_SOURCE 洞的最前面 資料的最前面 1 0000…0000 2 3

進階版的mycp.c,mycp2.c(第二部分) 取得每個資料區段的位置及大小 移動到該區段的開頭位置 進行該區段的複製

結果 $./mycp myHole myHole2 $ ./mycp2 myHole myHole3 $ ls myH* -lhs 12K -rw------- 1 shiwulo shiwulo 196K Jan 13 04:24 myHole 196K -rw------- 1 shiwulo shiwulo 196K Jan 13 05:08 myHole2 12K -rw------- 1 shiwulo shiwulo 196K Jan 13 05:09 myHole3

mycp2有錯誤,試著修改這個錯誤

協調式鎖定檔案flock

lock.c

執行結果 $ ./lock myHole e fd = 3 is opened end 先執行 後執行 $ ./lock myHole e fd = 3 is opened end $ ./lock myHole e fd = 3 is opened /*被鎖住了,除非另外一個行程unlock或者結束*/

flock() int flock(int fd, int operation); LOCK_SH:分享鎖,除了互斥鎖,可以多個人同時編譯 LOCK_EX:互斥鎖,只可以這個行程進行編譯 LOCK_UN:解開這個鎖 請注意,如果另外一個行程並未使用flock,那麼另一個行程就不需要遵照這些「鎖」

強制鎖

強制鎖 在Solaris, HP-UX, and Linux上可以使用set-grup-id,讓一個檔案只 可以由一個行程開啟 具體的做法是在chmod前加上2,例如: chmod 2644 test

確保寫入 sync & fsync & fdatasync

三個類似的函數 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)寫回磁 碟

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;

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;

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;

sync.c的執行結果 $ time ./sync ********** real 0m21.215s user 0m0.136s sys 0m5.272s

比較 sync fdatasync no sync real 0m21.215s 0m17.545s 0m0.076s user sys 0m5.272s 0m3.980s 0m0.068s