linux系统提供的接口函数-系统IO(二)

linux系统提供的接口函数-系统IO(二)

1 stat结构体

在linux下面,有一个stat结构体是用来保存一个文件的状态或属性信息的。 我们在终端下输入man 2 stat查看这个结构体如下所示:

struct stat {
    dev_t     st_dev;         /* ID of device containing file */
    ino_t     st_ino;         /* Inode number */
    mode_t    st_mode;        /* File type and mode */
    nlink_t   st_nlink;       /* Number of hard links */
    uid_t     st_uid;         /* User ID of owner */
    gid_t     st_gid;         /* Group ID of owner */
    dev_t     st_rdev;        /* Device ID (if special file) */
    off_t     st_size;        /* Total size, in bytes */
    blksize_t st_blksize;     /* Block size for filesystem I/O */
    blkcnt_t  st_blocks;      /* Number of 512B blocks allocated */

    /* Since Linux 2.6, the kernel supports nanosecond
    precision for the following timestamp fields.
    For the details before Linux 2.6, see NOTES. */

    struct timespec st_atim;  /* Time of last access */
    struct timespec st_mtim;  /* Time of last modification */
    struct timespec st_ctim;  /* Time of last status change */

    #define st_atime st_atim.tv_sec      /* Backward compatibility */
    #define st_mtime st_mtim.tv_sec
    #define st_ctime st_ctim.tv_sec
    };

让我们再来看看这个结构体各成员的含义:

  • dev_t st_dev; 用来标识容纳该文件(就是你的那个设备的设备号)。 比如该文件可能会存在于某个硬盘或者某个U盘上 ,那么这个硬盘或U盘会有一个设备号,st_dev则负责保存这个设备号。
  • ino_t st_ino; 该文件的inode编号 ,文件系统中的每个文件都有一个唯一的inode号。
  • mode_t st_mode; 文件类型与权限。
  • nlink_t st_nlink; 该文件的硬链接数。
  • uid_t st_uid; 文件拥有者的id 。
  • gid_t st_gid; 文件拥有者的组id 。
  • dev_t st_rdev; 设备号 (如果该文件是设备则有)。
  • off_t st_size; 文件的大小,对于普通文件来说, 意味着就是文件内容的字节数。对于符号链接(软链接)其文件内容是指向的那个文件的文件名的字节数。
  • blksize_t st_blksize; 块大小(与具体的硬件设备有关 512字节为一块)。
  • blkcnt_t st_blocks; 该文件占多少块。
  • struct timespec st_atim; 最后访问“文件内容”的时间 。
  • struct timespec st_mtim; 最后修改“文件内容”的时间。
  • struct timespec st_ctim; 最后修改“文件属性”的时间。

软硬链接区别
在这里插入图片描述

Linux下对inode和块的理解
其中我们最常用的是这个结构体成员st_mode(文件类型与权限),那么怎么去获取文件的类型和权限呢? 宏,Linux提供了宏来解析该成员变量,比如:

    struct stat st; 
    /*
        S_ISREG(st.st_mode) 为真表示该文件是普通文件 -
        S_ISDIR(st.st_mode) 为真表示该文件是目录文件 d 
        S_ISCHR(st.st_mode) 为真表示该文件是字符设备 c 
        S_ISBLK(st.st_mode) 为真则表示该文件是块设备 b 
        S_ISFIFO(st.st_mode)为真则表示该文件是管道文件 p
        S_ISLNK(st.st_mode) 为真则表示该文件是符号链接文件 l 
        S_ISSOCK(st.st_mode)为真则表示该文件是套接字文件 s
    */上面的宏用来解析对应的文件的类型
    
    /*st.st_mode还包含了文件的权限位,可以通过如下代码来解析 
    if(st.st_mode & S_IRUSR)
    {
     		user有可读的权限 
    }
    */还有一些权限位也可以通过上述代码的方式来判断 
    //S_IWUSR S_IXUSR S_IRGRP S_IWGRP S_IXGRP 
    //S_IROTH S_IWOTH S_IXOTH

2 获取文件属性

NAME
​ stat, fstat, lstat - get file status

SYNOPSIS
​ #include <sys/types.h>
​ #include <sys/stat.h>
​ #include <unistd.h>

​ int stat(const char *pathname, struct stat *statbuf);
​ int fstat(int fd, struct stat *statbuf);
​ int lstat(const char *pathname, struct stat *statbuf);

