国产亚洲精品福利在线无卡一,国产精久久一区二区三区,亚洲精品无码国模,精品久久久久久无码专区不卡

當前位置: 首頁 > news >正文

做網(wǎng)站編程用什么語言好seo怎么做最佳

做網(wǎng)站編程用什么語言好,seo怎么做最佳,網(wǎng)站建設選擇題,淄博做網(wǎng)站哪家好文章目錄 Linux文件(系統(tǒng))IO(含動靜態(tài)庫的鏈接操作)1、C語言文件IO操作2、三個數(shù)據(jù)流stdin、stdout、stderr3、系統(tǒng)文件IO3.1、相關系統(tǒng)調(diào)用接口的使用3.2、文件描述符fd3.3、文件描述符的分配規(guī)則3.3、重定向3.4、自制shell加入重…

文章目錄

  • Linux文件(系統(tǒng))IO(含動靜態(tài)庫的鏈接操作)
    • 1、C語言文件IO操作
    • 2、三個數(shù)據(jù)流stdin、stdout、stderr
    • 3、系統(tǒng)文件IO
      • 3.1、相關系統(tǒng)調(diào)用接口的使用
      • 3.2、文件描述符fd
      • 3.3、文件描述符的分配規(guī)則
      • 3.3、重定向
      • 3.4、自制shell加入重定向功能
    • 4、FILE
    • 5、文件系統(tǒng)
      • 5.1、inode
      • 5.2、硬鏈接(Hard Link)
      • 5.3、軟鏈接(Symbolic Link)
    • 6、動態(tài)庫和靜態(tài)庫
      • 6.1、靜態(tài)庫
      • 6.2、動態(tài)庫

img

Linux文件(系統(tǒng))IO(含動靜態(tài)庫的鏈接操作)

1、C語言文件IO操作

  • fopen打開文件,fclose關閉文件

  • fwrite操作(寫文件)

fwrite函數(shù)從ptr里將nitems個大小為size字節(jié)的數(shù)據(jù)寫進定stream

  • hello_w.c文件

    #include <stdio.h>                                                              
    #include <string.h>    int main(){    FILE* pf = fopen("myfile.txt","w");    if(!pf){    perror("fopen");    return 1;    }    const char* msg = "hello fopen\n";    int count = 5;    while(count--){    fwrite(msg,strlen(msg),1,pf);    }    fclose(pf);    return 0;    
    }
    

    這里我們將會在此路徑得到一個寫有5hello fopenmyfile.txt文件,如下gif:

  • fread操作(寫文件)

fread 不會在字符串末尾添加 null 終止符(\0),fgets會。

fread函數(shù)從定stream里讀取nitemssize字節(jié)大小的數(shù)據(jù)存到ptr

  • hello_r.c文件

    #include <stdio.h>
    #include <string.h>int main(){FILE* pf = fopen("myfile.txt","r");if(!pf){perror("fopen");return 1;}char buffer[1024];while(1){//fgets// 每次讀1個字節(jié),讀1024次(文件結(jié)束就不讀)size_t s = fread(buffer,1,sizeof(buffer),pf);if(s > 0){buffer[s] = 0;printf("%s",buffer);}if(feof(pf)){break;}}fclose(pf);return 0;
    }
    

    這里我們將會在此路徑讀到myfile.txt文件,看到打印出來的數(shù)據(jù),如下gif:

  • fgets操作(每次讀文件的一行))

fgets函數(shù)從給定stream中讀取size個字節(jié)放到str中,當發(fā)現(xiàn)換行符、文件結(jié)束符或發(fā)生錯誤時,讀取將停止。

#include <stdio.h>
#include <string.h>int main(){FILE* pf = fopen("myfile.txt","r");if(!pf){perror("fopen");return 1;}char buffer[1024];const char* msg = "hello xxxxx\n";    while(1){    char* r = fgets(buffer,sizeof(msg),pf);                                                  if(!r)    break;    printf("%s",buffer);    }fclose(pf);return 0;
}

  • 打開文件的方式:
			 r      打開文本文件,用于讀。流被定位于文件的開始。r+     打開文本文件,用于讀寫。流被定位于文件的開始。w      將文件長度截斷為零,或者創(chuàng)建文本文件,用于寫。流被定位于文件的開始。w+     打開文件,用于讀寫。如果文件不存在就創(chuàng)建它,否則將截斷它。流被定位于文件的開始。a      打開文件,用于追加(在文件尾寫)。如果文件不存在就創(chuàng)建它。流被定位于文件的末尾。a+     打開文件,用于追加(在文件尾寫)。如果文件不存在就創(chuàng)建它。讀文件的初始位置是文件的開始,但是輸出總是被追加到文件的末尾。

2、三個數(shù)據(jù)流stdin、stdout、stderr

  • stdin(標準輸入流):我們在C語言常用的scanf函數(shù),其默認就是指定了標準輸入流stdin
#include <stdio.h>int main(){int x = 0;scanf("%d",&x);return 0;
}

fread使用標準輸入流:

#include <stdio.h>    
#include <string.h>    int main(){ char buffer[10];    // 這里要注意,這里的sizeof(buffer)-1是多少,fread就會去讀多少數(shù)據(jù),直到讀到這么多數(shù)據(jù)    size_t bytesRead = fread(buffer, sizeof(char), sizeof(buffer)-1, stdin);    buffer[bytesRead] = '\0';  // 字符串結(jié)束符    printf("buff:%s",buffer);return 0;
}

如果我們想指定輸入流,我們可以使用fscanf函數(shù):

#include <stdio.h>    int main(){    FILE* pf = fopen("myfile","r");    if(!pf){    perror("fopen error");    return 1;    }    int in = 0;    fscanf(pf,"%d",&in);    printf("%d\n",in);fclose(pf);return 0;    
}

  • stdout(標準輸出流):我們在C語言常用的printf函數(shù),其默認就是指定了標準輸入流stdout
#include <stdio.h>int main(){printf("%d\n",2);return 0;
}

fwrite使用標準輸出流:

