免費網(wǎng)絡(luò)翻外墻軟件寧波seo高級方法
【Linux基礎(chǔ)IO篇】用戶緩沖區(qū)、文件系統(tǒng)、以及軟硬鏈接
目錄
- 【Linux基礎(chǔ)IO篇】用戶緩沖區(qū)、文件系統(tǒng)、以及軟硬鏈接
- 深入理解用戶緩沖區(qū)
- 緩沖區(qū)刷新問題
- 緩沖區(qū)存在的意義
- File
- 模擬實現(xiàn)C語言中文件標(biāo)準(zhǔn)庫
- 文件系統(tǒng)
- 認(rèn)識磁盤
- 對目錄的理解
- 軟硬鏈接
- 軟硬鏈接的刪除
- 文件的三個時間
作者:愛寫代碼的剛子
時間:2023.11.5
前言:本篇博客將介紹緩沖區(qū)、磁盤的構(gòu)成、分區(qū),以及文件系統(tǒng)中的結(jié)構(gòu)、軟硬鏈接
深入理解用戶緩沖區(qū)
觀察幾個現(xiàn)象:
正常現(xiàn)象一:
現(xiàn)象二:
現(xiàn)象三:
圖示:
解釋現(xiàn)象:
-
對于現(xiàn)象二:C語言文件操作函數(shù)中的字符串不帶有’\n’,即數(shù)據(jù)還停留在C語言的緩沖區(qū)中,并未刷新和調(diào)用write()函數(shù),將數(shù)據(jù)刷新到內(nèi)核系統(tǒng)文件的緩沖區(qū)中,而write()函數(shù)由于是系統(tǒng)調(diào)用函數(shù),能正常執(zhí)行,但是由于close()函數(shù)將顯示器文件關(guān)閉了,即使進程結(jié)束也不能將數(shù)據(jù)從緩沖區(qū)刷新到顯示器中。(如果將close函數(shù)屏蔽,將會將C語言緩沖區(qū)中的數(shù)據(jù)刷新到顯示器)
-
對于現(xiàn)象三:(1)在程序未進行重定向之前,默認(rèn)是將數(shù)據(jù)寫入顯示器中,當(dāng)遇到’\n’時,將數(shù)據(jù)從C語言的緩沖區(qū)中刷新到內(nèi)核文件系統(tǒng)的緩沖區(qū),fork()并不影響原本程序的運行。(2)在程序進行重定向之后,數(shù)據(jù)從向顯示器寫入變?yōu)榱讼蛭募袑懭?#xff0c;緩沖區(qū)從行緩沖變?yōu)榱巳彌_,所以此時的’\n’并不起作用,所以數(shù)據(jù)依然存儲在C語言的緩沖區(qū)中,fork()函數(shù)創(chuàng)建子進程時子進程會將父進程的C語言緩沖區(qū)進行拷貝,當(dāng)父子進程結(jié)束后將緩沖區(qū)里面的內(nèi)容全部刷新,輸出到顯示器中。
- 顯示器的文件的刷新方案是行刷新,所以在printf執(zhí)行完就會在遇到’\n’的時候會立即進行刷新,用戶刷新的本質(zhì)就是將數(shù)據(jù)通過1+write寫入到內(nèi)核中。(目前我們認(rèn)為,只要將數(shù)據(jù)刷新到了內(nèi)核,數(shù)據(jù)就可以到硬件了)。
緩沖區(qū)刷新問題
- 無緩沖 ——直接刷新
- 行緩沖——不刷新,碰到’\n’刷新(顯示器)
- 全緩沖——緩沖區(qū)滿了才刷新(普通文件的寫入)
- 進程退出的時候也會刷新緩沖區(qū)
緩沖區(qū)存在的意義
- 解決效率問題
- 配合格式化(printf中需要將%d等符號進行替換)
File
FILE里面含有對應(yīng)打開文件等緩沖區(qū)字段和維護信息,同時FILE對象屬于用戶,這個緩沖區(qū)屬于用戶級緩沖區(qū)(語言屬于用戶層)
模擬實現(xiàn)C語言中文件標(biāo)準(zhǔn)庫
- Mystdio.h文件:
#ifndef __MYSTDIO_H__
#define __MYSTDIO_H__
//open頭文件
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
//
#include <unistd.h>
//
#include <string.h>
#include <stdlib.h>#define FILE_MODE 0666
#define SIZE 1024#define FLUSH_NOW 1//0001
#define FLUSH_LINE 2//0010
#define FLUSH_ALL 4//0100typedef struct IO_FILE{int fileno;int flag;char inbuffer[SIZE];int in_pos;char outbuffer[SIZE];int out_pos;//緩沖區(qū)的有效字符和無效字符分界
}_FILE;_FILE* _fopen(const char* filename,const char* flag);
int _fwrite(_FILE *fp,const char*s,int len);
void _fclose(_FILE *fp);#endif
- Mystdio.c文件:
#include "Mystdio.h"_FILE* _fopen(const char* filename,const char* flag)
{int f=0;int fd=-1;if(strcmp(flag,"w")==0){f=O_CREAT|O_WRONLY|O_TRUNC;fd= open(filename,f,FILE_MODE);}else if(strcmp(flag,"r")==0){f=O_CREAT|O_RDONLY|O_TRUNC;fd= open(filename,f,FILE_MODE);}else if(strcmp(flag,"a")==0){f=O_APPEND;fd= open(filename,f);}else{return NULL;}if(fd==-1) return NULL;//創(chuàng)建文件結(jié)構(gòu)對象_FILE* fp=(_FILE*)malloc(sizeof(_FILE));if(fp==NULL)return NULL;fp->fileno = fd;fp->flag= FLUSH_LINE;fp->out_pos=0;return fp;
}
int _fwrite(_FILE *fp,const char*s,int len)
{memcpy(&fp->outbuffer[fp->out_pos],s,len);//這里省略了異常處理fp->out_pos+=len;if(fp->flag & FLUSH_NOW){write(fp->fileno,fp->outbuffer,fp->out_pos);fp->out_pos=0;}else if(fp->flag & FLUSH_LINE){if(fp->outbuffer[fp->out_pos-1]=='\n')//'\n'可能出現(xiàn)在字符中間,這時候需要對字符串做裁剪{write(fp->fileno,fp->outbuffer,fp->out_pos);fp->out_pos=0;}}else if(fp->flag & FLUSH_ALL){if(fp->out_pos==SIZE){write(fp->fileno,fp->outbuffer,fp->out_pos);//全寫出去fp->out_pos=0;}}return len;//成功寫入的長度
}
void _fflush(_FILE *fp)
{if(fp->out_pos>0){write(fp->fileno,fp->outbuffer,fp->out_pos);fp->out_pos=0;}
}
void _fclose(_FILE *fp)
{if(fp==NULL)return ;_fflush(fp);close(fp->fileno);free(fp);
}
Main.c文件:
#include "Mystdio.h"#define myfile "test.txt"
int main()
{_FILE* fp=_fopen(myfile,"w");if(fp==NULL){return 1;}int cnt =10;while(cnt--){const char* msage = "hello Linux\n";_fwrite(fp,msage,strlen(msage));sleep(1);}//_fflush(fp)return 0;
}
測試:
makefile文件:
while :;do cat test.txt;sleep 1;echo “----------------------”;done持續(xù)打印文件內(nèi)容
一般C庫函數(shù)寫入文件時是全緩沖的,而寫入顯示器是行緩沖。 printf fwrite 庫函數(shù)會自帶緩沖區(qū)(進度條例子就可以說明),當(dāng)發(fā)生重定向到普通文件時,數(shù)據(jù)的緩沖方式由行緩沖變成了全緩沖。而我們放在緩沖區(qū)中的數(shù)據(jù),就不會被立即刷新,甚至fork之后 但是進程退出之后,會統(tǒng)一刷新,寫入文件當(dāng)中。 但是fork的時候,父子數(shù)據(jù)會發(fā)生寫時拷貝,所以當(dāng)你父進程準(zhǔn)備刷新的時候,子進程也就有了同樣的 一份數(shù)據(jù),隨即產(chǎn)生兩份數(shù)據(jù)。write沒有變化,說明沒有所謂的緩沖。
printf fwrite 庫函數(shù)會自帶緩沖區(qū)(用戶級緩沖區(qū)),而 write系統(tǒng)調(diào)用沒有帶緩沖區(qū)。(為了提升整機性能,OS也會提供相關(guān)內(nèi)核級緩沖區(qū))
文件系統(tǒng)
認(rèn)識磁盤
- 磁頭是一面一個,磁頭和盤面不接觸,磁頭臂會進行擺動來定位柱面或磁道(磁臂運動越少,效率越高,反之越低),所以在軟件設(shè)計上一定要有意識將相關(guān)數(shù)據(jù)放在一起
磁盤的邏輯結(jié)構(gòu)是線性的(對磁盤理解和建模)
- 扇區(qū)的一般大小是512字節(jié)或者4kb
不僅CPU有寄存器,其他設(shè)備(外設(shè))也存在寄存器
對磁盤進行分區(qū)
關(guān)于inode中的block數(shù)組:
inode中有struct inode結(jié)構(gòu)體,里面包括了文件所有的屬性,和一個blocks[15]數(shù)組,其中的下標(biāo)[0, 11]直接保存的就是該文件對應(yīng)的blocks編號,下標(biāo)[12, 15]指向一個datablock,但是這個datablock不保存有效數(shù)據(jù),而保存文件所適用的其它塊的編號。(相當(dāng)于一個二級索引)
stat +文件名查看文件的具體信息
ls -lia查看所有文件的信息(包括文件的inode)
ls -i查看當(dāng)前目錄下各文件的inode編號
啟動塊的大小是確定的,而塊組的大小是由格式化的時候確定的,并且不可以更改。
文件 = 內(nèi)容 + 屬性,二者都是數(shù)據(jù),都要存儲。Linux采用的是將內(nèi)容和屬性數(shù)據(jù)分開存儲的方案,內(nèi)容在block中(4KB),內(nèi)容是可以無限增多的。屬性數(shù)據(jù)在inode中(128字節(jié)),文件的屬性是穩(wěn)定的。
注意一個要點,inode可能會存在用完的情況
- Linux系統(tǒng)中,一個文件一個inode,每一個inode。每一個inode都有自己的inode編號(inode的設(shè)置是以分區(qū)為單位的,不能跨分區(qū))
對目錄的理解
Linux下一切皆文件,目錄也是一個文件,通過文件名(對應(yīng)的inode編號)-> 找到自己所處的目錄 -> 根據(jù)目錄的inode,找到目錄的data block -> 將文件名和inode編號的映射關(guān)系寫入到目錄的數(shù)據(jù)塊中。
- 問題1:為什么同一個目錄下不能存在同名文件?
因為一個文件只存在一個inode,文件名是作為key值去找inode的,如果存在同名文件就會破壞對應(yīng)的映射關(guān)系。
- 問題2:為什么沒有w權(quán)限就不能創(chuàng)建文件?
即便是能創(chuàng)建文件,也不能將該文件與inode的映射關(guān)系寫到數(shù)據(jù)塊中。
- 問題3:為什么沒有r權(quán)限就不能查看文件?
無法拿到該目錄文件中文件與inode之間的映射關(guān)系
- 問題4:為什么沒有x權(quán)限就不能進入目錄?
不讓用戶更改環(huán)境變量中的目錄信息
查找任何一個文件時,我們必須從當(dāng)前目錄遞歸到根目錄,然后從根目錄信息中查找對應(yīng)子目錄的inode信息(效率會低)
所以Linux中會存在提升效率的方法,將常用的路徑信息進行緩存(dentry緩存,里面的結(jié)構(gòu)算法較復(fù)雜)
軟硬鏈接
建立軟鏈接
軟鏈接的inode不同
軟連接又叫符號鏈接,軟連接文件相當(dāng)于源文件來說是一個獨立的文件,該文件有自己的inode號,但是該文件只包含了源文件的路徑名,所以軟連接文件的大小要比源文件小得多。軟連接就類似于Windows操作系統(tǒng)當(dāng)中的快捷方式。軟鏈接保存的是對應(yīng)文件的所在路徑
ln -s 對應(yīng)的路徑 軟鏈接的名字添加快捷方式
建立硬鏈接
硬鏈接的inode相同,同時文件的引用計數(shù)變?yōu)榱?
- 硬連接數(shù)本質(zhì)就是該文件inode屬性中的計數(shù)器count,標(biāo)識有幾個文件名和我的inode建立了映射關(guān)系。簡言之,就是有幾個文件名指向我的inode(文件本身)硬鏈接就是讓多個不在或者同在一個目錄下的文件名,同時能夠修改同一個文件,其中一個修改后,所有與其有硬鏈接的文件都一起修改了。
為什么文件被創(chuàng)建出來,默認(rèn)的硬連接數(shù)是1?
- 如果硬鏈接數(shù)是0,那么就應(yīng)該是被關(guān)閉的文件了,所以至少應(yīng)該從1開始。此外,普通文件的文件名,本身就和自己的inode具有映射關(guān)系,且只有1個,所以文件的默認(rèn)硬連接數(shù)是1。
創(chuàng)建一個新目錄時引用計數(shù)默認(rèn)為2
- 我們也可以根據(jù)系統(tǒng)的硬連接數(shù),不進入文件,從而估算出文件的目錄數(shù)(一個目錄下相鄰的子目錄數(shù) = 該目錄的硬連接數(shù) - 2)。因此,硬鏈接的一個作用就是進行路徑切換。
軟硬鏈接的刪除
unlink(unlink也可以刪除普通文件,與rm沒什么區(qū)別)
文件的三個時間
這其中包含了文件的三個時間信息:
- Access: 文件最后被訪問的時間。
- Modify: 文件內(nèi)容最后的修改時間。
- Change: 文件屬性最后的修改時間。
當(dāng)我們修改文件內(nèi)容時,文件的大小一般會隨之改變,所以Modify的改變會帶動Change一起改變,但對該文件屬性一般不會影響文件內(nèi)容,所以一般情況下Change的改變不會帶動Modify的改變。此外,我們可以使用touch命令把這三個時間都更新到最新狀態(tài)。(當(dāng)一文件存在時使用touch命令,此時touch命令的作用變?yōu)楦挛募畔?#xff09;。