文件IO 第三天 (靜態(tài)庫&動態(tài)庫)

姓名:謝煥彬 學號:19020100303
一、獲取文件屬性(選學)
我們可以使用stat()/fstat()/lstat()函數(shù)來獲取某個文件的屬性信息剖煌。

注意:stat既是Linux系統(tǒng)的用于查看文件屬性的指令,又是在編程過程中可以使用的一個獲取文件屬性信息的函數(shù)。

其中stat()函數(shù)可以根據(jù)文件名(可帶路徑)獲取文件的屬性信息玉工;fstat()函數(shù)可以根據(jù)已打開文件的文件描述符獲得該文件的屬性信息缚俏;lstat()函數(shù)用法類似于stat(),不過若該文件是一個符號鏈接文件則會返回該符號鏈接的信息而不是該符號鏈接的引用文件的信息怠益。

函數(shù)stat()/fstat()/lstat()

需要頭文件:#include<sys/stat.h>

                    #include<sys/types.h>

                    #include<unistd.h>

函數(shù)原型:int stat(const char *pathname,struct stat *buf);

                 int fstat(int fd,struct stat *buf);

                 int lstat(const char *pathname,struct stat *buf);

函數(shù)參數(shù):pathname:打開文件名(可以包含具體路徑名)

                 fd:已打開文件的文件描述符

                 buf:struct stat類型的結構體指針仪搔,用于存放文件信息

函數(shù)返回值:成功:0

                    失敗:-1

注意:必須定義struct stat類型結構體然后使用地址傳遞的方式傳遞參數(shù)蜻牢,不允許直接定義struct stat*類型指針直接傳參

struct stat結構體成員:

struct stat

{

dev_t     st_dev;     /* ID of device containing file - 文件所在的設備ID*/

ino_t     st_ino;     /* inode number - inode節(jié)點號*/

mode_t    st_mode;    /* protection - 文件的模式(文件烤咧、目錄等)*/

nlink_t   st_nlink;   /* number of hard links - 鏈接至此文件的鏈接數(shù)(硬鏈接)*/

uid_t     st_uid;     /* user ID of owner - 文件所有者ID*/

gid_t     st_gid;     /* group ID of owner - 文件組ID*/

dev_t     st_rdev;    /* device ID (if special file) - 設備號(針對某些特殊設備文件)*/

off_t     st_size;    /* total size, in bytes - 文件大小(單位字節(jié))*/

blksize_t st_blksize; /* blocksize for file system I/O - 系統(tǒng)塊大小*/

blkcnt_t  st_blocks;  /* number of 512B blocks allocated - 文件所占的塊數(shù)*/

time_t    st_atime;   /* time of last access - 最近被訪問時間(例如使用read()讀取數(shù)據(jù))*/

time_t    st_mtime;   /* time of last modification - 最近被修改時間(例如使用write()寫入數(shù)據(jù))*/

time_t    st_ctime;   /* time of last status change - 文件狀態(tài)改變時間*/

};
其中文件的類型存放在結構體成員st_mode內(nèi)

/*****單詞翻譯*****/

regular file:普通文件

directory file:目錄文件

character special file:字符特殊設備文件

block special file:塊設備特殊文件

FIFO:進程間通信(又名管道)

socket:套接字

symbolic link:符號鏈接抢呆,指向另一個文件

/*****單詞翻譯end**/

結構體成員st_mode內(nèi)存取的宏定義以及相關含義(部分煮嫌,其他相關宏定義請查看幫助手冊):

S_IFREG(m)  is it a regular file?

S_IFDIR(m)  directory?

S_IFCHR(m)  character device?

S_IFBLK(m)  block device?

S_IFIFO(m)     FIFO (named pipe)?

S_IFLNK(m)  symbolic link? (Not in POSIX.1-1996.)

S_IFSOCK(m) socket? (Not in POSIX.1-1996.)

示例:編程實現(xiàn)stat指令的類似功能,即對指定路徑的文件獲取其相關文件信息

include <sys/types.h>

include <sys/stat.h>

include <time.h>

include <stdio.h>

include <stdlib.h>

int main(int argc, char *argv[])

