【Linux】內存級文件

目錄

C語言關于文件操作的函數

Linux關于文件操作的系統調用

完善myshell

C語言緩沖區


其實我們在C語言就學過文件操作,但是從語言的角度,我們只是說會用了關于文件的一些操作和函數,但其實它究竟是怎么回事我們其實并不明白,那么當我們學習到Linux操作系統的時候,我們才能更加深入的去了解這文件究竟是怎么回事

那么我們需要首先明確一些概念:文件=內容+屬性,不能說我這個文件是空,那么就不占空間;訪問文件都得先打開,然后通過執行代碼的方式去修改文件內容,也就是文件要被加載到內存中;是進程打開的文件并且一個進程可以打開多個文件;一個時間段內,可能有多個進程,操作系統要管理這些進程,同時也可能有多個被打開的文件,操作系統也要管理這些文件,那么如何管理呢?先描述,再組織,就是說,操作系統要給每個文件創建一個結構體對象,對文件的管理就變成了對于結構體的管理。

C語言關于文件操作的函數

下面只是介紹了一小部分,如果想詳細了解,可以去我的另一篇博客:

C語言文件操作詳談

那么下面先回憶一下之前C語言用的一些函數:

首先就是fopen,并且我記得當時選項w最為奇怪,因為每一個用,那么之前的數據都會不見了,這不是跟我們的輸出重定向(>)很像嗎,因為它們都是每次使用之前的內容就會被清空

這段話的意思是:截斷文件到長度0或者如果沒有那么創建新文件

我們原來都是這么用的:

我們說如果w選項,當前路徑下沒有這個文件,那么就會在當前路徑下去創建,那么進程怎么知道當前在那個路徑下呢?我們說過進程啟動時,會記錄下自己的路徑

所以進程就會在這個路徑下創建新文件

并且我們還有一個選項a,叫做append(附加),這不是跟追加重定向(>>)很像嗎

下面我們再看一下fwrite這個函數

基本的使用就是這樣

我們可以確定的是,輸入或輸出一些東西必須要指定文件,就連鍵盤和顯示器也不例外,因為Linux下一切皆文件,那么平時用printf/scanf的時候也沒打開鍵盤和顯示器文件并且也沒有指定文件啊(我們這么想是因為把鍵盤和顯示器看成和普通文件一樣了),那是因為首先stdout,stdin和stderror是進程默認打開的,不用手動打開,可以直接使用

其次為什么printf不用指定文件呢?因為它為了方便使用,是不用指定文件的,但是底層實現是封裝了fprintf

fprintf是要加stdout的,所以printf就是這么實現的

Linux關于文件操作的系統調用

我們知道,對硬件進行修改只能通過操作系統,所以操作系統就必須提供系統調用接口,像fopen這樣的庫函數是語言層面的概念,為了實現語言的跨平臺性和可移植性,所以它要封裝系統調用,并且在不同的操作系統要封裝各自的系統調用。所以我們下面就介紹一下Linux操作系統關于文件操作的系統調用open和close

open的第一個參數就是文件所處的路徑,第二個參數就是之前說的類似于“w“、“a”的一些選項,常見的打開標志有:

O_RDONLY:以只讀方式打開文件

O_WRONLY:以只寫方式打開文件

O_CREAT:沒有這個文件就創建

O_TRUNC:打開文件前會清空文件

O_APPEND:在文件尾追加數據

第三個參數就是我剛創建好一個文件,此時要給文件設置的權限,返回值就是文件描述符,其實就是一個整數,通過這個整數,就可以確定這個文件,具體是怎么確定的,這就跟底層實現有關了,我們后面會介紹一下

當我們用第一個open時,并且路徑中沒有,它需要創建,這時我們可以看到文件的權限是亂碼

所以我們一般使用第二個,權限計算還是給定的權限減去權限掩碼,我們把它們當成八進制數,比如:

666-002=664

并且通過umask()函數我們還可以在當前程序中設置權限掩碼并且這個權限掩碼只在當前程序下生效

666-666=000