#include <stdio.h>    
#include <string.h>                                                                              int main(){    FILE* pf = fopen("myfile","w");    if(!pf){    perror("fopen");    return 1;    }    const char* msg = "hello fprintf\n";       fwrite(msg,strlen(msg),1,stdout);fclose(pf);	return 0;    
}

如果我們想指定輸出流,我們可以使用fprintf函數(shù):

#include <stdio.h>    int main(){    FILE* pf = fopen("myfile","w");    if(!pf){    perror("fopen error");    return 1;    }    const char* msg = "hello fprintf\n";    fprintf(pf,"%s",msg);                                                                       fclose(pf);return 0;    
}

  • stderr(標準輸入流)(后面講)

3、系統(tǒng)文件IO

3.1、相關系統(tǒng)調(diào)用接口的使用

  • 類比C標準庫中的函數(shù)fopen、fwrite、fread等,我們稱之為庫函數(shù)(權(quán)限低)(底層就是使用系統(tǒng)調(diào)用接口)。

  • 而open、write、read等屬于系統(tǒng)提供的接口,我們稱之為系統(tǒng)調(diào)用接口(權(quán)限高)。

  • 參考下圖的lib和系統(tǒng)調(diào)用接口

  • open打開文件,close關閉文件

這里注意有兩個open系統(tǒng)調(diào)用接口,第一個不帶第三個參數(shù)mode,這個mode就是給創(chuàng)建的文件設置權(quán)限的比如0666,就是讓這個文件的root、自己、其他人的權(quán)限都設置為只能讀寫,當然,這個還要看權(quán)限掩碼umask是多少,假如umask是0002這最后文件的權(quán)限為0664(前面的博客有講權(quán)限掩碼,自行查閱)。

  • 系統(tǒng)調(diào)用接口write寫文件

write接口試圖從buf中取nbyte字節(jié)的數(shù)據(jù)寫到到文件描述符fildes指向的空間中

#include <stdio.h>    
#include <fcntl.h>    
#include <unistd.h>    
#include <string.h>    int main(){    int fd = open("myfile",O_WRONLY|O_TRUNC|O_CREAT,0666);                      printf("%d\n",fd);// 3const char* msg = "hello write\n";    write(fd,msg,strlen(msg));    close(fd);    return 0;    
}
  • 系統(tǒng)調(diào)用接口read讀文件

read接口試圖從文件描述符fildes指向的空間中讀取nbyte字節(jié)放到buf

#include <stdio.h>    
#include <fcntl.h>    
#include <unistd.h>    
#include <string.h>    int main(){    int fd = open("myfile",O_RDONLY);    printf("%d\n",fd);// 3char buff[64];        const char* msg = "hello write\n";    read(fd,buff,strlen(msg));    printf("%s",buff);close(fd);    return 0;    
}

3.2、文件描述符fd

  • 經(jīng)過上面對文件myfile的寫讀,我們知道這個open的返回值fd是3,那么為什么返回值是3?我們再打開幾個文件試試它們的文件描述符到底是多少:

    #include <stdio.h>    
    #include <fcntl.h>    
    #include <unistd.h>    
    #include <string.h>    int main(){int fd1 = open("myfile1",O_WRONLY|O_TRUNC|O_CREAT,0666);    int fd2 = open("myfile2",O_WRONLY|O_TRUNC|O_CREAT,0666);    int fd3 = open("myfile3",O_WRONLY|O_TRUNC|O_CREAT,0666);    int fd4 = open("myfile4",O_WRONLY|O_TRUNC|O_CREAT,0666);    int fd5 = open("myfile5",O_WRONLY|O_TRUNC|O_CREAT,0666);    printf("%d\n",fd1);    printf("%d\n",fd2);    printf("%d\n",fd3);                                                         printf("%d\n",fd4);    printf("%d\n",fd5);//close省時間不寫了return 0;
    }
    

    我們得出結(jié)果是:

    這像是什么?是不是數(shù)組的下標!那么問題又來了,前面的0,1,2去哪了?這時我們想到還有三個數(shù)據(jù)流,分別是stdin、stdout、stderr。我們繼續(xù)來驗證一下:

    #include <stdio.h>    
    #include <fcntl.h>    
    #include <unistd.h>    
    #include <string.h>    int main(){int fd1 = open("myfile1",O_WRONLY|O_TRUNC|O_CREAT,0666);    int fd2 = open("myfile2",O_WRONLY|O_TRUNC|O_CREAT,0666);    int fd3 = open("myfile3",O_WRONLY|O_TRUNC|O_CREAT,0666);    int fd4 = open("myfile4",O_WRONLY|O_TRUNC|O_CREAT,0666);    int fd5 = open("myfile5",O_WRONLY|O_TRUNC|O_CREAT,0666);    printf("%d\n",stdin->_fileno);    printf("%d\n",stdout->_fileno);    printf("%d\n",stderr->_fileno);    printf("%d\n",fd1);    printf("%d\n",fd2);                                                         printf("%d\n",fd3);    printf("%d\n",fd4);    printf("%d\n",fd5);//close省時間不寫了return 0;
    }
    

    這個打印出來的結(jié)果是:

    因此驗證了我們的猜想!

  • 而現(xiàn)在知道,文件描述符就是從0開始的小整數(shù)。當我們打開文件時,操作系統(tǒng)在內(nèi)存中要創(chuàng)建相應的數(shù)據(jù)結(jié)構(gòu)來描述目標文件。于是就有了file結(jié)構(gòu)體,表示一個已經(jīng)打開的文件對象。而進程執(zhí)行open系統(tǒng)調(diào)用,所以必須讓進程和文件關聯(lián)起來。每個進程都有一個指針*files, 指向一張表files_struct,該表最重要的部分就是包涵一個指針數(shù)組,每個元素都是一個指向打開文件的指針!所以,本質(zhì)上,文件描述符就是該數(shù)組的下標。所以,只要拿著文件描述符,就可以找到對應的文件。如下圖:


3.3、文件描述符的分配規(guī)則

  • 來看下列代碼:
