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

但行好事,莫问前程!

  • 累计撰写 24 篇文章
  • 累计创建 11 个标签
  • 累计收到 5 条评论

目 录CONTENT

文章目录

文件IO

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

库函数 vs 系统调用

标准C库函数带有缓冲区,能够提高效率。而系统调用,每调用一次就会去读/写一次磁盘。

所有,在不同的场景要选择使用库函数还是系统调用。如,网络通信的时候,避免数据一直在缓冲区中,所以要使用系统调用。而操作磁盘时,使用库函数。

image-1677756338473

  • 标准C库IO函数
    image-1677756177560

相关概念

虚拟地址空间

image-1677756769939

文件描述符

每个进程默认能同时打开的文件个数为1024个,前3个(标准输入、标准输出、标准错误)默认是打开的,并且这3个是指向同一个终端。

不同的文件描述符可以指向同一个文件。

image-1677758593469

相关函数

open、close

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

int open(const char *pathname, int flags);

功能

  • 打开一个已经存在的文件

返回值

  • 成功:返回一个新的文件描述符
  • 失败:返回 -1,并设置 errno

参数

  • pathname:要打开的文件路径
  • flags:对文件的操作权限的设置和其他设置
    • 必选项:O_RDONLY,O_WRONLY,O_RDWR(3选1)
    • 可选项:O_CREAT(文件不存在,创建新文件)等(与必选项按位或)
#includ e <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int open(const char *pathname, int flags, mode_t mode);

功能

  • 创建一个新的文件

返回值

  • 同上

参数

  • pathname:同上
  • flags:同上
  • mode:八进制的数,表示创建出的新的文件的操作权限。如:0775,最终的权限是mode & ~umask的值。umask的作用就是抹去某些权限。
// 关闭文件描述符
int close(int fd);

read、write

  • read

    #include <unistd.h>
    
    ssize_t read(int fd, void *buf, size_t count);
    

    功能

    • 读数据

    返回值

    • 成功:返回读取到的字节数,如果为0表示文件读完了
    • 失败:-1,设置errno

    参数

    • fd:文件描述符,通过open得到的,可通过fd操作这个文件
    • buf:将读取的数据存放到buf,数组的地址(传出参数)
    • count:指定的数组的大小
  • write

    #include <unistd.h>
    
    ssize_t write(int fd, const void *buf, size_t count);
    

    功能

    • 写数据

    返回值

    • 成功:返回实际写入的字节数
    • 失败:-1,设置errno

    参数

    • fd:文件描述符
    • buf:要往磁盘写入的数据,数组
    • count:要写的数据的实际的大小

lseek

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

off_t lseek(int fd, off_t offset, int whence);

功能

  • 移动文件指针

返回值

  • 成功:返回偏移后文件指针的位置
  • 失败:-1,设置errno

参数

  • fd:文件描述符
  • offset:偏移量
  • whence:
    • SEEK_SET:从文件头开始偏移offset
    • SEEK_CUR:从文件当前位置开始偏移offset
    • SEEK_END:从文件尾开始偏移offset
// lseek的常见作用:

// 1、移动文件指针到文件头
lseek(fd, 0, SEEK_SET);

// 2、获取当前文件指针的位置
lseek(fd, 0, SEEK_CUR);

// 3、获取文件长度
lseek(fd, 0, SEEK_END);

// 4、拓展文件的长度,当前文件10B,要拓展100B,拓展的是空字符
lseek(fd, 100, SEEK_END);

stat、lstat

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

// 获取一个文件的相关信息,保存到statbuf中
int stat(const char *pathname, struct stat *statbuf);

// 用来获取一个软链接文件的信息
int lstat(const char *pathname, struct stat *statbuf);

返回值

  • 成功:0
  • 失败:-1,设置errno

参数

  • pathname:操作的文件的路径
  • statbuf:结构体变量,用于保存获取到的文件的信息(传出参数)
  • stat结构体
    image-1677764737006

模拟实现 ls -l 命令

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