我們上面介紹了第二個參數,第二個參數要給一個整數,其實下面的一些“選項”就是一些,這些宏可以通過位運算決定整數的比特位,其實就是位圖,從而達到不同的下面的選項,知道了這些宏的意義,我們就可以和之前fopen的不同選項的功能對應上了,其實“w”選項不就是這三個選項的疊加嗎

其實如果只有前兩個的話原始文件中的內容并不會清空,而新內容就從頭開始進行覆蓋,就是這樣一種現象

選項“a”不就是這三個選項的疊加嗎

并且追加內容時會在下一行追加

所以我們說fopen底層就是封裝了open,并且不同的選項底層就對應了不同的宏,我們上面說stdin、stdout、stderr每個進程都會默認打開,并且它們的類型是FILE*,這是C語言層面的類型,本質就是一個結構體,既然Linux要通過文件描述符來確定一個文件C語言要通過FILE*的對象,所以FILE結構體中肯定有文件描述符

為了探究文件描述符到底是什么,我們可以連續創建一些文件看看它們的文件描述符之間有什么規律

文件描述符是從3開始依次增長,那0,1,2呢?其實0,1,2就分別是程序默認打開的stdin,stdout和stderr,并且這個數字其實就是數組下標,什么意思呢?

我們知道,操作系統不僅要進行進程管理,還要進行文件管理,對于進程的管理我們知道是通過PCB,就是一個結構體對象;同樣,對于打開的文件(不管以什么樣的方式打開),操作系統也是要創建一個結構體對象(比如我們叫做struct file)進行管理的,我們可以把它們之間的關系大致理解為這樣:

就是說:一個進程的PCB中存著此進程打開的文件列表的指針,通過這個指針可以找到打開的文件列表,而這個文件列表中也是存著每個文件管理的指針,通過這個指針就可以找到操作系統對文件進行管理的結構體了

這就是為什么我們可以通過文件描述符(一個整數),來確定唯一一個文件了

并且要注意,我們圖中的緩沖區是操作系統給每個打開的文件提供的,和下面說的C語言庫函數提供的緩沖區不是一個概念

既然stdout等是自動打開的,我們可以關閉stdout試一試,還不能關這個,因為一旦關了就打印不出東西來了,我們可以關掉stdin,于是新打開的文件的文件描述符就會從最小的沒用到的0開始

這個_fileno就是FILE*結構體中文件描述符變量的名字

所以我們就可以利用上面的規則實現重定向,比如printf默認向stdout中打印,其實它認識的是文件描述符為1,所以我們就可以這樣,將想打印的內容打印到一個文件中而不是顯示器

這樣也確實比較麻煩,并且還要了解文件描述符的分配規則,其實也不知可以這么做,系統還提供了系統調用來供我們使用,這個就是通過拷貝指針來實現目的,就是上面圖中中間那個圖里邊的指針

于是我們就可以這么用:

就是讓數組下標為1的里邊的內容變成數組下標為fd的里邊的內容,這樣printf打印肯定還是向數組下標為1的里邊的指針指向的文件打印,這時文件就變成log.txt

完善myshell

既然已經明白了重定向的原理:就是通過文件描述符實現向任意一個文件中寫入,本質上就是改變文件指針數組中的指針就可以實現向指定的文件中寫入,于是我們就可以完善一下我們之前寫的myshell

可以移步到下面這篇博客中:自定義bash進程

C語言緩沖區

我們這里說的緩沖區其實就是C語言庫中提供的一個緩沖區,這么說可能有點抽象,其實就是在我們調用文件相關函數的時候,需要用到FILE這個類型,這個類型其實就是一個結構體,里邊就存著緩沖區。

也就是說:不管是log.txt這樣的普通文件的文件指針,又或者是stdout這樣的進程自動打開的文件指針,它們都是一個結構體對象,這個對象中就存著緩沖區,那么這樣好處是什么呢?

1.首先我們要知道,調用系統調用是有成本的,所以我們需要盡可能的少的調用,那么我先暫時把數據寫到C語言的緩沖區中,最后統一的調用系統調用把數據給操作系統,這樣系統調用的次數就減少了

2.其次在寫代碼的人看來,執行完這句代碼,數據就好像已經寫入到了文件中,但實際上是寫入到了緩沖區中,這種在內存中的拷貝可比把數據寫入到磁盤當中快多了,這樣表現出來的就是C語言的速度很快,可以提高寫代碼的人的體驗

