檔案描述符 檔案控制代碼 重定向
一檔案相關的系統呼叫
C語言標準庫函式中有對檔案的操作
fopen(),fwrite(),fread(),fclose();等
FILE *fd=fopen("./test.txt","w"); if(fd==NULL) { perror("fopen"); return 1; } char * str=(char *)"hello world"; fwrite(str,strlen(str),1,fd); fclose(fd);
作業系統中對檔案的操作有系統呼叫
open(),write(),read(),cloes()等
事實上C語言中的檔案操作函式是通過系統呼叫來實現的。
當我們開啟一個檔案時,系統會為該檔案分配一個檔案描述符,這個檔案描述符是一個比較小得整數
當作業系統建立一個程序時,會先對這個程序先進行描述,在進行組織
對程序進行描述是用一個結構體,稱為程序控制塊(PCB),對程序進行組織是用一個連結串列將這些程序控制塊鏈在一起
一個程序中可以開啟多個檔案,同樣,作業系統對檔案也是先進行描述,再進行組織
檔案描述也是用一個結構體來,對其進行組織也是用一個連結串列將其鏈上去
下面是一個簡單的例子
int fd=open("test.txt",O_WRONLY | O_CREAT,0644); if(fd<0) { perror("open");exit(1); } char * str=(char *)"hello world"; printf("fd:%d\n",fd); write(fd,str,strlen(str)); close(fd);
這裡的檔案描述符打印出來是3
這是因為作業系統會為程序預設開啟三個檔案,分配三個檔案描述符0,1,2,分別是標準輸入,標準輸出,標準錯誤。
檔案描述符預設從空閒的檔案描述符中最小的開始分配
close(0);
close(2);
int fd_1=open("test1.txt",O_WRONLY|O_CREAT,0644);
int fd_2=open("test2.txt",O_WRONLY|O_CREAT,0644);
if(fd_1<0||fd_2<0)
{
perror("open");exit(1);
}
//這裡預設從檔案描述符中找最小的
printf("fd_1:%d\n",fd_1);
printf("fd_2:%d\n",fd_2);
作業系統中關於檔案的相關係統呼叫
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
ssize_t read(int fd, void *buf, size_t count);
ssize_t write(int fd, const void *buf, size_t count);
int open(const char *pathname, int flags, mode_t mode);
ssize_t read(int fd, void *buf, size_t count);
ssize_t write(int fd, const void *buf, size_t count);
這裡注意read()函式
返回值sszie_t 是一個有符號長整型,返回值大於0時,表示本次實際讀的字元個數,小於0時表示讀取失敗,等於0時表示讀到檔案的結束標誌EOF(在Linux下輸入時按下Ctrl+D可以獲取,windows下按下Ctrl+Z可以獲取)
說了這麼多次檔案描述符,那麼檔案描述符到底是什麼呢
files_struct結構體裡面的內容:
files_struct結構儲存了程序開啟的所有檔案表資料,描述一個正被開啟的檔案。Linux中一個程序最多隻能同時開啟NR_OPEN_DEFAULT個檔案,而且前三項分別設為標準輸入、標準輸出和出錯資訊輸出檔案,定義如下:
struct files_struct {
atomic_t count; //自動增量
struct fdtable *fdt;
struct fdtable fdtab;
fd_set close_on_exec_init; //執行exec時 需要關閉的檔案描述符初值集合
fd_set open_fds_init; //當前開啟檔案 的檔案描述符遮蔽字
struct file * fd_array[NR_OPEN_DEFAULT];
spinlock_t file_lock; /* Protects concurrent writers. Nests inside tsk->alloc_lock */
};
file結構體:
檔案結構體代表一個開啟的檔案,系統中的每個開啟的檔案在核心空間都有一個關聯的struct file。它由核心在開啟檔案時建立,並傳遞給在檔案上進行操作的任何函式。在檔案的所有例項都關閉後,核心釋放這個資料結構。在核心建立和驅動原始碼中,struct file的指標通常被命名為file或filp
二、重定向
看下面程式碼:
close(1);
int fd_1=open("test1.txt",O_WRONLY|O_CREAT,0644);
if(fd_1<0)
{
perror("open");
exit(1);
}
//這裡預設從檔案描述符中找最小的,所以這裡分配的檔案描述符為1
printf("fd_1:%d\n",fd_1);
//因為printf()函式底層實現還是呼叫了系統呼叫fprintf();
//fprintf(stdout,"%d\n",fd_1)
//所以這裡其實是將內容輸出重定向到檔案描述符為1的檔案中、
//那麼這裡就不會看到標準輸出上與內容輸出
//而是在我們開啟的檔案中
int fd_1=open("test1.txt",O_WRONLY|O_CREAT,0644);
if(fd_1<0)
{
perror("open");exit(1);
}
//這裡預設從檔案描述符中找最小的,所以這裡分配的檔案描述符為1
printf("fd_1:%d\n",fd_1);
//因為printf()函式底層實現還是呼叫了系統呼叫fprintf();
//fprintf(stdout,"%d\n",fd_1)
//所以這裡其實是將內容輸出重定向到檔案描述符為1的檔案中、
//那麼這裡就不會看到標準輸出上與內容輸出
//而是在我們開啟的檔案中
//修改檔案控制代碼,完成重定向
//dup2(int oldfd,int newfd);這個函式的作用是用newfd是覆蓋了oldfd
//對newfd檔案的操作其實就是oldfd的操作。
int fd=open("test.txt",O_CREAT | O_WRONLY,0644);
if(fd<0)
{
perror("open");exit(1);
}
dup2(fd,1);
printf("fd : %d\n",fd);//就是相當於對檔案描述符為1中的問價寫其實就是對檔案描述符為fd的檔案進行操作
const char * str="nihao shijie\n";
write(1,str,strlen(str));
close(fd);
//修改檔案控制代碼,完成重定向
//dup2(int oldfd,int newfd);這個函式的作用是用newfd是拷貝覆蓋了oldfd
//相當於對newfd檔案的操作其實就是oldfd的操作。
int fd=open("test.txt",O_CREAT | O_WRONLY,0644);
if(fd<0)
{
perror("open");exit(1);
}
dup2(fd,1);
printf("fd : %d\n",fd);//就是相當於對檔案描述符為1中的問價寫其實就是對檔案描述符為fd的檔案進行操作
const char * str="nihao shijie\n";
write(1,str,strlen(str));
close(fd);
//三種輸出“hello world”
//多種列印hello world
const char * str="hello world\n";
printf("%s",str);
write(1,str,strlen(str));
fprintf(stdout,"%s",str);
當我們在函式結束的時候建立一個子程序呢?
const char * str_printf="str_printf\n";
const char * str_fprintf="str_fprintf\n";
const char * str_write="str_write\n";
printf("%s",str_printf);
fprintf(stdout,"%s",str_fprintf);
write(1,str_write,strlen(str_write));
fork();
緩衝區重新整理格式:
1.無緩衝(系統呼叫,write)
2.行緩衝 (顯示器)按行重新整理換緩衝區
3.全緩衝(檔案)緩衝區滿了才會進行重新整理
當然每一個程序結束會重新整理緩衝區
當我們在標準輸出上進行輸出時是按行重新整理自己的緩衝區的,其實是沒有什麼不同的,因為這裡的字串後面都加了'\n',會自己重新整理緩衝區,建立一個子程序後,子程序的緩衝區中為空
因為輸出到一個檔案中時是一種全緩衝(等到緩衝區滿了才進行重新整理)
所以當將其輸出重定向到一個檔案中的話,是一個全緩衝,父程序將字串輸出後,並沒有進行重新整理,字串仍然在父程序的緩衝區中,建立一個子程序的話,子程序會將父程序發資料進行拷貝,那麼子程序的緩衝區中就會有和父程序同樣的字串,結束時重新整理緩衝區,字串就會被重新整理到目標檔案,所以會出現兩遍printf()和fprintf()的內容列印了兩遍。write()是屬於系統呼叫,沒有緩衝區,直接輸出,所以是一遍。
上面兩個printf()函式和fprintf()函式底層都是呼叫系統呼叫write(),但是系統呼叫在使用者態是沒有緩衝區的,所以上面說的緩衝區緩衝區是C庫提供的