侧边栏壁纸
博主头像
如此肤浅

但行好事,莫问前程!

  • 累计撰写 52 篇文章
  • 累计创建 6 个标签
  • 累计收到 6 条评论

目 录CONTENT

文章目录

Linux 系统编程 | 2. 文件IO

如此肤浅
2022-06-22 / 0 评论 / 0 点赞 / 62 阅读 / 13,433 字 / 正在检测是否收录...
温馨提示:
本文最后更新于 2022-06-24,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

文件和目录

系统调用

什么是系统调用

系统调用,顾名思义,说的是操作系统提供给用户程序调用的一组“特殊”接口。用户程序可以通过这组“特殊”接口来获得操作系统内核提供的服务,比如用户可以通过文件系统相关的调用请求系统打开文件、关闭文件或读写文件,可以通过时钟相关的系统调用获得系统时间或设置定时器等。

从逻辑上来说,系统调用可被看成是一个内核与用户空间程序交互的接口——它好比一个中间人,把用户进程的请求传达给内核,待内核把请求处理完毕后再将处理结果送回给用户空间。
image-1655858911041

系统服务之所以需要通过系统调用来提供给用户空间的根本原因是为了对系统进行“保护”,因为我们知道 Linux 的运行空间分为内核空间与用户空间,它们各自运行在不同的级别中,逻辑上相互隔离。

所以用户进程在通常情况下不允许访问内核数据,也无法使用内核函数,它们只能在用户空间操作用户数据,调用用户空间函数。

系统调用的实现

系统调用是属于操作系统内核的一部分的,必须以某种方式提供给进程让它们去调用。CPU 可以在不同的特权级别下运行,而相应的操作系统也有不同的运行级别,用户态内核态。运行在内核态的进程可以毫无限制的访问各种资源,而在用户态下的用户进程的各种操作都有着限制,比如不能随意的访问内存、不能开闭中断以及切换运行的特权级别。显然,属于内核的系统调用一定是运行在内核态下,但是如何切换到内核态呢?

答案是软件中断。软件中断和我们常说的中断(硬件中断)不同之处在于,它是通过软件指令触发而并非外设引发的中断,也就是说,又是编程人员开发出的一种异常(该异常为正常的异常)。操作系统一般是通过软件中断从用户态切换到内核态。

系统调用和库函数的区别

Linux 下对文件操作有两种方式:系统调用(system call)和库函数调用(Library functions)。

库函数由两类函数组成:① 不需要调用系统调用;② 需要调用系统调用。

image-1655859277434

系统调用是需要时间的,程序中频繁的使用系统调用会降低程序的运行效率。当运行内核代码时,CPU 工作在内核态,在系统调用发生前需要保存用户态的栈和内存环境,然后转入内核态工作。系统调用结束后,又要切换回用户态。这种环境的切换会消耗掉许多时间!

  • C 语言 IO 库函数工作流程
    image-1655859950690

库函数访问文件的时候根据需要,设置不同类型的缓冲区,从而减少了直接调用 IO 系统调用的次数,提高了访问效率。

文件IO

虚拟地址空间

每个进程都会分配虚拟地址空间,在32位机器上,该地址空间为4G:

image-1655862139055
注:静态的局部变量存放在数据段(.data)中;动态分配的内存放在堆区。

在进程里平时所说的指针变量,保存的就是虚拟地址。当应用程序使用虚拟地址访问内存时,处理器(CPU)会将其转化成物理地址。

这样做的好处在于:① 进程隔离,更好的保护系统安全运行;② 屏蔽物理差异带来的麻烦,方便操作系统和编译器安排进程地址。

文件描述符

在 Linux 的世界里,一切设备皆文件。我们可以系统调用中 I/O 的函数对文件进行相应的操作( open()、close()、write() 、read() 等)。

打开现存文件或新建文件时,系统(内核)会返回一个文件描述符,文件描述符用来指定已打开的文件。这个文件描述符相当于这个已打开文件的标号,文件描述符是非负整数,是文件的标识,操作这个文件描述符相当于操作这个描述符所指定的文件。