#include <stdio.h>    
#include <fcntl.h>    
#include <unistd.h>    int main(){    int fd = open("myfile",O_RDONLY);    if(fd < 0){    perror("open");    return 1;    }    printf("%d\n",fd);    close(fd);    return 0;    
}

輸出是3,關閉0或2試試(這里不能關閉1,因為1是標準輸出流,關閉了我們就看不到輸出結(jié)果,想看到輸出結(jié)果我們需要進行輸出重定向,下面會講)

#include <stdio.h>    
#include <fcntl.h>    
#include <unistd.h>    int main(){    close(0);//close(2);int fd = open("myfile",O_RDONLY);    if(fd < 0){    perror("open");    return 1;    }    printf("%d\n",fd);    close(fd);    return 0;    
}

輸出是0或者2。說明了什么?說明文件描述符的分配規(guī)則是在files_struct數(shù)組當中,找到當前沒有被使用的最小的一個下標,作為新的文件描述符。


3.3、重定向

  • 重定向分為輸入重定向和輸出重定向

  • 針對上面關閉1的情況,關閉1后看不到輸出結(jié)果了,那么我們可以進行輸出重定向,看下面代碼:

    #include <stdio.h>    
    #include <fcntl.h>    
    #include <unistd.h>    int main(){    close(1);int fd = open("myfile",O_WRONLY|O_CREAT,0666);    if(fd < 0){    perror("open");    return 1;    }    printf("%d\n",fd);    fflush(stdout);// 	刷新緩沖區(qū)close(fd);    return 0;    
    }
    

    我們發(fā)現(xiàn),本來應該輸出到顯示器上的數(shù)據(jù),輸出到了myfile文件中!這種現(xiàn)象叫做輸出重定向。輸入重定向類似(從myfile文件中讀取數(shù)據(jù))。

    常見的重定向有:>, >>, <

  • 那么重定向的本質(zhì)是什么?看下圖:

    也就是將file*的指針重新指向新的文件。

  • 除了使用關閉標準輸入輸出流的方法實現(xiàn)重定向功能,還可以使用dup2系統(tǒng)調(diào)用。

    dup2系統(tǒng)調(diào)用,fildes2文件描述符是指定的,fildes是需要重定向的文件描述符。

    使用dup2系統(tǒng)調(diào)用代碼:

    #include <stdio.h>    
    #include <fcntl.h>    
    #include <unistd.h>    int main(){    int fd = open("myfile",O_RDWR|O_CREAT,0666);                                   if(fd < 0){    perror("open");    return 1;    }    close(1);    dup2(fd,1);    printf("hello dup2\n");    fflush(stdout);    return 0;    
    }
    

    #include <stdio.h>
    #include <unistd.h>
    #include <fcntl.h>int main() {int fd = open("./log", O_CREAT | O_RDWR);if (fd < 0) {perror("open");return 1;}close(1);dup2(fd, 1);for (;;) {char buf[1024] = {0};ssize_t read_size = read(0, buf, sizeof(buf) - 1);// 從標準輸入流(命令行)讀取數(shù)據(jù)if (read_size < 0) {perror("read");break;}printf("%s", buf);fflush(stdout);}return 0;
    }
    

3.4、自制shell加入重定向功能

  • myshell.c文件

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/wait.h>
    #include <fcntl.h>
    #include <sys/stat.h>#define SIZE 1024
    #define MAX_LEN 128
    #define SEP " "// 下面的都和重定向有關
    #define NoneRedir  -1
    #define StdinRedir  0
    #define StdoutRedir 1
    #define AppendRedir 2//#define IgnSpace(buf,pos) do{ while(isspace(buf[pos])) pos++; }while(0)int redir_type = NoneRedir;
    char *filename = NULL;char* argv[MAX_LEN];//命令行字符串數(shù)組
    char pwd[SIZE];
    char envtemp[SIZE];
    int lastcode = 0;//退出碼const char* HostName(){char* hostname = getenv("HOSTNAME");if(hostname) return hostname;else return "None";
    }const char* UserName(){char* hostname = getenv("USER");if(hostname) return hostname;else return "None";
    }const char* CurrentWorkDir(){char* hostname = getenv("PWD");if(hostname) return hostname;else return "None";
    }char* Home(){char* hostname = getenv("HOME");if(hostname) return hostname;else return "None"; 
    }int Interactive(char* commandline, int size){printf("[%s@%s %s]$ ",UserName(),HostName(),CurrentWorkDir());fgets(commandline,SIZE,stdin);commandline[strlen(commandline)-1] = '\0';return strlen(commandline);//空串返回0
    }void Check_redir(char in[])
    {// ls -a -l// ls -a -l > log.txt// ls -a -l >> log.txt// cat < log.txtredir_type = NoneRedir;filename = NULL;int pos = strlen(in) - 1;while( pos >= 0 ){if(in[pos] == '>'){if(in[pos-1] == '>'){redir_type = AppendRedir;in[pos-1] = 0;pos++;//IgnSpace(in, pos);while(in[pos] == ' '){pos++;}filename = in+pos;break;}else{redir_type = StdoutRedir;in[pos++] = 0;//IgnSpace(in, pos);while(in[pos] == ' '){pos++;}filename = in+pos;//printf("debug: %s, %d\n", filename, redir_type);break;}}else if(in[pos] == '<'){redir_type = StdinRedir;in[pos++] = 0;//IgnSpace(in, pos);while(in[pos] == ' '){pos++;}filename = in+pos;//printf("debug: %s, %d\n", filename, redir_type);break;}else{pos--;}}
    }void Split(char* commandline){Check_redir(commandline);int i = 0;argv[i++] = strtok(commandline,SEP);while(argv[i++] = strtok(NULL,SEP));//解決ls無彩色問題if(strcmp(argv[0],"ls") == 0){argv[i-1] = (char*)"--color";argv[i] = NULL;}
    }int BuildingCmd(){int ret = 0;if(strcmp(argv[0],"cd") == 0){ret = 1;char* target = argv[1];//cd XXX 和cdif(!target) target = Home();//第二個參數(shù)為NULL//改變當前工作目錄chdir(target);//處理target為..的情況//重新獲取當前路徑char temp[1024];getcwd(temp,1024);//更新當前環(huán)境變量PWDsnprintf(pwd,SIZE,"PWD=%s",temp);//導出環(huán)境變量putenv(pwd);}else if(strcmp(argv[0],"export") == 0){ret = 1;if(argv[1]){strcpy(envtemp,argv[1]);putenv(envtemp);}}else if(strcmp(argv[0],"echo") == 0){ret = 1;if(argv[1] == NULL){printf("\n");}else{if(argv[1][0] == '$'){if(argv[1][1] == '?'){printf("%d\n",lastcode);lastcode = 0;}else{char* e = getenv(argv[1]+1);if(e) printf("%s\n",e);}}else{printf("%s\n",argv[1]);}}}return ret;
    }void Execute(){//只能交給子進程,如果用父進程執(zhí)行命令行,執(zhí)行一次就結(jié)束了pid_t id = fork();if(id < 0) perror("fork\n");else if(id == 0){int fd = -1;if(redir_type == StdinRedir){fd = open(filename, O_RDONLY);dup2(fd, 0);}else if(redir_type == StdoutRedir){umask(0);fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC,0666);dup2(fd, 1);}else if(redir_type == AppendRedir){fd = open(filename, O_CREAT | O_WRONLY | O_APPEND);dup2(fd, 1);}else{// do nothing}execvp(argv[0],argv);exit(1);//執(zhí)行完退出}//父進程等待子進程int status = 0;pid_t rid = waitpid(id,&status,0);if(rid == id) lastcode = WEXITSTATUS(status);//等待成功
    }int main(){while(1){char commandline[SIZE];//1. 打印命令行提示符,獲取用戶的命令字符串int n = Interactive(commandline,SIZE);if(!n) continue;//返回值為0就是空串,下面代碼不再執(zhí)行//2. 對命令行字符串進行切割Split(commandline);//3. 處理內(nèi)建命令n = BuildingCmd();if(n) continue;//4. 執(zhí)行這個命令Execute();}//int i;//for(i=0;argv[i];++i){//    printf("argv[%d]:%s\n",i,argv[i]);//}return 0;
    }
    

4、FILE

  • 因為IO相關函數(shù)與系統(tǒng)調(diào)用接口對應,并且?guī)旌瘮?shù)封裝系統(tǒng)調(diào)用,所以本質(zhì)上,訪問文件都是通過fd訪問的。所以C庫當中的FILE結(jié)構(gòu)體內(nèi)部,必定封裝了fd。

  • 研究以下代碼看輸出結(jié)果