{

struct stat sb;



if (argc != 2)

{

    perror("Too few Arguments. Usage: <command> <pathname>\n");

    exit(0);

}

if (stat(argv[1], &sb) == -1)

{

    perror("stat");

    exit(0);

}

printf("File type:");

switch (sb.st_mode & S_IFMT)//S_IFMT用于判定該文件的類型

{

    case S_IFBLK:  printf("block device\n");            break;

    case S_IFCHR:  printf("character device\n");        break;

    case S_IFDIR:  printf("directory\n");               break;

    case S_IFIFO:  printf("FIFO/pipe\n");               break;

    case S_IFLNK:  printf("symlink\n");                 break;

    case S_IFREG:  printf("regular file\n");            break;

    case S_IFSOCK: printf("socket\n");                  break;

    default:       printf("unknown?\n");                break;

}



printf("I-node number:%ld\n", (long) sb.st_ino);

printf("Mode:%lo (octal)\n",(unsigned long) sb.st_mode);

printf("Link count:%ld\n", (long) sb.st_nlink);

printf("Ownership:UID=%ld   GID=%ld\n",(long) sb.st_uid, (long) sb.st_gid);

printf("Preferred I/O block size: %ld bytes\n",(long) sb.st_blksize);

printf("File size:%lld bytes\n",(long long) sb.st_size);

printf("Blocks allocated:%lld\n",(long long) sb.st_blocks);

printf("Last status change:%s", ctime(&sb.st_ctime));

printf("Last file access:%s", ctime(&sb.st_atime));

printf("Last file modification:%s", ctime(&sb.st_mtime));

return 0;

}
可以先使用stat指令查看該文件镀娶,再運行這個程序查看該文件并做對比

二立膛、操作目錄相關函數(shù)(選學)
我們可以使用opendir()/readdir()函數(shù)來打開某目錄并獲取目錄內(nèi)文件的信息。在這里opendir()函數(shù)相當于fopen()函數(shù)梯码,readdir()函數(shù)相當于fread()函數(shù)宝泵,只不過操作的流的結構體類型不同。

函數(shù)opendir()

需要頭文件:#include<sys/types.h>

                    #include<dirent.h>

函數(shù)原型:DIR *opendir(const char *name);

函數(shù)參數(shù):name:打開目錄名(可以包含具體路徑名)

函數(shù)返回值:成功:操作該目錄的流的指針(DIR*)

                    失斝ⅰ:NULL

該函數(shù)的返回值是操作目錄的流DIR的指針儿奶,其中操作目錄的流DIR的結構體成員如下:

struct __dirstream

{

void *__fd; /* struct hurd_fd' pointer for descriptor. */

char *__data; /* Directory block. */

int __entry_data; /* Entry number `__data' corresponds to. */

char *__ptr; /* Current pointer into the block. */

int __entry_ptr; /* Entry number `__ptr' corresponds to. */

size_t __allocation;/* Space allocated for the block. */

size_t __size; /* Total valid data in the block. */

__libc_lock_define (, __lock) /* Mutex lock for this structure. */

};

typedef struct __dirstream DIR;
函數(shù)readdir()

需要頭文件:#include<dirent.h>

函數(shù)原型:struct dirent *readdir(DIR *dirp);

函數(shù)參數(shù):dirp:操作目錄的流的指針

函數(shù)返回值:成功:struct dirent*類型的指針

                    失敗:NULL

該函數(shù)的返回值是結構體struct dirent的結構體指針鳄抒,結構體成員如下: 

struct dirent

{

    long d_ino; /* inode number 索引節(jié)點號 */

    off_t d_off; /* offset to this dirent 在目錄文件中的偏移 */

    unsigned short d_reclen;/* length of this d_name 文件名長 */

    unsigned char d_type; /* the type of d_name 文件類型 */

    char d_name [NAME_MAX+1];/* file name (null-terminated) 文件名 */

}

示例:編程實現(xiàn)模擬Linux的ls指令的程序闯捎。

思路:使用opendir()函數(shù)打開一個目錄,然后使用readdir()函數(shù)獲取這個目錄內(nèi)的文件的相關信息并依次輸出(這里只輸出d_name即可)

include<unistd.h>

include<sys/types.h>

include<fcntl.h>

include<dirent.h>

include<stdio.h>

ifndef NULL

define NULL 0

endif

ifndef ERROR

define ERROR 0

define OK 1

endif

typedef int Status;

Status ls(char *dirname)

