Lin9ux下的基礎I/O
C語言階段
- 在學習C語言的時候,我們要實現程序的I/O操作,需要調用fopen()和fclose來打開和關閉文件,fopen()成功返回FILE*的文件指針,
- 利用fread和fwrite函數來進行文件的讀寫操作。當然我們還學過fseek重置文件指針,ftell,rewind等函數
- 我們還知道,C會默認打開流,stdin,stdout,stderr,並且這三個流都是FILE*類型的,即文件指針。
系統編程階段
當我們進入系統編程之後,我們可以同樣的調用系統接口,來實現I/O操作。
系統接口open
int open(const char*pathname, int flags);
int open(const char* pathname,int flags,mode_t mode)
pathnamm:要打開文件名稱
flag:O_RDONLY(只讀),O_WRONLY(只寫),O_RDWR(可讀可寫),O_CREAT(文件不存在則創建之,並且要加上mode權限)
3.write
ssize_t write(int fd, const void *buf, size_t count);
fd:對應操縱文件的文件描述符,
buf:源數據存放內存
count:一次寫入多少個字節
返回值:實際寫入字節數read
ssize_t read(int fd, void *buf, size_t count);
fd:對應文件的文件描述符;
buf:寫到的目的內存;
count:一次寫入的字節數;
返回值:實際讀取的字節數- close
int close(int fd);
關閉對應文件描述符爲fd的文件。 - 我們知道,c提供的f#類函數都是庫函數,調用成功返回文件指針,而open,close,write,read屬於系統調用接口,在講操作系統的作用的時候,講過下面這張圖。
- 不難看出,c庫函數都是封裝了系統調用的接口,方便二次開發。我們又知道linux下一個進程的PCB中有files*的一個字段,描述一個進程打開文件的情況,而這個指針指向一個files_struct的結構體表,這個表中最重要的就是保護一個指針數組,每個數組元素都指向打開的文件,我們通常就利用數組的下標來操縱對應位置的元素,我們把這裏的下標稱之爲文件描述符,我們都知道數組下標從0開始的,所以每一個進程都默認打開0,1,2三個文件描述符表示的文件,分別代表標準輸入,標準輸出,標準出錯。如下圖
這裏我們就來練習一下這幾個函數:
1 #include<stdio.h>
2 #include<unistd.h>
3 #include<sys/types.h>
4 #include<string.h>
5 #include<fcntl.h>
6 #include<sys/stat.h>
7 int main()
8 {
9
10 int fd=open("myfile",O_RDWR|O_CREAT,0644);
11 if(fd<0)
12 {
13 perror("open");
14 return -1;
15 }
16 int count=5;
17 char *buf="hello,world\n";
18 while(count--)
19 {
20 write(fd,buf,strlen(buf));
21 }
22 char buf2[1024];
23 while(1)
24 {
25
26 ssize_t s=read(fd,buf2,strlen(buf)-1);
27 if(s>0)
28 {
29 printf("myfile if :%s\n",buf2);
30 }
31 else
32 {
33 break;
34 }
35 }
36 close (fd);
37 return 0;
- 既然現在我們瞭解了系統編程的I/O函數,已經文件描述符的概念,我們就知道,一個進程默認會打開0,1,2三個分別代表標準輸入,標準輸出,標準出錯的文件描述符,而系統默認的輸出就是屏幕,默認的輸入就是鍵盤,如果講原本輸出到屏幕的數據輸出到指定地方,這就叫做輸出重定向.就拿輸出重定向來舉例,printf()默認是往屏幕上面打印數據,再調用時,底層就會找到1號文件描述符,我們只需要關閉1號文件描述符,然後用我們創建的myfile代替他,以後調用printf()函數就會找到1號文件描述符對應的內容,即往myfile中輸出數據,
我們來實現一個小程序,使原來往屏幕上打印的數據,輸出到文件中。
1 #include<stdio.h>
2 #include<unistd.h>
3 #include<sys/types.h>
4 #include<string.h>
5 #include<fcntl.h>
6 #include<sys/stat.h>
7 int main()
8 {
9 close(1);
10 int fd=open("myfile",O_RDWR|O_CREAT,0644);
11 if(fd<0)
12 {
13 perror("open");
14 return -1;
15 }
16
17 printf("how are you?\n");
18 close(fd);
}
原本輸出到屏幕上的數據就被輸出到文件裏面了
FILE結構和文件描述符
- 因爲IO函數和系統調用對應,所以本質上還是操控的是文件描述符。所以再FILE結構體裏面肯定封裝了文件描述符。
- 一般的C庫函數寫入文件都是全緩衝的,寫入顯示器是行緩衝的。
- 而我們的系統調用接口是沒有自帶緩衝區的。
靜態庫和動態庫
要了解靜態庫和動態庫首先我們來了解一下文件系統,來看一下文件系統,每一個當我們使用ls -l 選項列出文件信息的時候,能看到文件的模式,硬鏈接數,文件所屬者和所屬組,當然還有文件大小和文件最後被修改時間,以及文件名。
但是每一個文件再計算機中如何存儲的,我們來看下面這張圖
超級快:保存着文件系統本身的結構信息。
i節點表:存放着文件的屬性,如大小,所有者,最後修改時間
數據區:存放着文件的內容在查找一個文件的時候,並不是利用文件名來找,而是用文件名對應的inode(其實在linux中一個inode可以對應多個inode,)
- 我們可以使用ln命令來使兩個文件對應同一個inode。
- 我們可以看出, 1 和 2 文件的 元信息中有 一列變成了2.
- 我們把通過inode引用另外一個文件叫做硬鏈接,把通過文件名引用另一個文件叫做軟鏈接
靜態庫
程序在編譯鏈接的過程中,把庫的代碼全部鏈接到可執行文件中,運行期間不需要靜態庫,這樣造成可執行文件太大,並且效率低。
靜態庫命名規則 libXXX.a(lib是前綴,.a是後綴,XXX是庫名)
生成方法
- 先使用gcc -c選項使目標文件在彙編完成後結束生成.o文件
- 使用ar -rc 庫名 目標文件 來生成動態庫
.
鏈接方法
- gcc man.c -L . -l mymath
-L選項後面加上庫的路徑,-l選項後加上庫名,注意刪除掉前綴後綴
動態庫
程序在運行期間纔回去鏈接到動態庫的代碼,多程序 共享使用庫的代碼。一個動態庫鏈接的可執行程序僅僅包含它用到的函數的入口地址,而不是外部函數所在目標文件的整個機器碼。節省了資源規則:libXXX.so
動態庫命名
生成方法
gcc -shared表示生成共享庫格式
gcc -fPIC表示生成位置無關的碼
鏈接方法
gcc man.c -L . -l mymath