// 模拟实现 ls -l 指令
// -rw-rw-r-- 1 zengyq zengyq 43 Mar  2 21:16 a.txt
int main(int argc, char* argv[]) {
    // 1. 判断输入的参数是否正确
    if(argc < 2) {
        printf("%s filename\n", argv[0]);
        return -1;
    }

    // 2. 通过 stat 获取用户传入的文件信息
    struct stat st;
    int ret = stat(argv[1], &st);
    if(ret == -1) {
        perror("stat");
        return -1;
    }

    // 3. 获取文件类型和文件权限
    // (字符串最后一个是空字符'\0',所以长度是11)
    char perms[11] = {0}; // 用于保存文件类型和权限

    // 文件类型
    switch (st.st_mode & S_IFMT)
    {
    case S_IFSOCK:
        perms[0] = 's';
        break;
    case S_IFLNK:
        perms[0] = 'l';
        break;
    case S_IFREG:
        perms[0] = '-';
        break;
    case S_IFBLK:
        perms[0] = 'b';
        break;
    case S_IFDIR:
        perms[0] = 'd';
        break;
    case S_IFCHR:
        perms[0] = 'c';
        break;
    case S_IFIFO:
        perms[0] = 'p';
        break;
    default:
        perms[0] = '?';
        break;
    }

    // 文件的访问权限
    // 文件所有者
    perms[1] =  st.st_mode & S_IRUSR ? 'r' : '-';
    perms[2] =  st.st_mode & S_IWUSR ? 'w' : '-';
    perms[3] =  st.st_mode & S_IXUSR ? 'x' : '-';

    // 文件所在组
    perms[4] =  st.st_mode & S_IRGRP ? 'r' : '-';
    perms[5] =  st.st_mode & S_IWGRP ? 'w' : '-';
    perms[6] =  st.st_mode & S_IXGRP ? 'x' : '-';

    // 其他人
    perms[7] =  st.st_mode & S_IROTH ? 'r' : '-';
    perms[8] =  st.st_mode & S_IWOTH ? 'w' : '-';
    perms[9] =  st.st_mode & S_IXOTH ? 'x' : '-';

    // 硬链接数
    int linkNum = st.st_nlink;

    // 文件所有者
    char *fileUser = getpwuid(st.st_uid)->pw_name;

    // 文件所在组
    char *fileGrp = getgrgid(st.st_gid)->gr_name;

    // 文件大小
    off_t fileSize = st.st_size;

    // 获取修改时间
    char *time = ctime(&st.st_mtime);
    char mtime[512] = {0};
    strncpy(mtime, time, strlen(time) - 1); // 去掉time末尾的换行

    char buf[1024];
    sprintf(buf, "%s %d %s %s %ld %s %s", perms, linkNum, fileUser, fileGrp, fileSize, mtime, argv[1]);
    
    printf("%s\n", buf);
    return 0;

执行结果如下:
image-1677921082066

文件属性操作函数

access、chmod、truncate

  • access

    #include <unistd.h>
    
    int access(const char *pathname, int mode);
    

    功能

    • 判断当前进程对某个文件是否有某个权限,或者判断文件是否存在

    返回值

    • 成功:0
    • 失败:-1,并设置errno

    参数

    • pathname:判断的文件的路径
    • mode:
      • R_OK:判断是否有读权限
      • W_OK:判断是否有写权限
      • X_OK:判断是否有执行权限
      • F_OK:判断文件是否存在
  • chmod

    #include <sys/stat.h>
    
    int chmod(const char *pathname, mode_t mode);
    

    功能

    • 修改文件的权限

    返回值

    • 成功:0
    • 失败:-1,并设置errno

    参数

    • pathname:需要修改的文件的路径
    • mode:需要修改的权限值,八进制的数,如:0777
  • truncatre

     #include <unistd.h>
    #include <sys/types.h>
    
    int truncate(const char *path, off_t length);
    

    功能

    • 缩减或扩展文件的大小到指定的大小

    返回值

    • 成功:0
    • 失败:-1,并设置errno

    参数

    • path:需要修改的文件的路径
    • length:需要最终文件变成的大小

目录操作函数

mkdir、rmdir、rename、chdir、getcwd

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

int mkdir(const char *pathname, mode_t mode);

功能

  • 创建一个目录

返回值

  • 成功:0
  • 失败:-1,并设置errno

参数

  • pathname:创建的目录的路径
  • mode:权限,八进制的数(一个目录必须有可执行权限才能进入这个目录)
// 删除一个空目录
int redir(const char *pathname);

// 修改文件的名称和位置
int rename(const char *oldpath, const char *newpath);

// 修改进程当前的工作目录
int chdir(const char *path);

// 获取进程当前的工作目录,保存到大小为size的数组buf中
char *getcwd(char *buf, size_t size);

dup、dup2

#include <unistd.h>

// 复制文件描述符oldfd,返回一个最小的可用的新文件描述符
int dup(int oldfd);

// 重定向文件描述符:newfd 原来指向的文件close,然后指向oldfd指向的文件。
// oldfd 必须是一个有效的文件描述符
int dup2(int oldfd, int newfd);

fcntl

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

int fcntl(int fd, int cmd, ... /* arg */ );

功能

  • 操作文件描述符

返回值

  • 成功:0
  • 失败:-1,并设置errno

参数

  • fd:需要操作的文件描述符
  • cmd:表示对fd进行什么操作
    • F_DUPFD:复制文件描述符,复制的是第一个参数fd,得到一个新的文件描述符
    • F_GETFL:获取指定文件描述符的文件状态,获取的flag和通过open函数传递的flag是相同的东西
    • FSETFL:设置文件描述符的状态flag
  • …:(可变参数)cmd所表示的操作需要的参数,不同的操作需要的参数个数可能不同
0

评论区