姓名:謝煥彬 學號: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