程序运行起来后(每个进程)都有一张文件描述符的表,标准输入、标准输出、标准错误输出设备文件被打开,对应的文件描述符 0、1、2 记录在表中。程序运行起来后这三个文件描述符是默认打开的。

#define STDIN_FILENO  0 //标准输入的文件描述符
#define STDOUT_FILENO 1 //标准输出的文件描述符
#define STDERR_FILENO 2 //标准错误的文件描述符

在程序运行起来后打开其他文件时,系统会返回文件描述符表中最小可用的文件描述符,并将此文件描述符记录在表中。

image-1655866162015

  • 最大打开的文件个数
    Linux 中一个进程最多只能打开 NR_OPEN_DEFAULT (即1024)个文件,故当文件不再使用时应及时调用 close() 函数关闭文件。

    # 查看当前系统允许打开最大文件个数:
    cat /proc/sys/fs/file-max
    
    #当前默认设置最大打开文件个数1024
    ulimit -a
    
    # 修改默认设置最大打开文件个数为4096
    ulimit -n 4096
    

错误处理函数:perror、strerror

errno 是记录系统的最后一次错误代码。代码是一个 int 型的值,在 errno.h 中定义。查看错误代码 errno 是调试程序的一个重要方法。

当 Linux C api 函数发生异常时,一般会将 errno 全局变量赋一个整数值,不同的值表示不同的含义,可以通过查看该值推测出错的原因。

errno 对应的错误信息保存在下面两个文件中:

/usr/include/asm-generic/errno-base.h
/usr/include/asm-generic/errno.h

perror、strerror 案例:

#include <stdio.h>
#include <errno.h>
#include <string.h>

// errno是一个全局变量,在errno.h头文件中有定>义
// errno是保存系统最近出错的错误码
int main()
{
    // 打开一个不存在的文件
    FILE *fp = fopen("txt", "r");
    if(NULL == fp)
    {
        printf("fopen failed...\n");
        printf("errno:%d\n", errno);
        // strerror() 根据errno的值,解析出错的原因
        printf("fopen:%s\n", strerror(errno));
        
        // 根据errno值输出错误信息(更方便)
        perror("fopen err");
        
        return 1;
    }
    return 0;
}

执行结果如下:

ubuntu@VM-12-7-ubuntu:~/test$ ./a.out 
fopen failed...
errno:2
fopen:No such file or directory
fopen err: No such file or directory

常用系统调用

open

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
功能:
	打开文件,如果文件不存在则可以选择创建。
参数:
	pathname:文件的路径及文件名
	flags:打开文件的行为标志,必选项 O_RDONLY, O_WRONLY, O_RDWR
	mode:这个参数,只有在文件不存在时有效,指新建文件时指定文件的权限
返回值:
	成功:成功返回打开的文件描述符
	失败:-1
  • flags 详细说明
    必选项:

    取值 含义
    O_RDONLY 以只读的方式打开
    O_WRONLY 以只写的方式打开
    O_RDWR 以可读、可写的方式打开

    可选项,和必选项按位或起来:

    取值 含义
    O_CREAT 文件不存在则创建文件,使用此选项时需使用mode说明文件的权限
    O_EXCL 如果同时指定了O_CREAT,且文件已经存在,则出错
    O_TRUNC 如果文件存在,则清空文件内容
    O_APPEND 写文件时,数据添加到文件末尾
    O_NONBLOCK 对于设备文件, 以O_NONBLOCK方式打开可以做非阻塞I/O
  • mode 补充说明
    ① 文件最终权限:mode & ~umask
    ② shell 进程的 umask 掩码可以用 umask 命令查看

    # 查看掩码(补码)
    umask
    # 设置掩码,mode为八进制数
    umask mode
    # 查看各组用户的默认操作权限
    umask  -S
    

    mode 的取值:

    取值 八进制 含义
    S_IRWXU 00700 文件所有者的读、写、可执行权限
    S_IRUSR 00400 文件所有者的读权限
    S_IWUSR 00200 文件所有者的写权限
    S_IXUSR 00100 文件所有者的可执行权限
    S_IRWXG 00070 文件所有者同组用户的读、写、可执行权限
    S_IRGRP 00040 文件所有者同组用户的读权限
    S_IWGRP 00020 文件所有者同组用户的写权限
    S_IXGRP 00010 文件所有者同组用户的可执行权限
    S_IRWXO 00007 其他组用户的读、写、可执行权限
    S_IROTH 00004 其他组用户的读权限
    S_IWOTH 00002 其他组用户的写权限
    S_IXOTH 00001 其他组用户的可执行权限