{

DIR *p_dir;

struct dirent *p_dirent;

if((p_dir=opendir(dirname))==NULL)

{

    fprintf(stderr,"---->can\'t open %s\n",dirname);

    return ERROR;

}

while((p_dirent=readdir(p_dir)))

{

    printf("%s\n",p_dirent->d_name);

}

closedir(p_dir);

return OK;

}

int main(int argc,char **argv)

{

if(argc==1)          //如果沒給具體目錄默認為.即當前目錄

    ls(".");



while(--argc)        //若攜帶了多個目錄名則需要將每個目錄內(nèi)的文件都輸出

{

    printf("%s\n",*++argv);

    ls(*argv);

}

}
三许溅、靜態(tài)庫與動態(tài)庫的分析
1瓤鼻、什么是庫?
庫(library)是一種可執(zhí)行代碼的二進制形式贤重,通常把一些常用的函數(shù)制作成各種函數(shù)庫茬祷,然后被系統(tǒng)載入內(nèi)存中運行。庫內(nèi)一般都是各種標準程序并蝗、子程序祭犯、相關文件以及目錄等的集合秸妥,內(nèi)置一些經(jīng)常用的程序。主要有:

1)標準子程序:例如三角函數(shù)沃粗、反三角函數(shù)等

2)標準程序:例如解常微分方程等

3)服務性程序:例如輸入粥惧、輸出、磁盤操作最盅、調試等突雪。

由于windows與linux系統(tǒng)不同,因此二者的二進制庫是不兼容的涡贱。

Linux系統(tǒng)下的庫分為靜態(tài)庫與動態(tài)庫兩種挂签。二者的不同點是在載入時間的不同。

靜態(tài)庫在程序編譯時的鏈接階段被鏈接到目標代碼中盼产,運行程序時將不再需要靜態(tài)庫。編譯后的可執(zhí)行程序體積較大勺馆。

動態(tài)庫在程序編譯時并不會馬上鏈接到目標代碼中戏售,而是在執(zhí)行階段才被程序載入,因此編譯后的可執(zhí)行程序體積較小草穆,但是需要系統(tǒng)動態(tài)庫存在灌灾。

庫是前輩高手寫好的成熟的可以直接復用的代碼,只需遵守使用協(xié)議即可悲柱。不可能所有人的編程學習都是從零開始锋喜,庫的存在使得程序開發(fā)變得更加簡易。而且如果不同的應用程序調用同樣的庫豌鸡,那么內(nèi)存內(nèi)只需有一份該庫的實例即可嘿般,節(jié)省了存儲空間。

2涯冠、制作一個靜態(tài)庫
我們可以使用GNU下的ar工具來制作一個靜態(tài)庫

ar是類似gcc的一個GNU工具包內(nèi)的工具炉奴,作用是建立、修改蛇更、提取歸檔文件瞻赶。歸檔文件是包含多個文件內(nèi)容的一個大文件,被包含文件的原始內(nèi)容派任、權限砸逊、時間戳、所有者等屬性都保存于歸檔文件中掌逛,并且可以通過“提取”來還原該文件师逸。

示例:自己制作一個靜態(tài)庫,庫函數(shù)的功能是傳遞一個字符串并輸出颤诀。

第一步:需要準備3個文件:hello.h字旭、hello.c对湃、test.c。其中hello.h和hello.c用于制作靜態(tài)庫遗淳,test.c是測試程序主函數(shù)

//文件hello.h

ifndef HELLO_H

define HELLO_H

include<stdio.h>

void hello(const char *name);

endif

//文件hello.c

include"hello.h"

void hello(const char *name)

{

printf("Hello %s, You are handsome!\n",name);

}

//文件test.c

include"hello.h"

int main()

{

hello("Li");//調用自己制作的庫

return 0;

}
第二步:將hello.c編譯生成目標文件hello.o

gcc hello.c -c -o hello.o

第三步:使用ar將hello.o制作成靜態(tài)庫

ar crs libmyhello.a hello.o

ar參數(shù)解析:

1.c:表示無提示方式創(chuàng)建文件包

2.r:在文件包中替代文件

3.s:強制重新生成文件包的符號表

這樣就制作了一個名為libmyhello.a的文件包(即靜態(tài)庫)

第四步:編譯test.c拍柒,將剛制作的靜態(tài)庫加載至程序內(nèi)

gcc test.c -L. -lmyhello -o hello

gcc參數(shù)解析:

