一.系統所提供的IO接口
1.open()
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
pathname: 要打開或創建的目標文件
flags: 打開文件時,可以傳入多個參數選項,用下面的一個或者多個常量進行“或”運算,構成flags。
參數:
O_RDONLY: 只讀打開
O_WRONLY: 只寫打開
O_RDWR : 讀,寫打開
這三個常量,必須指定一個且只能指定一個
O_CREAT : 若文件不存在,則創建它。需要使用mode選項,來指明新文件的訪問權限
O_APPEND: 追加寫
mode參數表示設置文件訪問權限的初始值,和用戶掩碼umask有關,
比如0644表示-rw-r–r–,也可以用S_IRUSR、S_IWUSR等宏定義按位或起來表示,詳見open(2)的Man Page。
要注意的是,有以下幾點:
1. 文件權限由open的mode參數和當前進程的umask掩碼共同決定。
2. 第三個參數是在第二個參數中有O_CREAT時才作用,如果沒有,則第三個參數可以忽略
返回值:
成功:新打開的文件描述符
失敗:返回-1
2.read函數
//功能: 從fd文件中讀取數據到buf所指向的空間,該空間大小爲len
//返回值爲:實際讀取的字節數
int read(int fd, char *buf, size_t len);
3.write函數
//功能:往fd所指的文件中寫入數據,數據的起始位置爲buf,大小爲len
int write(int fd, const char *buf, size_t len);
4.close函數
int close(int fd);
fd表示文件標識符
返回值:返回0表示成功,返回-1表示失敗
二.文件描述符fd
- 通過對open函數的學習,我們知道了文件描述符就是一個整數
- Linux進程默認情況下會有3個缺省打開的文件描述符,分別是標準輸入0, 標準輸出1, 標準錯誤2.
- 0,1,2對應的物理設備一般是:鍵盤,顯示器,顯示器
文件描述符與文件流指針的區別
fd只是一個整數,在open時產生,起到一個索引的作用,進程通過PCB中的文件描述符表找到該fd所指向的文件指針file。
- open:文件描述符的操作(如:open)返回的是一個文件描述符(int fd),內核會在每個進程空間中維護一個文件描述符表,所有打開的文件都將通過,此表中的文件描述符來引用。
- fopen:流(如:fopen)返回的是一個文件指針(即指向FILE結構體的指針),FILE結構是包含有文件描述符的,fopen可以看做是open(fd直接操作的系統調用)的封裝,它的優點是帶有I/O緩存。
文件描述符與文件流指針的關係
庫函數與系統調用接口關係:上下級調用關係
文件流指針這個結構體中包含了文件描述符,當使用標準庫進行io操作時, 其最終通過文件流指針找到文件描述符進而對文件進行操作
三.重定向
1.文件描述符的分配規則
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
close(0);
//close(2);
int fd = open("myfile", O_RDONLY);
if(fd < 0){
perror("open");
return 1;
}
printf("fd: %d\n", fd);
close(fd);
return 0;
}
- 發現是結果是: fd: 0 或者 fd 2 可見,文件描述符的分配規則:在files_struct數組當中,找到當前沒有被使用的最小的一個下標,作爲新的文件描述符。
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
int main()
{
close(1);
int fd = open("myfile", O_WRONLY|O_CREAT, 00644);
if(fd < 0){
perror("open");
return 1;
}
printf("fd: %d\n", fd);
fflush(stdout);
close(fd);
exit(0);
}
此時,本來應該輸出到顯示器上的內容,輸出到了文件 myfile 當中,其中,fd=1。這種現象叫做輸出重定向。
2.使用 dup2 系統調用重定向
函數原型如下:
#include <unistd.h>
int dup2(int oldfd, int newfd); //將newfd的內容拷貝到oldfd
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
int main() {
int fd = open("./log", O_CREAT | O_RDWR);
if (fd < 0) {
perror("open");
return 1;
}
close(1);
dup2(fd, 1);
for (;;) {
char buf[1024] = {0};
ssize_t read_size = read(0, buf, sizeof(buf) - 1);
if (read_size < 0) {
perror("read");
break;
}
printf("%s", buf);
fflush(stdout);
}
return 0;
}
3.緩衝區問題
#include <stdio.h>
#include <string.h>
int main()
{
const char *msg0="hello printf\n";
const char *msg1="hello fwrite\n";
const char *msg2="hello write\n";
printf("%s", msg0);
fwrite(msg1, strlen(msg0), 1, stdout);
write(1, msg2, strlen(msg2));
fork();
return 0;
}
運行結果:
hello printf
hello fwrite
hello write
對進程實現輸出重定向 ./hello > file , 我們發現結果變成了:
hello write
hello printf
hello fwrite
hello printf
hello fwrite
- 一般C庫函數寫入文件時是全緩衝的,而寫入顯示器是行緩衝。
- printf fwrite 庫函數會自帶緩衝區,當發生重定向到普通文件時,數據的緩衝方式由行緩衝變成了全緩衝。而我們放在緩衝區中的數據,就不會被立即刷新,甚至fork之後。但是進程退出之後,會統一刷新,寫入文件當中。
- 但是fork的時候,父子數據會發生寫時拷貝,所以當你父進程準備刷新的時候,子進程也就有了同樣的一份數據,隨即產生兩份數據。
- write 沒有變化,說明沒有所謂的緩衝。
四.動態庫和靜態庫
- 靜態庫(.a):程序在編譯鏈接的時候把庫的代碼鏈接到可執行文件中。程序運行的時候將不再需要靜態庫。
- 動態庫(.so):程序在運行的時候纔去鏈接動態庫的代碼,多個程序共享使用庫的代碼。
- 一個與動態庫鏈接的可執行文件僅僅包含它用到的函數入口地址的一個表,而不是外部函數所在目標文件的整個機器碼在可執行文件開始運行以前,外部函數的機器碼由操作系統從磁盤上的該動態庫中複製到內存中,這個過程稱爲動態鏈接(dynamic linking)
- 動態庫可以在多個程序間共享,所以動態鏈接使得可執行文件更小,節省了磁盤空間。操作系統採用虛擬內存機制允許物理內存中的一份動態庫被要用到該庫的所有進程共用,節省了內存和磁盤空間。
1.生成靜態庫
1.將要生成靜態庫的文件編譯爲.o文件
gcc -c add.c -o add.o
gcc -c sub.c -o sub.o
gcc -c mul.c -o mul.o
gcc -c div.c -o div.o
2.生成靜態庫
ar -rc libmymath.a add.o sub.o mul.o div.o
ar -rc lib+庫名 .o文件
-c 創建
-r 替換
3.查看靜態庫中的目錄列表
ar -tv libmymath.a
t: 列出靜態庫中的文件
v: verbose 詳細信息
4.使用
gcc main.c -L. -lmymath
-L 指定庫的路徑
-l 指定庫名
目標文件生成後,靜態庫刪除,程序依舊可以運行
2.生成動態庫
- -fPIC的全稱是Posttion Independent code,用於生成位置無關代碼。代碼無絕對跳轉,跳轉都爲相對跳轉
- 即使不使用 -fPIC也可以生成.so文件,但是對於源文件有要求,因爲不加fPIC編譯的so必須要在加載到用戶程序的地址空間時重定向所有表,這將導致不能引用其他地方的代碼
- 添加fPIC實現真正意義上的多個進程共享so文件, 多個進程引用同一個PIC動態庫時,可以共用內存。被調用的庫在不同進程中的虛擬地址不同,但是操作系統會將其映射到同一塊物理內存上
1.將要生成靜態庫的文件編譯爲.o文件
gcc -fPIC -c add.c -o add.o
gcc -fPIC -c sub.c -o sub.o
gcc -fPIC -c mul.c -o mul.o
gcc -fPIC -c div.c -o div.o
2.生成動態庫
gcc --share add.o sub.o mul.o div.o -o libmymath.so
–share 生成一個共享庫而不是可執行程序
-
因爲gcc默認是動態鏈接 因此優先使用動態庫生成可執行程序
-
庫文件的默認查找路徑/lib64 /usr/lib64
-
設置環境變量:LD_LIBRARY_PATH=. (庫的運行路徑)