📝前言:
這篇文章我們來講講Linux——基礎IO主要包括:
- 文件基本概念
- 回顧 C文件的操作
- 介紹系統關于文件的基本操作
🎬個人簡介:努力學習ing
📋個人專欄:Linux
🎀CSDN主頁 愚潤求學
🌄其他專欄:C++學習筆記,C語言入門基礎,python入門基礎,C++刷題專欄
目錄
- 一,基本概念
- 1. 概念
- 2. 系統角度
- 二,C 文件的 IO
- 1. 打開操作
- 2. 寫操作
- 3. 讀操作
- 4. 默認打開的流
- 三,系統文件的 IO
- 1. 位標記傳遞
- 2 系統調用 open
- 3 讀寫的系統調用
- 使用示例
- 庫函數和系統調用
- 4 文件描述符(重點)
- 文件描述符的分配原則
一,基本概念
1. 概念
- 狹義:
- 文件在磁盤上
- 磁盤是永久性存儲物質
- 磁盤是外設,即是輸入設備,也是輸出設備
- 對文件的操作,其實是對外設的輸入和輸出,稱為 IO
- 廣義:
- Linux 下?切皆文件
- 文件 == 內容 + 屬性
2. 系統角度
- 對?件的操作本質是進程對?件的操作
- 硬件(如磁盤)的管理者是操作系統
- ?件的讀寫本質不是通過 C 語? / C++ 的庫函數來操作的(這些庫函數只是為用戶提供?便),底層是通過文件相關的系統調用接口來實現的
二,C 文件的 IO
1. 打開操作
fopen:
FILE *fopen(const char *pathname, const char *mode);
pathname
:文件路徑mode
:打開方式
- 返回:
- 成功:返回文件指針
- 失敗:返回
NULL
這個FILE
的類型是一個文件結構體,通過typedef
來的。這是語言層的。
2. 寫操作
寫操作,在文件不存在的時候都會創建文件。
打開文件時的寫入模式:
- 清空寫
w
:清空文件內容,重新寫 - 追加寫
a
:在原來文件內容后面追加寫
fwrite:
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
- 把
ptr
的數據寫到stream
size
:每個數據項的大小,比如寫int
,siezeof(int)
(就是4
)nmemb
:數據項的個數
- 把
- 返回:實際成功寫入的數據項數量
注意:文件寫入的時候不能寫入'\0'
,'\0'
是C語言的東西,寫入了以后是亂碼
示例:
5 // C 語言文件接口回顧6 int main()7 {8 FILE* fp1 = fopen("log1.txt", "w");9 FILE* fp2 = fopen("log2.txt", "a");10 if(fp1 == NULL) printf("log1.txt 打開失敗\n");11 if(fp2 == NULL) printf("log2.txt 打開失敗\n");12 char* str1 = (char *)"測試寫入\n";13 fwrite(str1, strlen(str1), 1, fp1);14 fwrite(str1, strlen(str1), 1, fp2);15 fclose(fp1);16 fclose(fp2);17 return 0;18 }
當使用相對路徑的時候,進程通過自己記錄的自己工作路徑CWD
信息,知道新建的文件要放在哪里
運行結果:
3. 讀操作
讀操作,如果文件不存在會報錯
fread:
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
- 不解釋了,和
fwrite
的都一樣,不一樣的就是stream
是讀入的流,ptr
是存儲讀入數據的地方。 - 在上一次讀取結束后文件指針的位置繼續讀取。
- 不解釋了,和
- 返回:實際成功讀取的數據項數量
示例:
20 int main()21 {22 FILE* fp = fopen("log2.txt", "r");23 if(fp == NULL) printf("打開文件失敗");24 25 26 char* str1 = (char *)"測試寫入\n";27 char buff[1024];28 while(1)29 {30 // 這里每次讀都是覆蓋寫buff的,但是我們及時打印出來 31 int s = fread(buff, 1, sizeof(str1), fp); // 這里每次讀一個char,方便后續設置 '\0'32 if(s > 0)33 {34 buff[s] = 0;35 printf("%s", buff);36 }37 if(feof(fp))38 break;39 }40 return 0;41 }
運行結果:
4. 默認打開的流
#include <stdio.h>
extern FILE *stdin;
extern FILE *stdout;
extern FILE *stderr;
C默認會打開三個輸?輸出流,分別是標準輸入:stdin
, 標準輸出:stdout
, 標準錯誤:stderr
三,系統文件的 IO
頭文件:<unistd.h>
1. 位標記傳遞
利用位圖進行標記位傳遞,假設,現有標記位:ONE : 0001
,TWO:0010
、THREE:0100
- 如果我們單獨傳入一個
ONE
就相當于傳遞了最低位的1
。 - 如果我們希望傳遞第最低位的
1
和次低位的1
,就可以傳入ONE | TWO
(通過獲的方式)
2 系統調用 open
系統,通過系統調用open
打開文件:
pathname
:文件路徑falgs
:選項(約等于C語言里面的打開模式),通過 或|
來實現傳遞多個選項【這個選項本質是宏實現的標記位】- 常見選項:
O_RDONLY
:只讀打開O_WRONLY
: 只寫打開O_RDWR
: 讀,寫打開
- 上面這三個常量,必須指定?個且只能指定?個。
- 其他選項
O_CREAT
:若文件不存在,則創建它。需要使用mode
選項(傳入3個8進制數字),來指明新文件的訪問權限。如果新建的時候,不傳入,那對應的文件就是亂碼【別忘了實際的權限要考慮umask
的影響】O_TRUNC
:清空寫O_APPEND
:追加寫
- 返回值
- 成功:新打開的文件的文件描述符(系統通過這個來找到需要訪問的文件)
- 失敗:-1
3 讀寫的系統調用
write:
- 參數
fd
:文件描述符buf
:指向:要寫入的數據,的緩沖區指針count
:要寫入的字節數
- 返回
- 成功:實際寫入的字節數
- 失敗:-1,并設置errno
- 寫入類型
- 對于系統而言,沒有什么文本寫入和二進制寫入(這是語言層的概念)
- 你傳什么指針,他就寫什么,如果你原來數據是一個
int a
,指針傳&a
,則它就是按二進制寫
read和write
一樣,只是這里是讀到buf
其余close
、 lseek
接口類似C語言
使用示例
以寫入為例:
45 int main()46 {47 int fd = open("log.txt", O_WRONLY | O_CREAT | O_APPEND, 0666); // 這個打開的效果就相當于 C 語言庫函數的直接 mode 為 a48 const char* str = (char*)"hello world!\n";49 write(fd, str, strlen(str)); // 不寫入 '\0'50 char fd_str[20];51 sprintf(fd_str, "%d\n", fd); // 轉換為文本52 write(fd, fd_str, strlen(fd_str));53 close(fd);54 return 0;55 }
通常如果是read
,則open
就是用帶兩個參數的
運行結果:
庫函數和系統調用
- 語言層的文件結構體
FILE
里面會存儲文件描述符fd
,用于給下層的系統調用傳遞 - C語言的庫函數
fwrite
和fread
其實都是對底層系統調用write
和read
的封裝,在上層為用戶提供便利。 - C語言的庫函數具有可移植性,但是系統調用沒有
- 系統調用,如:Linux 的,用的是Linux這一套的。如果把代碼移植到Windows或者其他操作系統就不行了
- C語言,C的
fwrite
和fread
是對系統調用的封裝,但是它不只封裝一份操作系統的,它會實現多個操作系統的。后續根據自己在哪個操作系統上使用,通過條件編譯(裁剪),來調用對應的系統接口
4 文件描述符(重點)
什么是文件描述符?
如上圖所展示的 3
就新打開的log.txt
的文件描述符
0, 1, 2
分別對應 stdin
, stdout
, stderr
那系統是如何通過文件描述符來找到要訪問的文件的呢?
- 在進程的
task_struct
里面有一個files
指針,指向文件描述表files_struct
- 在文件描述表中,有
fd_array[]
指針數組,數組的下標對應著文件描述符,指向對應的文件 - 而每個文件被描述成一個結構體
struct file
(類似C語言的FILE
),里面存儲著文件的信息
有了文件描述符fd
,進程就會在自己的文件描述表里面找對應下標的文件指針,然后就可以訪問對應的文件了。
文件描述符的分配原則
在files_struct
數組當中,找到當前沒有被使用的最小的?個下標,作為新的?件描述符。
比如,一開始有0, 1, 2
:
- 直接創建一個新文件,則新文件
fd == 3
- 先
close(1)
,再創建新文件,則fd == 1
注意:這里的關閉不是真關閉,只是“引用計數 - 1”。
- 因為一個文件可以同時被多個進程訪問
- 同一個文件也可以被同一個進程的多個下標指針同時指向。比如
stdout
和stderr
其實都是向顯示器輸出
🌈我的分享也就到此結束啦🌈
要是我的分享也能對你的學習起到幫助,那簡直是太酷啦!
若有不足,還請大家多多指正,我們一起學習交流!
📢公主,王子:點贊👍→收藏?→關注🔍
感謝大家的觀看和支持!祝大家都能得償所愿,天天開心!!!