1.-L:表示增加目錄,讓編譯器可以在該目錄下尋找?guī)煳募怠:竺娴?. 表示當前目錄拆讯;

2.-l:表示加載libXXX.a/libXXX.so庫文件。



這樣我們就生成了一個hello的可執(zhí)行文件养叛。執(zhí)行該文件种呐,會輸出"Hello Li, You are handsome!",這樣我們就成功制作了一個庫函數(shù)hello弃甥。

練習:將libmyhello.a文件刪除爽室,再次執(zhí)行hello程序查看

若我們刪除庫(即libmyhello.a文件),再次執(zhí)行該程序仍然可以得到正確的結果淆攻。這是因為靜態(tài)庫在鏈接階段已經(jīng)和程序整合到一起阔墩,即使原始庫文件不存在,程序依然可以成功執(zhí)行瓶珊。

3啸箫、制作一個動態(tài)庫

我們可以使用gcc工具來制作一個動態(tài)庫

示例:自己制作一個動態(tài)庫,庫函數(shù)的功能是傳遞一個字符串并輸出伞芹。

第一步:需要準備3個文件:hello.h忘苛、hello.c、test.c唱较。其中hello.h和hello.c用于制作動態(tài)庫扎唾,test.c是測試程序主函數(shù)

//代碼同上,略

第二步:使用gcc編譯生成動態(tài)庫

gcc hello.c -fPIC -c -o hello.o

gcc hello.o -shared -o libmyhello.so

(或者直接一步:gcc hello.c -fPIC -shared -o libmyhello.so)

gcc參數(shù)解析:

1.-fPIC(或-fpic):表示編譯為位置獨立的代碼南缓。位置獨立的代碼即位置無關代碼稽屏,在可執(zhí)行程序加載的時候可以存放在內(nèi)存內(nèi)的任何位置。若不使用該選項則編譯后的代碼是位置相關的代碼西乖,在可執(zhí)行程序加載時是通過代碼拷貝的方式來滿足不同的進程的需要狐榔,沒有實現(xiàn)真正意義上的位置共享。

2.-shared:指定生成動態(tài)鏈接庫

此時我們就生成了動態(tài)庫libmyhello.so获雕。

若此時編譯文件時加載庫

gcc test.c -L. -lmyhello -o hello

運行文件hello時會發(fā)現(xiàn)報錯:

./hello: error while loading shared libraries: libmyhello.so: cannot open shared object file: No such file or directory

這是因為Linux系統(tǒng)還無法定位到我們自己制作的庫的位置薄腻,即我們暫時還無法使用該動態(tài)庫。

對于Linux系統(tǒng)而言届案,在可執(zhí)行程序加載動態(tài)庫的時候庵楷,不僅要知道該庫的名字,還需要知道其絕對路徑。

我們可以使用ldd指令查看某個可執(zhí)行程序加載庫的情況

ldd hello

linux-gate.so.1 =>  (0xb77b0000)

libmyhello.so => not found

libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb75f6000)

/lib/ld-linux.so.2 (0xb77b1000)

第三步:定位自己制作的動態(tài)庫

要想讓自己制作的動態(tài)庫生效尽纽,我們需要了解正常情況下系統(tǒng)是如何加載一個動態(tài)庫的咐蚯。以我們熟悉的stdio庫為例,系統(tǒng)在加載標準輸入輸出庫時遵循以下幾個步驟:

1.執(zhí)行./hello指令弄贿,終端解釋該指令春锋,終端指示應加載動態(tài)庫stdio,尋找存放動態(tài)庫的配置文件差凹。

2.存放動態(tài)庫的配置文件默認目錄為/etc/ld.so.conf.d/以及下屬的眾多子目錄內(nèi)的配置文件期奔。配置文件指示該庫的絕對路徑在/usr/lib或/lib下。

3.去往/usr/lib或/lib危尿,將存儲的stdio庫加載到程序hello中呐萌。

為了讓系統(tǒng)能成功找到自己制作的動態(tài)庫,需要定位到該動態(tài)庫的位置谊娇。參照正常加載動態(tài)庫的方式肺孤,我們可以有三種方式:

1)把自己制作的庫拷貝到/usr/lib和/lib下。

2)在LD_LIBRARY_PATH環(huán)境變量中添加自己制作的庫所在的位置济欢。