我剛才說緩沖區就在FILE的結構體對象中,那么它跟我們之前說的進程地址空間是什么關系呢?

其實你得看FILE對象在哪,FILE對象可以在調用fopen函數時在函數內部去堆上申請,所以緩沖區就在堆上,我們下邊也會模擬實現一下緩沖區的工作原理。而堆不就是進程地址空間的一部分嘛。

我們在語言層面上有這么幾種處理不同文件的緩沖區的策略

1.無緩沖區,不用刷新

2.行刷新,比如要寫入到顯示器中

3.全緩沖,全部刷新,就是一般文件只有當緩沖區寫滿時才刷新

當然我們也可以主動刷新,比如調用fflush函數,或者進程結束時,緩沖區會自動刷新

那么,上面說的緩沖區的位置,處理不同文件的緩沖區的策略你怎么證明呢?下面我們就來寫一個代碼驗證一下

我們運行這個代碼,無論是直接運行,或者是打印到一個文件中,都是正常的:

但是當我們僅在代碼尾部加了一個fork后,結果就變了

可以看到,打印到顯示器上是正常的,但是打印到文件中,系統調用打印了一回,庫函數打印了兩回。這是因為系統調用沒有緩沖區,直接寫到操作系統中;而庫函數因為是寫入到文件中,所以不是行刷新,程序結束才會刷新,而在程序沒結束時,子進程被創建,父子進程共享進程地址空間,而進程地址空間中就存著緩沖區,緩沖區中有內容,程序結束時,父子進程緩沖區中的內容都要被刷新出來刷新緩沖區也是一種修改數據,所以發生寫時拷貝

知道了上面的理論,我們可以自己創建一個源代碼文件來實現FILE結構體并且實現相關函數,我們實現的大體思路是首先用法要和庫中的保持一致,我們內部實現要加上緩沖區,并且對于不同種類的文件要有不同的刷新方式,我們只要是通過自己實現知道C語言的緩沖區具體在哪里就可以

//myFILE.h
#pragma once
#include<string.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<sys/types.h>
enum MODEBUFF
{NO_BUFF,ROW_BUFF,FULL_BUFF,
};
typedef struct MYFILE
{int fileno;char buffer[64];int pos;int mode;
}MYFILE;MYFILE* fopen(const char*path,const char*option);int mywrite(const char*str,int size,int n,MYFILE*fp);int fclose(MYFILE*fp);
//myFILE.c
#include"myFILE.h"MYFILE* fopen(const char*path,const char*option)
{MYFILE* tmp=(MYFILE*)malloc(sizeof(MYFILE));tmp->pos=0;tmp->mode=FULL_BUFF;if(option[0]=='w'){tmp->fileno=open(path,O_WRONLY|O_CREAT|O_TRUNC,0666);}else if(option[0]=='a'){tmp->fileno=open(path,O_WRONLY|O_CREAT|O_APPEND|0666);}else if(option[0]=='r'){tmp->fileno=open(path,O_RDONLY);}else return NULL;return tmp;
}int mywrite(const char*str,int size,int n,MYFILE*fp)
{while(n--){  if(fp->mode==FULL_BUFF){strncpy(fp->buffer+fp->pos,str,size);fp->pos=strlen(fp->buffer);}else{write(fp->fileno,str,size);}}
return size;
}
int fclose(MYFILE*fp)
{if(fp->pos!=0)write(fp->fileno,fp->buffer,fp->pos);close(fp->fileno);free(fp);fp=NULL;return 0;
}
//test.c
#include"myFILE.h"int main()
{MYFILE* fp=fopen("./tmp1","w");mywrite("abcbcbbc",5,1,fp);fork();fclose(fp);return 0;
}
//makefile
myFILE:myFILE.c test.cgcc -o $@ $^
.PHONY:clean
clean:rm myFILE 

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/22877.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/22877.shtml
英文地址,請注明出處:http://en.pswp.cn/web/22877.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

大模型日報2024-06-05