int stat(const char *pathname, struct stat *statbuf);

  • 功能及参数:stat是用来获取pathname指定的那个文件的属性信息的, 获取的信息用statbuf指向的空间保存。注意当pathname是 一个链接文件的时候,stat是获取符号链接文件指向的那个文件的属性信息。
  • 返回值:
    成功返回0
    失败返回-1 并且errno被设置

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

  • 功能及参数:fstat也是用来获取文件的属性信息的 ,只不过 fstat需要一个文件描述符来执行一个文件,所以要先调用open函数打开 它返回一个文件描述符。
  • 返回值:
    成功返回0
    失败返回-1 并且errno被设置

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

  • 功能及参数:lstat的功能与stat是一样的 。只不过要注意当pathname是一个链接文件的时候, lstat获取的是符号链接文件本身的属 性信息。
  • 返回值:
    成功返回0
    失败返回-1 并且errno被设置

注意区分stat和lstat:

    1n -s 1.txt 2.txt //创建1.txt的一个符号(软)连接 2.txt
    stat(2.txt,statbuf) --> statbuf结构体保存的是1.txt的属性 
    lstat(2.txt,statbuf) -->statbuf结构体保存的是2.txt的属性

例子:获取文件类型、属性和大小

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

int main(int argc , char * argv[]) 
{
    //获取指定文件的属性 并保存在st这个结构体中
    struct stat st;
    stat(argv[1] ,&st);
    /*
    struct stat st;
    struct stat * p = &st; 
    stat(argv[1] ,p);
    */
    //通过对st结构体成员变量的分析,来解析他的类型、权限和大小
    //解析类型 
    if( S_ISREG(st.st_mode) )
    {
        printf("is reg file!\n");
    }
    else if(S_ISDIR(st.st_mode))
    {
         printf("is dir file!\n");
    }
    //解析权限 
    if( st.st_mode & S_IRUSR)
    {
        printf("uesr have read!\n");
    }
    //解析大小
    printf("file size = %ld\n",st.st_size);

}
	

3 目录操作

  • 在linux下面, 目录也是一个文件 ,同样可以通过open打开它 。
  • 打开之后我们可以得到一个文件描述符, 这个文件描述符描述的是一个目录。
  • 但是我们在用read(fd,…)去读一个目录的内容时,会报错,所以在linux不能用read去读一个目录 。
  • 目录文件里面的内容到底是什么了?为什么我们不能去read它呢?

目录和普通文件的区别 ,在linux下面 ,任何一个文件要存在必须有一个inode ,普通文件需要一个inode。目录文件同样也需要一个inode, 目录文件中的内容应该是什么呢?

我们联想一下windows 在 windows下我们双击普通文件相当于就是打开了这个文件, 我们看到的就是该文件的内容 ,同理我们双击文件夹也应该打开了这个文件夹(目录) 理应我们看到就是文件夹的内容 ,那么这就意味着我们访问一个文件夹 ,其实得到的就是这个文件夹下子文件或者子文件夹的属性信息。 linux也是如此。

​linux下打开一个目录, 目录的内容也是该目录下包含的子目录或者子文件夹的属性信息!

那么怎么保存目录下子文件或者子目录的属性信息了?
​通过目录项数组, 也就是说目录文件的内容其实就是一个目录项数组 。

​“目录项”是什么呢? 我们把目录下的每一个子文件或者子目录都称之为一个目录项。

​目录项数组的本质就是一个结构体数组 ,每一个目录项都对应一个结构体,这个结构体的类型是 struct dirent,我们如果想要知道一个目录下面的文件内容 ,那么就必须要去读目录项。

打开一个目录 opendir

NAME
​ opendir, fdopendir - open a directory
SYNOPSIS
​ #include <sys/types.h>
​ #include <dirent.h>

​ DIR *opendir(const char *name);
​ DIR *fdopendir(int fd);
​函数功能:打开一个目录

参数列表: name: 你要打开的目录的路径名
​ fd: 你要打开的目录的文件描述符 (先要open它, 以只读方式)
​返回值:
​ 成功返回一个DIR*类型的指针, 这个指针就代表这个打开的目录
​ 失败返回NULL 同时errno被设置

