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