3)添加/etc/ld.so.conf.d/XXX.conf文件(XXX需要自己命名)渠旁,把庫所在的路徑添加到文件末尾并執(zhí)行l(wèi)dconfig刷新。

注意:練習這三種方法時盡量清除上一種方法的效果影響保證三種方法是獨立生效的船逮。

第一種:將庫拷貝到/usr/lib和/lib下。

sudo cp libmyhello.so /usr/lib

sudo cp libmyhello.so /lib

此時再執(zhí)行./hello即可得到正確的顯示結果粤铭。

第二種:修改LD_LIBRARY_PATH環(huán)境變量

sudo vim /etc/bash.bashrc

在文件最后挖胃,添加:

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/linux/file/dongtaiku

保存退出,重啟終端梆惯,此時再執(zhí)行./hello即可得到正確的顯示結果酱鸭。

第三種:添加/etc/ld.so.conf.d/XXX.conf文件

sudo vim /etc/ld.so.conf.d/my.conf

在文件內(nèi)添加動態(tài)庫的目錄

/home/linux/file/dongtaiku

保存退出,執(zhí)行l(wèi)dconfig使設置生效

sudo ldconfig

此時再執(zhí)行./hello即可得到正確的顯示結果垛吗。
————————————————
版權聲明:本文為CSDN博主「nan_lei」的原創(chuàng)文章凹髓,遵循CC 4.0 BY-SA版權協(xié)議,轉載請附上原文出處鏈接及本聲明怯屉。
原文鏈接:https://blog.csdn.net/nan_lei/article/details/81516030

?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末蔚舀,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子锨络,更是在濱河造成了極大的恐慌赌躺,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,865評論 6 518
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件羡儿,死亡現(xiàn)場離奇詭異礼患,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,296評論 3 399
  • 文/潘曉璐 我一進店門缅叠,熙熙樓的掌柜王于貴愁眉苦臉地迎上來悄泥,“玉大人,你說我怎么就攤上這事肤粱〉簦” “怎么了?”我有些...
    開封第一講書人閱讀 169,631評論 0 364
  • 文/不壞的土叔 我叫張陵狼犯,是天一觀的道長余寥。 經(jīng)常有香客問我,道長悯森,這世上最難降的妖魔是什么宋舷? 我笑而不...
    開封第一講書人閱讀 60,199評論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮瓢姻,結果婚禮上祝蝠,老公的妹妹穿的比我還像新娘。我一直安慰自己幻碱,他們只是感情好绎狭,可當我...
    茶點故事閱讀 69,196評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著褥傍,像睡著了一般儡嘶。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上恍风,一...
    開封第一講書人閱讀 52,793評論 1 314
  • 那天蹦狂,我揣著相機與錄音,去河邊找鬼朋贬。 笑死凯楔,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的锦募。 我是一名探鬼主播摆屯,決...
    沈念sama閱讀 41,221評論 3 423
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼糠亩!你這毒婦竟也來了虐骑?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 40,174評論 0 277
  • 序言:老撾萬榮一對情侶失蹤赎线,失蹤者是張志新(化名)和其女友劉穎富弦,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體氛驮,經(jīng)...
    沈念sama閱讀 46,699評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡腕柜,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,770評論 3 343
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片盏缤。...
    茶點故事閱讀 40,918評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡砰蠢,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出唉铜,到底是詐尸還是另有隱情台舱,我是刑警寧澤,帶...
    沈念sama閱讀 36,573評論 5 351
  • 正文 年R本政府宣布潭流,位于F島的核電站竞惋,受9級特大地震影響,放射性物質發(fā)生泄漏灰嫉。R本人自食惡果不足惜拆宛,卻給世界環(huán)境...
    茶點故事閱讀 42,255評論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望讼撒。 院中可真熱鬧浑厚,春花似錦、人聲如沸根盒。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,749評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽炎滞。三九已至敢艰,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間册赛,已是汗流浹背钠导。 一陣腳步聲響...
    開封第一講書人閱讀 33,862評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留击奶,地道東北人。 一個月前我還...
    沈念sama閱讀 49,364評論 3 379
  • 正文 我出身青樓责掏,卻偏偏與公主長得像柜砾,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子换衬,可洞房花燭夜當晚...
    茶點故事閱讀 45,926評論 2 361

推薦閱讀更多精彩內(nèi)容