close

#include <unistd.h>

int close(int fd);
功能:
	关闭已打开的文件
参数:
	fd : 文件描述符,open()的返回值
返回值:
	成功:0
	失败: -1, 并设置errno

需要说明的是,当一个进程终止时,内核对该进程所有尚未关闭的文件描述符调用 close 关闭,所以即使用户程序不调用 close,在终止时内核也会自动关闭它打开的所有文件。

但是对于一个长年累月运行的程序(比如网络服务器),打开的文件描述符一定要记得关闭,否则随着打开的文件越来越多,会占用大量文件描述符和系统资源。

open、close 案例:

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

int main()
{
    int fd = -1;
    
    // 1.以只读方式打开一个文件,若文件不存在则报错
    //fd = open("txt", O_RDONLY);
    
    // 2.以只写方式打开一个文件,若文件存在就直接打开,若文件不存在则新建一个文件
    //fd = open("txt", O_WRONLY | O_CREAT, 0644);
    
    // 3.以只写方式打开文件,若文件存在则报错,若不存在则新建一个文件
    //fd = open("txt", O_WRONLY | O_CREAT | O_EXCL, 0644);
   
    // 4.以读写的方式打开一个文件,若不存在则新建一个文件
    //fd = open("txt", O_RDWR | O_CREAT, 0644);

    // 5.若文件不存在则创建一个文件,若文件存在则打开后清空
    //fd = open("txt", O_WRONLY | O_TRUNC | O_CREAT, 0644);

    // 6.以只读、追加的方式打开文件
    fd = open("txt", O_WRONLY | O_APPEND);

    if(-1 == fd)
    {
        perror("open");
        return 1;
    }
    printf("fd = %d\n", fd);
    close(fd);

    return 0;
}

write

#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
功能:
	把指定数目的数据写到文件(fd)
参数:
	fd : 文件描述符
	buf : 数据首地址
count : 写入数据的长度(字节)
返回值:
	成功:实际写入数据的字节个数
	失败: - 1

open、close 案例:

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

int main()
{

    int fd = -1;
    int ret = -1;
    char* str = "hello world";

    // 1.以只写方式打开一个文件
    fd = open("txt", O_WRONLY | O_CREAT, 0644);
    if(-1 == fd)
    {
        perror("open");
        return 1;
    }
    printf("fd = %d\n", fd);
  
    // 2.写文件
    ret = write(fd, str, strlen(str));
    if(-1 == ret)
    {
        perror("write");
        return 1;
    }
    printf("write len:%d\n", ret);

    // 3.关闭文件
    close(fd);

    return 0;
}

read

#include <unistd.h>

ssize_t read(int fd, void *buf, size_t count);
功能:
	把指定数目的数据读到内存(缓冲区)
参数:
	fd : 文件描述符
	buf : 内存首地址
	count : 读取的字节个数
返回值:
	成功:实际读取到的字节个数
	失败: - 1

lseek 改变文件偏移量

所有打开的文件都有一个当前文件偏移量(current file offset),以下简称为 cfo。cfo 通常是一个非负整数,用于表明文件开始处到文件当前位置的字节数。

读写操作通常开始于 cfo,并且使 cfo 增大,增量为读写的字节数。文件被打开时,cfo 会被初始化为 0,除非使用了 O_APPEND 。

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

off_t lseek(int fd, off_t offset, int whence);
功能:
	改变文件的偏移量
参数:
	fd:文件描述符
	offset:根据whence来移动的位移数(偏移量),可以是正数,也可以负数。
    如果正数,则相对于whence往右移动,如果是负数,则相对于whence往左移动。
    如果向前移动的字节数超过了文件开头则出错返回,如果向后移动的字节数超过了文件末尾,
    再次写入时将增大文件尺寸。

	whence:其取值如下:
		SEEK_SET:从文件开头移动offset个字节
		SEEK_CUR:从当前位置移动offset个字节
		SEEK_END:从文件末尾移动offset个字节
