CSAPP–第十章–系统级IO(中)

Aki 发布于 2023-01-20 310 次阅读


读取文件元数据、

应用程序能够通过调用 stat 和 fstat 函数,检索到关于文件的信息(有时也称为文件的元数据(metadata))。

函数说明 fstat()用来将参数fd所指的文件状态,复制到参数buf所指的结构中(struct stat)。fstat()与stat()作用完全相同,不同处在于传入的参数为已打开的文件描述词。两个函数成功返回0,失败返回-1,并且设置error的值

//参数不同,一个是文件路径,一个是以打开的文件描述符
int stat(const char *path, struct stat *buf);
int fstat(int fd,struct stat *buf);

typedef struct stat 
{

    dev_t         st_dev;       //文件的设备编号

    ino_t         st_ino;       //节点

    mode_t        st_mode;      //文件的类型和存取的权限

    nlink_t       st_nlink;     //连到该文件的硬连接数目,刚建立的文件值为1

    uid_t         st_uid;       //用户ID

    gid_t         st_gid;       //组ID

    dev_t         st_rdev;      //(设备类型)若此文件为设备文件,则为其设备编号

    off_t         st_size;      //文件字节数(文件大小)

    unsigned long st_blksize;   //块大小(文件系统的I/O 缓冲区大小)

    unsigned long st_blocks;    //块数

    time_t        st_atime;     //最后一次访问时间

    time_t        st_mtime;     //指最近修改文件内容的时间

    time_t        st_ctime;     //指最近改动Inoed的时间

}stat;
#include<iostream>
using namespace std;
#include<mutex>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<cstring>
#include<dirent.h>


//安全的输出函数
mutex print_mutex{};
template<class...Args>
void print(const Args&...args)
{
	lock_guard<mutex> m(print_mutex);
	((cout << args),...);
}



//输出错误函数
void unix_error(const char* msg)
{
	print(msg);
	exit(0);
}


//安全考虑的open函数,第一个参数是文件路径,第二个参数是打开的模式
int Open(const char* path,int flag)
{
	int fd = 0;
        //打开文件失败会返回-1
	if((fd = open(path,flag)) < 0)
	{
		unix_error("open file error\n");
	}
	return fd;
}



//安全考虑的close函数,参数是文件描述符
void Close(int fd)
{
	int res = 0;
	if((res = close(fd)) < 0)
	{
		unix_error("close file error\n");
	}
}




//安全考虑的read函数
void Read(int fd,char*buf,size_t len)
{
	size_t left = len;
	size_t reads = 0;
	char* tmp = buf;

	while(left > 0)
	{
		//error
		if((reads = read(fd,buf,left)) < 0)
		{
			unix_error("read file error\n");
		}
		//EOF
		else if(reads == 0)
		{
			break;
		}
		left -= reads;
		tmp += reads;
	}
}


//安全考虑的write函数
void Write(int fd,char*buf,size_t len)
{
	int n = 0;
	if((n = write(fd,buf,len)) < 0)
	{
		unix_error("write file error\n");
	}
}

//读取文件元数据
void read_file_data(const char* path)
{
	struct stat status;
	if(stat(path,&status) < 0)
	{
		unix_error("read file data error\n");
	}
        print("filename : ",path,"\n");
        //S_ISDIR是一个宏函数,判断st_mode是不是目录,是返回1,不是返回0
	print("type : ",(S_ISDIR(status.st_mode) == 1 ? "dir" : "regular file"),"\n");
	print("size : ",status.st_size,"\n");
	print("user : ",status.st_uid,"\n");
}


int main(int args,char* argv[],char* envp[])
{

	if(argv[1])
	{
		read_file_data(argv[1]);
	}

	return 0;
}


st_mode 详细内容文章 Linux:st_mode详细分析_Echo_Xiaozhi的博客

打开,读取目录的内容、

opendir()用来打开参数指定的目录, 并返回DIR*形态的目录流, 和open()类似, 接下来对目录的读取和搜索都要使用此返回值。 成功则返回DIR* 型态的目录流, 打开失败则返回NULL

DIR类似于文件描述符,是一个描述目录的东西,不太重要,故不去深入研究。

DIR * opendir(const char * name);

应用程序可以用 readdir 系列函数来读取目录的内容。成功返回dirent的指针,失败返回NULL。

readdir每一次只会按顺序读取目录下的一个文件,需要在循环内才能够读取到目录下的所有文件!!!

struct dirent* readdir(DIR* dir_handle);

struct dirent
{
    ino_t d_ino;    //d_ino 此目录进入点的inode
    ff_t d_off;     //d_off 目录文件开头至此目录进入点的位移
    signed short int d_reclen;  //d_reclen _name 的长度, 不包含NULL 字符
    unsigned char d_type;       //d_type d_name  所指的文件类型 
    har d_name[256];            //d_name 文件名
};

d_type 的类型如下:
DT_REG   常规文件
DT_DIR   目录
DT_SOCK  套接字
DT_LNK   符号链接
DT_CHR   字符设备
DT_FIFO  一个命名管道,或FIFO。

 函数 closedir 关闭流并释放其所有的资源,成功返回0,失败返回-1。

closedir(DIR* dir);

chdir() 是linux中的一个系统调用函数(同cd),用于改变当前工作目录,其参数为Path 目标目录,可以是绝对目录或相对目录。成功返回0,失败返回-1。

int chdir(const char *path);

下面的程序打印一个目录下所有的文件!!!

#include<iostream>
using namespace std;
#include<mutex>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<cstring>
#include<dirent.h>


//安全的输出函数
mutex print_mutex{};
template<class...Args>
void print(const Args&...args)
{
	lock_guard<mutex> m(print_mutex);
	((cout << args),...);
}



//输出错误函数
void unix_error(const char* msg)
{
	print(msg);
	exit(0);
}




//打印path下的所有文件名称,递归实现
void print_dir(const char* path)
{
        //首先打开path目录
	DIR* dir = opendir(path);
	if(dir == nullptr)       //打开失败就返回,这也是递归出口
	{
		return;
	}
        
        //保存目录下文件信息所需要的结构体指针
	struct dirent*status = nullptr;

        //改变工作目录,很重要!!!
	if(chdir(path) < 0)
        {
              unix_error("change dir error\n");
        }

        //循环读取目录下的每一个文件
	while(status = readdir(dir))
	{
                //读取目录下文件时会读取到 . 和 .. 这两个需要忽略掉
		if(strcmp(status->d_name,".") != 0 && strcmp(status->d_name,"..") != 0)
		{
                        //分类讨论读取到的文件类型
			switch(status->d_type)
			{
                                //目录的情况
				case DT_DIR:
					print("dir : ",status->d_name,"\n");
					print_dir(status->d_name);
                                        //改变工作目录,很重要!!!
					if(chdir(path) < 0)
                                        {      
                                              unix_error("change dir error\n");
                                        }
					break;
                                //普通文件的情况
				case DT_REG:
					print("file : ",status->d_name,"\n");
					break;
				default:
					break;
		
		}

	}

	if(closedir(dir) < 0)
	{
		unix_error("close dir error\n");
	}
}


int main()
{

	char path[30];
	cin >> path;
	print_dir(path);

	return 0;
}