大模型日報 2024-06-05 大模型資訊 AI氣象預測取得重大進展&#xff1a;單臺桌面電腦即可運行全球天氣模型 摘要: 一項新的人工智能天氣預測模型已經取得重大進展&#xff0c;該模型能夠在一臺普通的桌面電腦上運行&#xff0c;預測全球天氣。這意味著即使沒有復雜的物理計算&a…

2024-5-19——找出數組游戲的贏家

2024-5-19 題目來源我的題解方法一 純模擬方法二 一次遍歷 題目來源 力扣每日一題&#xff1b;題序&#xff1a;1535 我的題解 方法一 純模擬 排除一種情況&#xff1a;當k>n-1時&#xff0c;至少會遍歷數組一遍&#xff0c;實質就是求數組的最大值。 其余的情況使用List…

對象格式的數據表單循環校驗

首先我們的代碼數據是這樣的&#xff08;直接和后臺對應&#xff09; ruleForm: { roadList: {vehicle: {name:"機動車",width: 0,length:0,area:0},notVehicle: {name:"非機動車",width: 0,length:0,area:0},walk: {name:"人行道",width: 0,len…

rust學習(字節數組轉string)

最新在寫數據傳輸相關的操作&#xff0c;發現string一個有趣的現象&#xff0c;代碼如下&#xff1a; fn main() {let mut data:[u8;32] [0;32];data[0] a as u8;let my_str1 String::from_utf8_lossy(&data);let my_str my_str1.trim();println!("my_str len is…

STM32實驗之USART串口發送+接受數據(二進制/HEX/文本)

涉及三個實驗&#xff1a; 1.USART串口發送和接收數據 我們使用的是將串口封裝成為一個Serial.c模塊.其中包含了 void Serial_Init(void);//串口初始化 void Serial_SendByte(uint8_t Byte);//串口發送一個字節 void Serial_SendArray(uint8_t *Array,uint16_t Length);//…

fun()const->Iterator

先看一個函數定義如下&#xff1a; template <typename T> auto Blocker<T>::ObservedEnd() const -> Iterator {return observed_msg_queue_.end(); } 1 迭代器&#xff1a;Iterator 2 C auto 返回類型推導&#xff1a;-> Iterator 3 函數體限定不能修改…

【C語言】詳解函數(上)(庖丁解牛版)

文章目錄 1. 前言2. 函數的概念3.庫函數3.1 標準庫和頭文件3.2 庫函數的使用3.2.1 頭文件的包含3.2.2 實踐 4. 自定義函數4.1 自定義函數的語法形式4.2 函數的舉例 5. 形參和實參5.1 實參5.2 形參5.3 實參和形參的關系 6. return 語句6. 總結 1. 前言 一講到函數這塊&#xff…

棧排序00

題目鏈接 棧排序 題目描述 注意點 對棧進行排序使最小元素位于棧頂最多只能使用一個其他的臨時棧存放數據不得將元素復制到別的數據結構&#xff08;如數組&#xff09;中棧中的元素數目在[0, 5000]范圍內 解答思路 本題是要實現一個小頂堆&#xff0c;可以直接使用Priori…

上位機圖像處理和嵌入式模塊部署(f407 mcu中的udp server開發)

【 聲明&#xff1a;版權所有&#xff0c;歡迎轉載&#xff0c;請勿用于商業用途。 聯系信箱&#xff1a;feixiaoxing 163.com】 既然lwip已經port到407上面了&#xff0c;接下來其實就可以做一些測試了。本身lwip支持tcp、udp&#xff0c;也支持client和server&#xff0c;既然…

【數據分享】中國第三產業統計年鑒(1991-2022)

大家好&#xff01;今天我要向大家介紹一份重要的中國第三產業統計數據資源——《中國第三產業統計年鑒》。這份年鑒涵蓋了從1991年到2022年中國第三產業統計全面數據&#xff0c;并提供限時免費下載。&#xff08;無需分享朋友圈即可獲取&#xff09; 數據介紹 每年的《中國…

LeetCode.55 跳躍游戲

LeetCode.55 跳躍游戲 題目描述解題思路錯誤的解題思路解題思路 代碼 題目描述 解題思路 錯誤的解題思路 我一開始的思路是累加可跳范圍內的最大值sum&#xff0c;如果最終sum > nums.size()那么就返回true&#xff0c;這種思路是錯誤的&#xff0c;因為在你選擇最大值的…