返回值:
	若lseek成功执行, 则返回新的偏移量

lseek 案例:

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

int main()
{
    int fd = -1;
    int ret = -1;

    // 1. 打开文件
    fd = open("txt", O_RDWR | O_CREAT, 0644);
    if(fd == -1)
    {
        perror("open");
        return 1;
    }
    printf("fd = %d\n", fd);

    // 2. lseek操作
    write(fd, "ABCDEFG", 7);
    //从文件开头偏移32个字节
    ret = lseek(fd, 32, SEEK_SET);
    if(-1 == ret)
    {
        perror("lseek");
        return 1;
    }
    write(fd, "1234567890", 10);

    //3. 关闭文件 
    close(fd);
    return 0;
}

stat/lstat 获取文件状态信息

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

int stat(const char *path, struct stat *buf);
int lstat(const char *pathname, struct stat *buf);
功能:
获取文件状态信息
	stat和lstat的区别:
		当文件是一个符号链接时,lstat返回的是该符号链接本身的信息;
		而stat返回的是该链接指向的文件的信息。
参数:
	path:文件名
	buf:保存文件信息的结构体
返回值:
	成功:0
	失败 - 1
  • struct stat 结构体说明
    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;	//文件字节数(文件大小)
      blksize_t		st_blksize;	//块大小(文件系统的I/O 缓冲区大小)
      blkcnt_t		st_blocks;	//块数
      time_t		st_atime;	//最后一次访问时间
      time_t		st_mtime;	//最后一次修改时间
      time_t		st_ctime;	//最后一次改变时间(指属性)
    };
    

stat 案例:

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

int main()
{ 
    int ret = -1;
    struct stat s;

    // 读取指定文件的信息 
    ret = stat("txt", &s);
    if(-1 == ret)
    {
        perror("stat");
        return 1;
    }
    // %#x:有前缀的16进制形式输出
    printf("st_dev:%ld\n", s.st_dev);
    printf("st_info:%ld\n", s.st_ino);
    printf("st_uid:%d\n", s.st_uid);

    switch (s.st_mode & 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;
    }
    return 0;
}

access 判断文件所属者权限

#include <unistd.h>

int access(const char *pathname, int mode);
功能:测试指定文件是否具有某种属性
参数:
	pathname:文件名
	mode:文件所属者的权限,4种权限
		R_OK:	是否有读权限
		W_OK:	是否有写权限
		X_OK:	是否有执行权限
		F_OK:	测试文件是否存在
返回值:
	0:	有某种权限,或者文件存在
	-1:没有,或文件不存在

chomd 修改文件权限

#include <sys/stat.h>

int chmod(const char *pathname, mode_t mode);
功能:修改文件权限
参数:
	filename:文件名
	mode:权限(8进制数)
返回值:
	成功:0
	失败:-1

chown 修改文件所属者/组

#include <unistd.h>

int chown(const char *pathname, uid_t owner, gid_t group);
功能:修改文件所有者和所属组
参数:
	pathname:文件或目录名
	owner:文件所有者id,通过查看 / etc / passwd 得到所有者id
	group:文件所属组id,通过查看 / etc / group 得到用户组id
返回值:
	成功:0
	失败:-1

truncate 修改文件大小

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

int truncate(const char *path, off_t length);
功能:修改文件大小
参数:
	path:文件文件名字
	length:指定的文件大小
		a)比原来小, 删掉后边的部分
		b)比原来大, 向后拓展
返回值:
	成功:0
	失败:-1
#include <unistd.h>

int link(const char *oldpath, const char *newpath);
功能:创建一个硬链接
参数:
	oldpath:源文件名字
	newpath:硬链接名字
返回值:
	成功:0
	失败:-1
#include <unistd.h>

int symlink(const char *target, const char *linkpath);
功能:创建一个软链接
参数:
	target:源文件名字
	linkpath:软链接名字
返回值:
	成功:0
	失败:-1
#include <unistd.h>