在linux下面用结构体DIR来描述一个已经打开的目录 ,DIR*则意味着指向一个已经成功打开的目录,后续的读操作需要用到该指针 。

读一个目录中的目录项 readdir

NAME
readdir - read a directory

SYNOPSIS
#include <dirent.h>
struct dirent *readdir(DIR *dirp);

函数功能: 从目录中读取目录项。

参数列表:
dirp : 表示你要从哪个打开的目录中读取目录项 ,就是opendir的返回值。
返回值:
返回下一个目录项的指针, 如果读完了整个目录项,就返回NULL 。

readdir用来从dirp指向的打开的目录中, 返回下一个目录项(struct dirent)的指针 。 一个目录下有多个目录项(一个子文件或者子文件夹都会对应一个目录项),每调用一次readdir就会返回下一个目录项的指针, 直到返回NULL。

 struct dirent {
         ino_t          d_ino;       /* Inode number */
         					//inode的编号
         off_t          d_off;       /* Not an offset; see below */
         					//目录项的偏移
         unsigned short d_reclen;    /* Length of this record */
         					//该结构体的长度
         unsigned char  d_type;      /* Type of file; not supported
         by all filesystem types */
         					//该目录项指向的那个文件的类型
         char           d_name[256]; /* Null-terminated filename */
							//该目录项指向的那个文件的名字
 };

该结构体中的成员变量只有d_ino 、d_name是所有linux系统都支持的。

​关闭一个目录 closedir

NAME
closedir - close a directory

SYNOPSIS
#include <sys/types.h>
#include <dirent.h>
int closedir(DIR *dirp);

函数功能: 关闭一个已经打开的目录

参数列表:
​ dirp : 你要关闭哪个打开的目录
​返回值:
​ 成功返回0
​ 失败返回-1 并且errno被设置

例子:把一个目录下所有的文件的文件名打印出来

#include <stdio.h> 
#include <sys/types.h>
#include <dirent.h>
#include <string.h>
//格式: ./main dir_name
int main(int argc , char * argv[]) 
{
    //1.打开目录 
    DIR * dir = opendir(argv[1]);
    if(dir == NULL)
    {
        perror("opendir error");
        return -1;
    }
    //2.不停地读目录项 
    struct dirent * dirp = NULL;
    while(1)
    {
        dirp = readdir(dir);
        if(dirp == NULL)
        {
            break;            
        }
        //打印读到的目录项指向的文件的名字 跳过.和..这两个文件夹 
        if( strcmp(dirp->d_name ,".") == 0    || strcmp(dirp->d_name ,"..") == 0 )
        {
            continue;
        }
        printf("%s\n",dirp->d_name);
    }
    //3.关闭目录
    closedir(dir);
    return 0;

}

例子:一个目录下所有普通文件的大小之和(目录下有目录 ,目录下的目录下还有文件 – 递归方式)

#include <stdio.h> 
#include <sys/types.h>
#include <dirent.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>

int get_dir_size(const char * pathname)
{
    int size = 0 ; //保存目录的大小
    //打开目录 
    DIR * dir = opendir( pathname);
    if(dir == NULL)
    {
        perror("opendir error");
        return -1;
    }
    //不停地读目录项 判断 
    struct dirent * dirp = NULL;
    while(1)
    {
        dirp = readdir(dir);
        if(dirp == NULL)
        {
            break;
        }
        //如果是.和.. 跳过它 
        if( strcmp(dirp->d_name ,".") == 0 || strcmp(dirp->d_name ,"..") == 0 )
        {
            continue;
        }
        //获取到带路径的文件名  路径:pathname 名字:dirp->d_name
        char filename[512]={0};
        sprintf(filename, "%s/%s",pathname,dirp->d_name);//拼接路径保存到filename
        //printf("%s/%s",pathname , dirp->d_name);
        //获取filename的属性信息 
        struct stat st; 
        int ret = stat(filename,&st);
        if(ret == -1)
        {
            perror("stat error");
            return -1;
        }
        if(S_ISDIR(st.st_mode))//如果是一个目录 
        {
           size += get_dir_size(filename);
        }
        else if(S_ISREG(st.st_mode))
        {
            size += st.st_size;
        }

    }
    closedir(dir);
    return size;
}

int main(int argc , char * argv[]) 
{
    printf("size = %d\n",get_dir_size(argv[1]));
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

QJ敬

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值