#include <stdio.h>    
#include <unistd.h>    
#include <sys/types.h>    
#include <sys/stat.h>    
#include <fcntl.h>    
#include <string.h>    int main(){    //int fd  = open("myfile",O_RDWR|O_CREAT|O_TRUNC,0666);    const char* msg1 = "hello printf\n"; // \n自帶刷新    const char* msg2 = "hello fwrite\n";    const char* msg3 = "hello write\n";    write(1,msg3,strlen(msg3));    printf("%s",msg1);    fwrite(msg2,strlen(msg2),1,stdout);    fork();    return 0;    
}

結(jié)果如下:

但如果對進程實現(xiàn)輸出重定向呢? ./buffer > buffile , 我們發(fā)現(xiàn)結(jié)果變成了:

我們發(fā)現(xiàn) printf 和 fwrite (庫函數(shù))都輸出了2次,而 write 只輸出了一次(系統(tǒng)調(diào)用)。為什么呢?肯定和fork有關!

  • 一般C庫函數(shù)寫入文件時是全緩沖的,而寫入顯示器是行緩沖

  • printf和``fwrite `庫函數(shù)會自帶緩沖區(qū)(之前的進度條例子就可以說明),當發(fā)生重定向到普通文件時,數(shù)據(jù)的緩沖方式由行緩沖變成了全緩沖。而我們放在緩沖區(qū)中的數(shù)據(jù),就不會被立即刷新,甚至fork之后。但是進程退出之后,會統(tǒng)一刷新,寫入文件當中。但是fork的時候,父子數(shù)據(jù)會發(fā)生寫時拷貝,所以當你父進程準備刷新(刷新完相當于清空數(shù)據(jù),就會發(fā)生寫時拷貝)的時候,子進程也就有了同樣的一份數(shù)據(jù),隨即產(chǎn)生兩份數(shù)據(jù)。

  • write 沒有變化,說明沒有所謂的緩沖。

  • 圖示:

  • 那么為什么會有緩沖區(qū)這個概念呢?
  • 可以提高使用者的效率
  • **減少系統(tǒng)調(diào)用次數(shù):**調(diào)用系統(tǒng)調(diào)用是有時間和空間上的消耗的,增加緩沖區(qū)可以在數(shù)據(jù)填滿過后再調(diào)用系統(tǒng)調(diào)用,減少調(diào)用系統(tǒng)調(diào)用的次數(shù)
  • **減少磁盤訪問次數(shù):**直接將數(shù)據(jù)寫入磁盤會導致頻繁的磁盤訪問,而磁盤訪問是一種相對較慢的操作。通過使用緩沖區(qū),可以將多個寫入操作合并到一起,減少磁盤訪問次數(shù),提高寫入性能。
  • 模擬實現(xiàn)FILE結(jié)構(gòu)體
  • mystdio.h文件

    #include<stdio.h>                                                                                                                                                     #define NONE_FLUSH (1<<1)
    #define LINE_FLUSH (1<<2)
    #define FULL_FLUSH (1<<3)
    #define SIZE 1024typedef struct _myFILE{int fileno;int pos;// 當前寫入位置int cap;// 文件容量int flush_mode; // 刷新模式char buff[SIZE];
    }myFILE;myFILE* my_fopen(const char *path, const char *mode);int my_fwrite(const char *ptr, int size,myFILE *stream);//int my_fread(void *ptr, int size, myFILE *stream);void my_fclose(myFILE* stream);const char *toString(int flag);void DebugPrint(myFILE *fp);
    
  • mystdio.c文件

    #include "mystdio.h"                                                                                                                                                                                                                                                                                                                                                                   
    #include <fcntl.h>           
    #include <string.h>          
    #include <sys/types.h>       
    #include <sys/stat.h>        
    #include <stdlib.h>      
    #include <unistd.h>      myFILE* my_fopen(const char *path, const char *mode){      int flag = 0;      if(strcmp(mode,"r") == 0){      flag |= O_RDONLY;      }else if(strcmp(mode,"w") == 0){      flag |= (O_WRONLY|O_CREAT|O_TRUNC);      }else if(strcmp(mode,"a") == 0){      flag |= (O_WRONLY|O_CREAT|O_APPEND);      }else{      return NULL;      }      int fd = 0;      if(flag & O_WRONLY){      umask(0);      fd = open(path,flag,0666);      }else{      fd = open(path,flag);      }      if(fd < 0) return NULL;      myFILE* fp = (myFILE*)malloc(sizeof(myFILE));      if(!fp){      perror("malloc");      return NULL;      }      fp->fileno = fd;      fp->pos = 0;      fp->cap = SIZE;      fp->flush_mode = LINE_FLUSH;      return fp;      
    }      void my_fflush(myFILE* stream){      if(stream->pos == 0) return;      write(stream->fileno,stream->buff,stream->pos);      stream->pos = 0;// 刷新后pos到最初位置      
    }      int my_fwrite(const char *ptr, int size,myFILE *stream){      memcpy(stream->buff+stream->pos,ptr,size);// buff從pos開始       stream->pos += size;      if((stream->flush_mode & LINE_FLUSH) && (stream->buff[stream->pos-1] == '\n')){      my_fflush(stream);      }else if((stream->flush_mode & FULL_FLUSH) && (stream->pos == stream->cap)){      my_fflush(stream);      }      return size;      
    }      void my_fclose(myFILE* stream){      my_fflush(stream);      close(stream->fileno);      free(stream);      
    }      const char *toString(int flag)      
    {      if(flag & NONE_FLUSH) return "None";      else if(flag & LINE_FLUSH) return "Line";      else if(flag & FULL_FLUSH) return "FULL";      return "Unknow";      
    }      void DebugPrint(myFILE *fp)      
    {      printf("outbufer: %s\n", fp->buff);      printf("fd: %d\n", fp->fileno);      printf("pos: %d\n", fp->pos);      printf("cap: %d\n", fp->cap);      printf("flush_mode: %s\n", toString(fp->flush_mode));      
    } 
    
  • main.c文件

    #include "mystdio.h"    
    #include <string.h>    
    #include <unistd.h>    int main(){    myFILE* fp = my_fopen("myfile","w");    if(!fp) return 1;    const char* msg = "hello my_fwrite\n";    my_fwrite(msg,strlen(msg),fp);                                                                                                                                    //int cnt = 5;    //char buffer[64];    //while(cnt)    //{    //    snprintf(buffer, sizeof(buffer), "helloworld,hellobit,%d  ", cnt--);    //    my_fwrite( buffer, strlen(buffer),fp);    //    DebugPrint(fp);    //    sleep(2);    //    //my_fflush(fp);    //}    //    //fork();    my_fclose(fp);    return 0;    
    }
    

5、文件系統(tǒng)

我們在命令行輸入ls -l ,除了看見文件名,還看見了一些文件的其他屬性

其中還包含:

  • 模式
  • 硬鏈接數(shù)
  • 文件所有者
  • 文件所屬組
  • 大小
  • 最后修改時間
  • 文件名

還可以通過stat命令來獲取文件的更多信息

上面的執(zhí)行結(jié)果有幾個信息我們可以通過inode來解釋一下


5.1、inode

為了能解釋清楚inode,我們需要來理解一下文件系統(tǒng),我們先看以下這張磁盤文件系統(tǒng)圖:

磁盤是一個典型的塊設備,磁盤分區(qū)被劃分為一個個的block(塊)。一個block的大小是在格式化的時候確定的,后面不可更改。

解釋一下上述圖片各名詞的含義:

  • 塊組(Block Group):文件系統(tǒng)會根據(jù)分區(qū)的大小劃分為數(shù)個塊組,一個塊組就是一個分區(qū),并且每個塊組都會有相同的結(jié)構(gòu)組成。

  • 超級塊(Super Block):存放文件系統(tǒng)本身的信息。記錄的信息主要有:bolck 和 inode的總量,未使用的block和inode的數(shù)量,一個block和inode的大小,最近一次掛載的時間,最近一次寫入數(shù)據(jù)的時間,最近一次檢驗磁盤的時間等其他文件系統(tǒng)的相關信息。Super Block的信息被破壞,可以說整個文件系統(tǒng)結(jié)構(gòu)就被破壞了,不過一般磁盤不止一個扇區(qū)存放有超級塊,可能會有三四個扇區(qū)存放有超級塊,以便于第一個扇區(qū)的超級塊損壞可以通過其他扇區(qū)的超級塊來恢復。

  • 塊組描述(Group Discriptor Table):描述塊組屬性信息,也就是管理磁盤塊位圖、inode為徒、inode表、數(shù)據(jù)塊的使用情況。

  • 塊位圖(Block Bitmap):塊位圖中記錄著數(shù)據(jù)塊中哪個數(shù)據(jù)塊已經(jīng)被占用,哪個數(shù)據(jù)塊沒有被占用。

  • inode位圖(inode Bitmap):每個bite表示該inode編號是否已經(jīng)被使用,比如0表示沒有被使用,1表示已經(jīng)被使用。

  • inode表(inode Table):存放文件屬性。如文件大小,所有者,最近修改時間,使用的塊情況等

  • 數(shù)據(jù)塊(Data Block):存放文件內(nèi)容。

將屬性和數(shù)據(jù)分開存放的想法看起來很簡單,但實際上是如何工作的呢?我們通過touch一個新文件來看看如何工作的。

查看inode編號ls -li。

從整體上來看,創(chuàng)建一個文件所經(jīng)歷的步驟如下:

為了說明問題,我們將上圖簡化:

創(chuàng)建文件一般需要4個操作:

  1. 存儲屬性

    內(nèi)核先找到一個空閑的i節(jié)點(這里是263466)。內(nèi)核把文件信息記錄到其中。

  2. 存儲數(shù)據(jù)

    該文件需要存儲在三個磁盤塊,內(nèi)核找到了三個空閑塊:300,500,800。將內(nèi)核緩沖區(qū)的第一塊數(shù)據(jù)復制到300,下一塊復制到500,以此類推。

  3. 記錄分配情況

    文件內(nèi)容按順序300,500,800存放。內(nèi)核在inode上的磁盤分布區(qū)記錄了上述塊列表。

  4. 添加文件名到目錄

    新的文件名abc。linux如何在當前的目錄中記錄這個文件?內(nèi)核將入口(263466,abc)(映射關系)添加到目錄文件。文件名和inode之間的對應關系將文件名和文件的內(nèi)容及屬性連接起來。


插寫一個文件系統(tǒng)操作:

使用dd命令創(chuàng)建大文件并將其寫入文件系統(tǒng),然后將文件系統(tǒng)掛載到系統(tǒng)中,可以按照以下步驟進行:

  1. 創(chuàng)建目錄 /path/to

    mkdir -p /path/to
    
  2. 使用 dd 命令創(chuàng)建大文件

    以下命令創(chuàng)建一個大小為1GB的空文件:

    dd if=/dev/zero of=/path/to/largefile bs=1M count=1024
    

    這將在指定路徑創(chuàng)建名為 largefile 的大小為1GB的空文件。

  3. 使用 mkfs 創(chuàng)建文件系統(tǒng)

    在上面創(chuàng)建的文件中,可以使用適當?shù)奈募到y(tǒng)類型(例如ext4)來創(chuàng)建文件系統(tǒng)。假設要創(chuàng)建ext4文件系統(tǒng):

    mkfs.ext4 /path/to/largefile
    

    這將在 /path/to/largefile 中創(chuàng)建一個ext4文件系統(tǒng)。

  4. 掛載文件系統(tǒng)

    創(chuàng)建文件系統(tǒng)后,您可以將其掛載到系統(tǒng)中的某個目錄。首先,創(chuàng)建一個用于掛載的目錄:

    mkdir /mnt/largefile
    

    然后將文件系統(tǒng)掛載到此目錄:

    mount -o loop /path/to/largefile /mnt/largefile
    

    這將將 /path/to/largefile 中的文件系統(tǒng)掛載到 /mnt/largefile 目錄中。


5.2、硬鏈接(Hard Link)

  • 硬鏈接是指在文件系統(tǒng)中,多個文件可以指向同一個inode,這樣多個文件實際上指向了相同的數(shù)據(jù)塊。
  • 硬鏈接是文件系統(tǒng)級別的鏈接,它們只是指向了相同的inode,不保留路徑信息,因此即使原文件移動或重命名,硬鏈接仍然有效。
  • 刪除原文件并不會影響硬鏈接的可用性,只有當所有鏈接都被刪除后,文件的inode和數(shù)據(jù)塊才會被釋放。

使用硬鏈接:ln 原文件名 硬鏈接文件名,如下

ln file.txt file.hard

我們前面有講到,文件的一行屬性信息,有一個是硬鏈接數(shù),我們能看到,一開始沒給file.txt建立硬鏈接的時候,它的硬鏈接數(shù)1,就是只有自己,當給它建立硬鏈接后,硬鏈接的文件和原文件的硬鏈接數(shù)都變?yōu)?。

因此我們得出結(jié)論:普通文件的硬鏈接數(shù)等于該文件的建立的硬鏈接文件個數(shù)+1(可以自行再給file.txt文件再建立一個硬鏈接文件,看是不是硬鏈接數(shù)都變?yōu)?)。

我們再看目錄的硬鏈接數(shù):

我們看到,新創(chuàng)建的目錄就有2個硬鏈接數(shù)!為什么是兩個?我們看圖可以知道,一個是自己的目錄文件的,還有一個是該目錄下的.目錄的,我們知道目錄.就是指當前目錄。我們再來測試,在該目錄下再創(chuàng)建5個目錄,再看看硬鏈接數(shù)是多少?

能看到硬鏈接數(shù)變?yōu)?了!為什么呢?我們發(fā)現(xiàn)其中2個是自己和.目錄,還有5個是該目錄下的目錄(a、b、c、d、e)下的..目錄,顯而易見..目錄就是上級目錄。因此我們得出結(jié)論:目錄文件的硬鏈接數(shù)等于2+該目錄下的目錄個數(shù)。

注意:用戶不能自己給目錄建立硬鏈接,只能由系統(tǒng)創(chuàng)建!因為自己創(chuàng)建目錄的硬鏈接會導致路徑回路。

假如當前目錄是/user/home,你給user目錄創(chuàng)建一個硬鏈接/,哪當前創(chuàng)建的硬鏈接目錄變?yōu)榱?code>/user/home/user,這就導致了路徑回路。


5.3、軟鏈接(Symbolic Link)

軟鏈接也叫符號鏈接。

  • 軟鏈接是一個特殊的文件,它包含了指向另一個文件的路徑,相當于一個快捷方式
  • 軟鏈接可以跨越文件系統(tǒng),因為它們保存的是路徑信息而不是inode號。
  • 軟鏈接不像硬鏈接那樣指向相同的數(shù)據(jù)塊,而是指向目標文件的路徑,因此即使原文件移動或重命名,軟鏈接也可能失效。
  • 刪除原文件后,軟鏈接將成為死鏈接(即指向一個不存在的目標),如果不再需要,可以手動刪除。

使用軟鏈接:ln -s 原文件 軟鏈接文件。如:

ln -s file.txt file.symb


6、動態(tài)庫和靜態(tài)庫

下面講解動靜態(tài)庫可能會用到的文件

  • mystdio.h文件

    #include<stdio.h>#define NONE_FLUSH (1<<1)
    #define LINE_FLUSH (1<<2)
    #define FULL_FLUSH (1<<3)
    #define SIZE 1024typedef struct _myFILE{int fileno;int pos;// 當前寫入位置int cap;// 文件容量int flush_mode; // 刷新模式char buff[SIZE];
    }myFILE;myFILE* my_fopen(const char *path, const char *mode);int my_fwrite(const char *ptr, int size,myFILE *stream);//int my_fread(void *ptr, int size, myFILE *stream);void my_fclose(myFILE* stream);const char *toString(int flag);void DebugPrint(myFILE *fp);
    
  • mystdio.c文件

    #include "mystdio.h"
    #include <fcntl.h>
    #include <string.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <stdlib.h>
    #include <unistd.h>myFILE* my_fopen(const char *path, const char *mode){int flag = 0;if(strcmp(mode,"r") == 0){flag |= O_RDONLY;}else if(strcmp(mode,"w") == 0){flag |= (O_WRONLY|O_CREAT|O_TRUNC);}else if(strcmp(mode,"a") == 0){flag |= (O_WRONLY|O_CREAT|O_APPEND);}else{return NULL;}int fd = 0;if(flag & O_WRONLY){umask(0);fd = open(path,flag,0666);}else{fd = open(path,flag);}if(fd < 0) return NULL;myFILE* fp = (myFILE*)malloc(sizeof(myFILE));if(!fp){perror("malloc");return NULL;}fp->fileno = fd;fp->pos = 0;fp->cap = SIZE;fp->flush_mode = LINE_FLUSH;return fp;
    }void my_fflush(myFILE* stream){if(stream->pos == 0) return;write(stream->fileno,stream->buff,stream->pos);stream->pos = 0;// 刷新后pos到最初位置
    }int my_fwrite(const char *ptr, int size,myFILE *stream){memcpy(stream->buff+stream->pos,ptr,size);// buff從pos開始 stream->pos += size;if((stream->flush_mode & LINE_FLUSH) && (stream->buff[stream->pos-1] == '\n')){my_fflush(stream);}else if((stream->flush_mode & FULL_FLUSH) && (stream->pos == stream->cap)){my_fflush(stream);}return size;
    }void my_fclose(myFILE* stream){my_fflush(stream);close(stream->fileno);free(stream);
    }const char *toString(int flag)
    {if(flag & NONE_FLUSH) return "None";else if(flag & LINE_FLUSH) return "Line";else if(flag & FULL_FLUSH) return "FULL";return "Unknow";
    }void DebugPrint(myFILE *fp)
    {printf("outbufer: %s\n", fp->buff);printf("fd: %d\n", fp->fileno);printf("pos: %d\n", fp->pos);printf("cap: %d\n", fp->cap);printf("flush_mode: %s\n", toString(fp->flush_mode));
    }
    
  • cal.h文件

    #pragma once                                                                       int Add(int a, int b);
    
  • cal.c文件

    #include "cal.h"    int Add(int a ,int b){    return a + b;                                                                  
    }
    
  • main.c文件

    #include <stdio.h>    
    #include "cal.h"    
    #include "mystdio.h"    int main(){    int res = Add(10,20);    printf("%d+%d=%d\n",10,20,res);    myFILE* fp = my_fopen("log1.txt","w");    if(fp) return 1;                                                               return 0;    
    }
    

6.1、靜態(tài)庫

靜態(tài)庫(.a文件):程序編譯的時候把哭的代碼鏈接到可執(zhí)行文件中,程序運行時不再需要靜態(tài)庫。

  • 生成靜態(tài)庫:將.o文件打包生成靜態(tài)庫

  • 使用歸檔工具:ar -rc 靜態(tài)庫名 .o文件

  • ar是gnu歸檔工具,rc表示(replace and create)

  • 這里的.o文件是用命令gcc -o 生成的(和動態(tài)庫不一樣,下面動態(tài)庫會講)

  • 需要注意的是:靜態(tài)庫名是有規(guī)范的,不能寫成XXX.a,必須寫成libXXX.a

  • 如:ar -rc libmylib.a *.o

  • 查看靜態(tài)庫列表ar -tv 靜態(tài)庫名

  • -t:列出靜態(tài)庫中的文件

  • -v:verbose 詳細信息

  • 如:ar -tv libmylib.a

  • 使用靜態(tài)庫

  • 先將main.c生成main.o文件

    • 這里我們使用一個新命令 gcc -c .c文件,可以生成同名.o文件。如gcc -c main.c。
  • 再使用命令:gcc - o 可執(zhí)行文件 帶main函數(shù)的目標文件(如main.o) -L 靜態(tài)庫路徑 -l靜態(tài)庫名(去除前綴lib和后綴.a,如mylib) -static

  • gcc -o myexe main.c -L. -lmylib -static

  • 解釋一下選項:

    • -L:指定靜態(tài)庫路徑(當前路徑可以使用.,也可以使用絕對路徑)
    • -l:指定靜態(tài)庫
    • -static:指定是靜態(tài)鏈接


6.2、動態(tài)庫

動態(tài)庫相對靜態(tài)庫的使用更復雜,原理也更復雜。

動態(tài)庫(.so文件):程序在運行的時候才去鏈接動態(tài)庫的代碼,多個程序共享使用庫的代碼。

一個與動態(tài)庫鏈接的可執(zhí)行文件僅僅包含它用到的函數(shù)入口地址的一個表,而不是外部函數(shù)所在目標文件的整個機器碼(不像靜態(tài)庫那樣直接在代碼里面展開)。

在可執(zhí)行文件開始運行以前,外部函數(shù)的機器碼由操作系統(tǒng)從磁盤上的該動態(tài)庫中復制到內(nèi)存中,這個過程稱為動態(tài)鏈接(dynamic linking)

動態(tài)庫可以在多個程序間共享,所以動態(tài)鏈接使得可執(zhí)行文件更小,節(jié)省了磁盤空間。操作系統(tǒng)采用虛擬內(nèi)存機制允許物理內(nèi)存中的一份動態(tài)庫被要用到該庫的所有進程共用,節(jié)省了內(nèi)存和磁盤空間。

  • 生成動態(tài)庫:動態(tài)庫的生成也需要目標文件,但是動態(tài)庫的目標文件和靜態(tài)庫的目標文件的生成方式不一樣,動態(tài)庫目標文件必須使用選項fPIC(產(chǎn)生位置無關碼)。

    • 先將.c文件生成.o文件:gcc -c -fPIC *.c,將.c文件生成同名.o文件

    • 再使用選項shared.o文件打包成動態(tài)庫:gcc -shared -o 動態(tài)庫名 .o文件

    • gcc -shared -o libmylib.so *.o

    • 需要注意的是:這個動態(tài)庫名也有規(guī)則:不能是XXX.so,必須是libXXX.so

  • 使用動態(tài)庫

    • 先用main.c文件生成main.o文件

    • 再使用命令gcc -o 可執(zhí)行文件 帶main函數(shù)的目標文件 -L動態(tài)庫路徑 -l(動態(tài)庫名去掉前綴lib,去掉后綴.so)

    • gcc -o myexe main.o -L. -lmylib

  • 現(xiàn)在有一個問題:如果動態(tài)庫不在當前目錄下呢?

    我們來測試一下,將動態(tài)庫和頭文件放到一個目錄下

    假設我們得到了一個壓縮文件,文件解壓后得到一個目錄文件,目錄文件里面包含所需所有的頭文件和靜態(tài)庫

    這時候我們怎么運行這個main函數(shù)呢?

    這里我們又會使用到一個選項I,這個選項是用來指明頭文件的路徑。

    現(xiàn)在出現(xiàn)一個新的問題!直接運行myexe會報錯!

    報錯的原因是找不到動態(tài)庫libmyib.so。我們來看看該可執(zhí)行文件所依賴的動態(tài)庫,使用命令:ldd 可執(zhí)行文件名。ldd即list dynamic dependence。

    ldd myexe

    也就是沒找到該動態(tài)庫。有下面四種解決辦法:

    1、將該文件所需的動態(tài)庫放到系統(tǒng)里的動態(tài)庫中(不建議使用,風險高)。

    如:sudo cp mylib/lib/* /lib64

    2、使用環(huán)境變量

    使用命令:export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:動態(tài)庫文件路徑。這樣導入環(huán)境變量的話,重啟shell又需要重新配置。

    如:export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/xp2/Linux_Primary/day_32/demo_02/dynamiclib/user/mylib/lib

    還可以去系統(tǒng)文件添加環(huán)境變量

    在這兩個文件中:.bash_profile.bashrc文件。查找到這兩個文件的方式就是先cd到用戶路徑cd ~,再ls -al查看這個路徑下所有文件就可以看到這兩個文件。

    3、使用軟鏈接的方式(推薦,簡單好用)

    在lib64目錄下新建一個同動態(tài)庫名的動態(tài)庫文件的軟鏈接。

    如:sudo ln -s /home/xp2/Linux_Primary/day_32/demo_02/dynamiclib/user/mylib/lib/libmylib.so /lib64/libmylib.so

    4、在系統(tǒng)目錄下(/etc/ld.so.conf.d/)新建一個(文件名.conf)配置文件(推薦)


那么好,Linux基礎IO就到這里,如果你對Linux和C++也感興趣的話,可以看看我的主頁哦。下面是我的github主頁,里面記錄了我的學習代碼和leetcode的一些題的題解,有興趣的可以看看。

Xpccccc的github主頁

http://aloenet.com.cn/news/40059.html

相關文章:

  • 外國人學做中國菜的網(wǎng)站營銷網(wǎng)站做的好的公司
  • 網(wǎng)上做家教兼職哪個網(wǎng)站南昌百度快速排名提升
  • 網(wǎng)站開發(fā)經(jīng)常遇到的問題大一html網(wǎng)頁制作作業(yè)簡單
  • 南京模板建網(wǎng)站哪家好百度一下官網(wǎng)
  • 聊大 網(wǎng)站設計seo優(yōu)化主要做什么
  • 太倉建設銀行網(wǎng)站搜索指數(shù)查詢
  • 劉琪 找誰做網(wǎng)站靠譜東莞網(wǎng)絡營銷全網(wǎng)推廣
  • 做網(wǎng)站怎么做鼠標跟隨2023很有可能再次封城嗎
  • 網(wǎng)站經(jīng)營與建設優(yōu)化大師軟件下載
  • 網(wǎng)上有做口譯的網(wǎng)站么官方進一步優(yōu)化
  • 彩票代購網(wǎng)站建設百度怎么注冊自己的店鋪
  • 做電影平臺網(wǎng)站怎么賺錢的什么是網(wǎng)絡營銷平臺
  • wordpress主頁居中重慶seo俱樂部
  • 焦作專業(yè)做網(wǎng)站公司中國新聞發(fā)布
  • wordpress 個性化韶山百度seo
  • 桐鄉(xiāng)網(wǎng)站建設世界十大網(wǎng)站排名出爐
  • 高明專業(yè)網(wǎng)站建設報價青海百度關鍵詞seo
  • 網(wǎng)站制作需要平臺培訓心得體會總結(jié)
  • 網(wǎng)站建設專企業(yè)站seo價格
  • 網(wǎng)站建設種類 優(yōu)幫云海外網(wǎng)站推廣優(yōu)化專員
  • 可信賴的南昌網(wǎng)站制作seo是付費還是免費推廣
  • 商城網(wǎng)站合同網(wǎng)站的優(yōu)化
  • wordpress 側(cè)邊懸浮塊鄭州谷歌優(yōu)化外包
  • web網(wǎng)站開發(fā)全過程網(wǎng)站內(nèi)鏈優(yōu)化
  • 做網(wǎng)站的流程方法上海排名優(yōu)化推廣工具
  • 網(wǎng)站的設計與維護摘要搜易網(wǎng)優(yōu)化的效果如何
  • 國家和省對政府網(wǎng)站建設要求做網(wǎng)絡推廣要學些什么
  • 關于門戶網(wǎng)站建設通報google chrome谷歌瀏覽器
  • 成都it培訓機構(gòu)優(yōu)化網(wǎng)絡搜索引擎
  • 門戶網(wǎng)站快速制作佛山網(wǎng)站建設維護