ssize_t readlink(const char *pathname, char *buf, size_t bufsiz);
功能:读软链接对应的文件名,不是读内容(该函数只能读软链接文件)
参数:
	pathname:软连接名
	buf:存放软件对应的文件名
	bufsiz :缓冲区大小(第二个参数存放的最大字节数)
返回值:
	成功:>0,读到buf中的字符个数
	失败:-1
#include <unistd.h>

int unlink(const char *pathname);
功能:删除一个文件(软硬链接文件)
参数:
	pathname:删除的文件名字
返回值:
	成功:0
	失败:-1

rename 重命名

#include <stdio.h>

int rename(const char *oldpath, const char *newpath);
功能:把oldpath的文件名改为newpath
参数:
oldpath:旧文件名
newpath:新文件名
返回值:
成功:0
失败:-1

dup/dup2 复制文件描述符

dup() 和 dup2() 是两个非常有用的系统调用,都是用来复制一个文件的描述符,使新的文件描述符也标识旧的文件描述符所标识的文件。

这个过程类似于现实生活中的配钥匙,钥匙相当于文件描述符,锁相当于文件,本来一个钥匙开一把锁,相当于,一个文件描述符对应一个文件,现在,我们去配钥匙,通过旧的钥匙复制了一把新的钥匙,这样的话,旧的钥匙和新的钥匙都能开启这把锁。

对比于 dup(),dup2() 也一样,通过原来的文件描述符复制出一个新的文件描述符,这样的话,原来的文件描述符和新的文件描述符都指向同一个文件,我们操作这两个文件描述符的任何一个,都能操作它所对应的文件。

  • dup()

    #include <unistd.h>
    
    int dup(int oldfd);
    功能:
        通过 oldfd 复制出一个新的文件描述符,
        新的文件描述符是调用进程文件描述符表中最小可用的文件描述符,
        最终 oldfd 和新的文件描述符都指向同一个文件。
    参数:
        oldfd : 需要复制的文件描述符 oldfd
    返回值:
    	成功:新文件描述符
        失败: -1
    
  • dup2()

    #include <unistd.h>
    
    int dup2(int oldfd, int newfd);
    功能:
        通过 oldfd 复制出一个新的文件描述符 newfd,如果成功,
        newfd 和函数返回值是同一个返回值,
        最终 oldfd 和新的文件描述符 newfd 都指向同一个文件。
    参数:
        oldfd : 需要复制的文件描述符
        newfd : 新的文件描述符,这个描述符可以人为指定一个合法数字(0 - 1023),
        如果指定的数子已经被占用(和某个文件有关联),
        此函数会自动关闭 close() 断开这个数字和某个文件的关联,再来使用这个合法数字。
    返回值:
        成功:返回 newfd
        失败:返回 -1
    

案例:
image-1656037871437

fcnlt 改变打开文件的性质

#include <unistd.h>
#include <fcntl.h>

int fcntl(int fd, int cmd, ... /* arg */);
功能:改变已打开的文件性质,fcntl针对描述符提供控制。
参数:
	fd:操作的文件描述符
	cmd:操作方式
	arg:针对cmd的值,fcntl能够接受第三个参数int arg。
返回值:
	成功:返回某个其他值
	失败:-1

fcntl 函数有5种功能:

  1. 复制一个现有的描述符(cmd=F_DUPFD)
  2. 获得/设置文件描述符标记(cmd=F_GETFD 或 F_SETFD)
  3. 获得/设置文件状态标记(cmd=F_GETFL 或 F_SETFL)
  4. 获得/设置异步I/O所有权(cmd=F_GETOWN 或 F_SETOWN)
  5. 获得/设置记录锁(cmd=F_GETLK, F_SETLK 或 F_SETLKW)

fcnlt案例:复制文件描述符

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

int main()
{
    int fd = -1;
    int ret = -1;
    int newfd = -1;
  
    //1.打开文件
    fd = open("txt", O_WRONLY | O_CREAT, 0644);
    if(-1 == fd)
    {
        perror("open");
        return 1;
    }
    printf("fd = %d\n", fd);

    //2.文件描述符复制
    newfd = fcntl(fd, F_DUPFD, 0);
    if(-1 == newfd)
    {
        perror("fcntl");
        return 1;
    }
    printf("newfd = %d\n", newfd);

    //3.写文件
    write(fd, "123456789", 9);
    write(newfd, "ABCDEFG", 7);

    //4.关闭文件
    close(fd);
    close(newfd);

    return 0;
}