2004NOIP普及組真題 3. FBI樹

線上OJ 地址&#xff1a; [04NOIP普及組] FBI樹 本題的意思是&#xff1a;給定一個 01字符串 &#xff08;對應一棵完全二叉樹的最后一層葉子節點&#xff09;&#xff0c;將樹的每一個節點的值用字母“F、B、I”表示。規則&#xff08;如下圖所示&#xff09;為&#xff1a; 1…

Spring AI 第二講 之 Chat Model API 第二節Ollama Chat

通過 Ollama&#xff0c;您可以在本地運行各種大型語言模型 (LLM)&#xff0c;并從中生成文本。Spring AI 通過 OllamaChatModel 支持 Ollama 文本生成。 先決條件 首先需要在本地計算機上運行 Ollama。請參閱官方 Ollama 項目 README&#xff0c;開始在本地計算機上運行模型…

curl 92 HTTP/2 stream 5 was not closed cleanly: CANCEL

source ~/.bash_profile flutter clean Command exited with code 128: git fetch --tags Standard error: 錯誤&#xff1a;RPC 失敗。curl 92 HTTP/2 stream 5 was not closed cleanly: CANCEL (err 8) 錯誤&#xff1a;預期仍然需要 2737 個字節的正文 fetch-pack: unexpec…

GPT革命:AI如何重塑我們的未來!

GPT革命&#xff1a;AI如何重塑我們的未來&#xff01; &#x1f604;生命不息&#xff0c;寫作不止 &#x1f525; 繼續踏上學習之路&#xff0c;學之分享筆記 &#x1f44a; 總有一天我也能像各位大佬一樣 &#x1f3c6; 博客首頁 怒放吧德德 To記錄領地 &#x1f31d;分享…

普通人也能弄的 16 個AI搞錢副業,門檻低,易上手!

大家好&#xff0c;我是靈魂畫師向陽 本期給大家分享的是利用AI 做副業的一些方法&#xff0c;大家可以挑選適合自己的賽道去搞錢 現在是人工智能時代&#xff0c;利用好AI 工具&#xff0c;可以降低普通人做副業的門檻&#xff0c;同時也能提高工作效率&#xff0c; 因此AI …

【微機原理與匯編語言】循環程序設計

一、實驗目的 1.熟練掌握8086/8088常用匯編指令的使用方法 2.熟練掌握循環結構程序編程技巧 3.熟練掌握匯編語言程序運行調試方法 二、實驗要求 認真分析實驗題目&#xff0c;設計程序流程圖&#xff0c;獨立完成代碼編寫及運行調試。 三、實驗題目 給出不大于255的十個…

圖片裁剪與上傳處理方案 —— 基于阿里云 OSS 處理用戶資料

目錄 01: 通用組件&#xff1a;input 構建方案分析 02: 通用組件&#xff1a;input 構建方案 03: 構建用戶資料基礎樣式 04: 用戶基本資料修改方案 05: 處理不保存時的同步問題 06: 頭像修改方案流程分析 07: 通用組件&#xff1a;Dialog 構建方案分析 08: 通用組件&…

計算機組成原理·考點知識點整理

根據往年考試題&#xff0c;對考點和知識點的一個整理。 校驗編碼 碼距 一種編碼的最小碼距&#xff0c;其實就是指這種編碼的碼距。碼距有兩種定義&#xff1a; 碼距所描述的對象含義 2 2 2 個特定的碼其二進制表示中不同位的個數一種編碼這種編碼中任意 2 2 2 個合法編碼的…

【linux進程控制(三)】進程程序替換--如何自己實現一個bash解釋器?

&#x1f493;博主CSDN主頁:杭電碼農-NEO&#x1f493; ? ?專欄分類:Linux從入門到精通? ? &#x1f69a;代碼倉庫:NEO的學習日記&#x1f69a; ? &#x1f339;關注我&#x1faf5;帶你學更多操作系統知識 ? &#x1f51d;&#x1f51d; 進程程序替換 1. 前言2. exec…