目录

常用系统调用

getcwd 获取进程工作目录

#include <unistd.h>

char *getcwd(char *buf, size_t size);
功能:获取当前进程的工作目录
参数:
	buf : 缓冲区,存储当前的工作目录
	size : 缓冲区大小
返回值:
	成功:buf中保存当前进程工作目录位置
	失败:NULL

chdir 改变进程工作路径

#include <unistd.h>

int chdir(const char *path);
功能:修改当前进程(应用程序)的路径
参数:
	path:切换的路径
返回值:
	成功:0
	失败:-1

getcwd、chdir 案例:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>

#define SIZE 32

int main()
{
    char buf[SIZE];
    int ret = -1;

    //1.获取当前进程的工作目录
    memset(buf, 0, SIZE);
    if(NULL == getcwd(buf, SIZE))
    {
        perror("getcwd");
        return 1;
    }
    printf("buf:%s\n", buf);

    //2.改变当前进程的工作目录
    ret = chdir("/home/ubuntu");
    if(-1 == ret)
    {
        perror("chdir");
        return 1;
    }

    //3.读取当前进程的工作目录
    memset(buf, 0, SIZE);
    if(NULL == getcwd(buf, SIZE))
    {
        perror("getcwd");
        return 1;
    }
    printf("buf:%s\n", buf);

    return 0;
}

opendir 打开目录

#include <sys/types.h>
#include <dirent.h>

DIR *opendir(const char *name);
功能:打开一个目录
参数:
	name:目录名
返回值:
	成功:返回指向该目录结构体指针
	失败:NULL

closedir 关闭目录

#include <sys/types.h>
#include <dirent.h>

int closedir(DIR *dirp);
功能:关闭目录
参数:
	dirp:opendir返回的指针
返回值:
	成功:0
	失败:-1

readdir 读取目录

#include <dirent.h>

struct dirent *readdir(DIR *dirp);
功能:读取目录
参数:
	dirp:opendir的返回值
返回值:
	成功:目录项结构体指针
	失败:NULL
struct dirent
{
	ino_t d_ino;	// 此目录进入点的inode
	off_t d_off;	// 目录文件开头至此目录进入点的位移
	signed short int d_reclen;	// d_name 的长度, 不包含NULL 字符
	unsigned char d_type;	// d_type 所指的文件类型 
	char d_name[256];	// 文件名
};
  • d_type 文件类型说明:
    取值 含义
    DT_BLK 块设备
    DT_CHR 字符设备
    DT_DIR 目录
    DT_LNK 软链接
    DT_FIFO 管道
    DT_REG 普通文件
    DT_SOCK 套接字
    DT_UNKNOWN 未知

opendir、readdir、closedir 案例:

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

int main()
{
    DIR* dir = NULL;
    struct dirent* d = NULL;

    //1.打开目录
    dir = opendir("./");
    if(NULL == dir)
    {
        perror("opendir");
        return 1;
    }
    printf("打开目录成功...\n");

    //2.循环读取目录的内容
    while(1)
    {
        d = readdir(dir);
        if(NULL == d)
        {
            break;
        }
        
        char* fileType;
        switch(d->d_type){
            case DT_BLK:
                fileType = "block fevice";
                break;
            case DT_CHR:
                fileType = "character";
                break;
            case DT_DIR:
                fileType = "directory";
                break;
            case DT_FIFO:
                fileType = "named pipe";
                break;
            case DT_LNK:
                fileType = "vsymbolic link";
                break;
            case DT_REG:
                fileType = "regular file";
                break;
            case DT_SOCK:
                fileType = "UNIX domain socket";
                break;
            case DT_UNKNOWN:
                fileType = "unknown";
                break;
            default:
                break;

        }
        printf("%s\n: %s\n", d->d_name, fileType);
    }


    //3.关闭目录
    closedir(dir);

    return 0;
}

该看P96

0

评论区