1. 文件操作

1.1 stat函数

1.1.1 作用

  • 获取文件属性(从inode上获取)
  • e.g. stat aaa

image.png

1.1.2 头文件

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

    1.1.3 函数声明

  • int stat(const char *pathname, struct stat *buf);

  • int fstat(int fd, struct stat *buf);

    1.1.4 返回值

  • 成功返回0,失败返回-1

    1.1.5 文件属性(struct stat)

    image.png

    1.1.6 mode_t st_mode

    image.png

  • 该变量占 2byte 共 16位

    • 掩码的使用:st_mode & 掩码
    • st_mode与某类文件属性信息掩码做按位与操作,然后与宏比较,即可得到相应的信息
    • 如果想获取文件属性信息,st_mode&掩码
      • e.g. st_mode & S_IFMT可以得到文件类型
    • 如果看文件是否有哪个权限,st_mode&掩码&权限的掩码,然后看是否大于0
      • e.g. st_mode & S_IRWXU & S_IRUSR 可以看文件所有者是否有读权限
  • 文件类型(12-15 bit)
    • 掩码:S_IFMT 0170000 过滤 st_mode中除文件类型以外的信息
    • S_IFSOCK 0140000 套接字
    • S_IFLNK 0120000 符号链接(软链接)
    • S_IFREG 0100000 普通文件
    • S_IFBLK 0060000 块设备
    • S_IFDIR 0040000 目录
    • S_IFCHR 0020000 字符设备
    • S_IFIFO 0010000 管道
  • 特殊权限位(9-11 bit)很少用
    • S_ISUID 0004000 设置用户ID
    • S_ISGID 0002000 设置组ID
    • S_ISVTX 0001000 黏住位

image.png

  • 文件所有者权限(6-8 bit)
    • 掩码:S_IRWXU 00700 过滤 st_mode中除文件所有者权限以外的信息
    • S_IRUSR 00400 读权限
    • S_IWUSR 00200 写权限
    • S_IXUSR 00100 执行权限
  • 所属组权限(3-5 bit)
    • 掩码:S_IRWXG 00070 过滤 st_mode中除所属组权限以外的信息
    • S_IRGRP 00040 读权限
    • S_IWGRP 00020 写权限
    • S_IXGRP 00010 执行权限
  • 其他人权限(0-2 bit)

    • 掩码:S_IRWXO 00007 过滤 st_mode中除其他人权限以外的信息
    • S_IROTH 00004 读权限
    • S_IWOTH 00002 写权限
    • S_IXOTH 00001 执行权限

      1.1.7 特性

  • 穿透(跟踪)软链接,读到的是软链接映射到的文件的属性信息

  • 相当于用vim打开一个软链接,打开的是软链接指向的文件

    1.1.8 练习:利用stat实现类似ls的功能
    image.png

    1. #include <sys/types.h>
    2. #include <sys/stat.h>
    3. #include <unistd.h>
    4. #include <stdio.h>
    5. #include <stdlib.h>
    6. #include <fcntl.h>
    7. #include <pwd.h>
    8. #include <grp.h>
    9. #include <time.h>
    10. #include <string.h>
    11. int main(int argc, char** argv)
    12. {
    13. char **fn = argv;
    14. if(argc < 2)
    15. {
    16. printf("miss a parameter: ./a.out filename\n");
    17. exit(1);
    18. }
    19. struct stat st;
    20. int ret = stat(fn[1], &st);
    21. char ftmo[11] = {0};
    22. //获取文件类型和权限
    23. switch(st.st_mode & S_IFMT)
    24. {
    25. case S_IFSOCK: ftmo[0] = 's'; break;
    26. case S_IFLNK: ftmo[0] = 'l'; break;
    27. case S_IFREG: ftmo[0] = '-'; break;
    28. case S_IFBLK: ftmo[0] = 'b'; break;
    29. case S_IFDIR: ftmo[0] = 'd'; break;
    30. case S_IFCHR: ftmo[0] = 'c'; break;
    31. case S_IFIFO: ftmo[0] = 'p'; break;
    32. }
    33. //获取所有者权限
    34. ftmo[1] = (st.st_mode & S_IRWXU & S_IRUSR)?'r':'-';
    35. ftmo[2] = (st.st_mode & S_IRWXU & S_IWUSR)?'w':'-';
    36. ftmo[3] = (st.st_mode & S_IRWXU & S_IXUSR)?'x':'-';
    37. //获取组内权限
    38. ftmo[4] = (st.st_mode & S_IRWXG & S_IRGRP)?'r':'-';
    39. ftmo[5] = (st.st_mode & S_IRWXG & S_IWGRP)?'w':'-';
    40. ftmo[6] = (st.st_mode & S_IRWXG & S_IXGRP)?'x':'-';
    41. //获取其他人权限
    42. ftmo[7] = (st.st_mode & S_IRWXO & S_IROTH)?'r':'-';
    43. ftmo[8] = (st.st_mode & S_IRWXO & S_IWOTH)?'w':'-';
    44. ftmo[9] = (st.st_mode & S_IRWXO & S_IXOTH)?'x':'-';
    45. ftmo[10] = '.';
    46. //硬链接数目
    47. int linknum = st.st_nlink;
    48. //文件所有者
    49. char* uname = getpwuid(st.st_uid)->pw_name;
    50. //文件所属组
    51. char* gname = getgrgid(st.st_gid)->gr_name;
    52. //文件大小
    53. size_t fsize = st.st_size;
    54. //文件日期
    55. //注意:ctime返回的字符串末尾有换行符,需要处理
    56. char* ftime = ctime(&st.st_ctime);
    57. ftime[strlen(ftime)-1] = 0;
    58. //文件名
    59. char* fname = fn[1];
    60. //格式化字符串
    61. char buf[100] = {0};
    62. sprintf(buf, "%s %d %s %s %d %s %s",
    63. ftmo, linknum, uname, gname, fsize, ftime, fname);
    64. printf("%s\n", buf);
    65. return 0;
    66. }

    1.2 lstat函数

    1.2.1 作用和头文件

  • 同stat

    1.2.2 函数声明

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

    1.2.3 特性

  • 不穿透(跟踪) 软链接,读到的是软链接的属性信息

  • 相当于ls -l 和 rm,操作的是软链接的信息

    1.3 access函数

    1.3.1 作用

  • 测试指定文件是否拥有某种权限

    1.3.2 头文件

  • include

    1.3.3 原型

  • int access(const char *pathname, int mode);

    1.3.4 参数

  • pathname —> 文件名

  • mode —> 权限类别

    • R_OK 是否有读权限
    • W_OK 是否有写权限
    • X_OK 是否有执行权限
    • F_OK 测试一个文件是否存在

      1.3.5 返回值

  • 0 —> 所有欲查核的权限都通过了检查

  • -1 —> 有权限被禁止

    1.4 chmod函数

    1.4.1 作用

  • 改变文件的权限

    1.4.2 头文件

  • #include <sys/stat.h>

    1.4.3 原型

  • int chmod( const char *filename, int pmode );

    1.4.4 参数

  • filename —> 文件名

  • pmode —> 权限

    • 必须是一个8进制数
    • 注:可以使用strtol函数将字符串转换为指定进制数 ‘0666’ -> 0666
      • include

      • long int strtol(const char nptr, char *endptr, int base); 其中endptr用于测试,可设为0

        1.4.5 返回值

  • 0 —> 改变成功

  • -1 —> 失败

    1.5 chown函数

    1.5.1 作用

  • 改变文件的所有者

    1.5.2 头文件

  • #include <unistd.h>

    1.5.3 原型

  • int chown(const char *path, uid_t owner, gid_t group);

    1.5.4 参数

  • 其中uid 和 gid可以通过/etc/passwd获得

  • passwd文件应该怎么看

    • man 5 passwd

      1.5.5 返回值

  • 0 —> 成功

  • -1 —> 失败

    1.6 truncate函数

    1.6.1 作用

  • 将参数path 指定的文件大小改为参数length 指定的大小。如果原来的文件大小比参数length大,则超过的部分会被删去。

  • 扩展或截断一个文件
    • 假设文件长度为100
    • 第二个参数指定长度为20 ->文件被截断
    • 第二个参数指定长度为300 -> 文件被拓展(空洞文件)
  • 与lseek的主要区别是扩展时不需要写入内容

    1.6.2 头文件

  • #include <unistd.h>

  • #include <sys/types.h>

    1.6.3 原型

  • int truncate(const char *path, off_t length);

  • path —> 文件路径
  • length —> 指定的文件大小

    1.6.4 返回值

  • 0 —> 执行成功

  • -1 —> 执行失败,需要写权限才能扩展文件大小

    1.7 链接

    1.7.1 link 函数

  • 作用:创建一个硬链接

  • 头文件:#include <unistd.h>
  • 原型:int link(const char _oldpath, const char newpath);_

    1.7.2 symlink 函数

  • 作用:创建一个软连接

  • 其他同link

    1.7.3 readlink 函数

  • 作用:读软链接对应的文件名,不是读内容

  • 读非软链接会失败

    image.png

    1.7.4 unlink 函数

  • 作用

    • 删除一个文件的目录项并减少它的链接数,若成功则返回0,否则返回-1,错误原因存于errno。
    • image.png
    • 如果想通过调用这个函数来成功删除文件,你就必须拥有这个文件的所属目录的写和执行权限。
  • 头文件:#include <unistd.h>
  • 声明:int unlink(const char *pathname);
  • 使用
    • 如果是软链接,删除软链接
    • 如果是硬链接,硬链接数减1,当减为0时,释放数据块和inode
    • 如果文件硬链接数为0,但有进程已打开该文件,并持有文件描述符,则等该进程关闭该文件时,kernel才真正去删除该文件
      • 利用该特性创建临时文件,先open或create创建一个文件,马上unlink此文件
      • 只是让文件具备了被释放的条件,不是真正释放,当占用该文件的所有进程使用结束关闭后才会释放
  • 示例:使用unlink函数制作一个可以自动删除的临时文件。比如看视频时的缓存文件,看完视频以后自动删除。

image.png

1.8 rename函数

  • 作用:文件重命名
  • 头文件:stdio.h
  • 函数原型:int rename(const char oldpath, const char newpath);

    2. 目录操作

    2.1 chdir 函数

  • 作用:修改当前进程的路径,相当于cd 修改当前工作目录,改变程序中打开的路径

  • 头文件:#include <unistd.h>
  • 函数原型:int chdir(const char *path);

    2.2 getcwd 函数

  • 作用:获取当前进程的工作目录

  • 头文件:#include <unistd.h>
  • 函数原型:char *getcwd(char *buf, size_t size);
  • 示例:在当前目录中运行程序,然后在进程中切换目录到上级目录并创建一个chdir.txt文件

image.png
image.png

2.3 mkdir 函数

  • 作用:创建目录
  • 头文件:#include <sys/stat.h> #include <sys/types.h>
  • 函数原型:int mkdir(const char *pathname, mode_t mode);
  • 注意:创建的目录需要有执行权限,否则无法进入目录

    2.4 rmdir 函数

  • 作用:删除一个空目录

  • 头文件:#include <unistd.h>
  • 函数原型:int rmdir(const char *pathname);

    2.5 opendir 函数

  • 作用:打开一个目录

  • 头文件:#include <sys/types.h> #include <dirent.h>
  • 函数原型:DIR *opendir(const char *name);
  • 返回值

    • 成功返回:DIR结构指针,该结构是一个内部结构,保存所打开的目录信息,作用类似于FILE结构
    • 出错返回:NULL

      2.6 readdir 函数

  • 作用:读目录

  • 头文件:#include <dirent.h>
  • 函数原型:struct dirent *readdir(DIR *dirp);
  • 返回值

image.png

  • 其中d_type:
    • DT_BLK - 块设备
    • DT_CHR - 字符设备
    • DT_DIR - 目录
    • DT_LNK - 软连接
    • DT_FIFO - 管道
    • DT_REG - 普通文件
    • DT_SOCK - 套接字
    • DT_UNKNOWN - 未知
  • -D_BSD_SOURCE 编译时添加宏定义

    2.7 closedir 函数

  • 作用:关闭目录
  • 头文件:#include <sys/types.h> #include <dirent.h>
  • 声明:int closedir(DIR *dirp);
  • 小练习:使用opendir\readdir\closedir递归获取指定目录下的普通文件个数 ```c

    include

    include

    include

    include

int getFileNum(char root) { // 打开指定目录 DIR dir = opendir(root); if(dir == NULL) { perror(“opendir”); exit(0); }

// 递归读取当前目录的普通文件数目    
int total = 0;
char path[1024] = {0};
struct dirent* ptr = NULL;
while((ptr = readdir(dir)) != NULL)
{
    // 过滤掉. 和 ..目录
    if(strcmp(ptr->d_name, ".") == 0 || strcmp(ptr->d_name, "..") == 0)
    {
        continue;
    }
    // 如果当前读取到的是普通文件
    if(ptr->d_type == DT_REG)
    {
        total ++;
    }
    // 如果当前读取到的是目录
    if(ptr->d_type == DT_DIR)
    {
        // 把目录名存储下来,递归调用读取
        sprintf(path, "%s/%s", root, ptr->d_name);
        total += getFileNum(path);
    }
}
closedir(dir);
return total;

}

int main(int argc, char* argv[]) { // 获取指定目录下的普通文件数量 int total = getFileNum(argv[1]); // 打印输出 printf(“%s has file number: %d\n”, argv[1], total); return 0; }

```

3. fcntl 函数

  • 作用:根据文件描述符来操作文件的状态
  • 头文件:#include <fcntl.h>
  • 函数原型
    • int fcntl(int fd, int cmd);
    • int fcntl(int fd, int cmd, long arg);
    • int fcntl(int fd, int cmd, struct flock *lock);
  • 具体功能:
    • 复制一个现有的描述符 — cmd
      • F_DUPFD
    • 获得/设置文件描述符标记 — cmd
      • F_GETFD
      • F_SETFD
    • 获得/设置文件状态标记 — cmd
      • F_GETFL——获取文件状态标记时,arg=0
        • 只读打开 O_RDONLY
        • 只写打开 O_WRONLY
        • 读写打开 O_RDWR
        • 执行打开 O_EXEC
        • 搜索打开目录 O_SEARCH
        • 追加写 O_APPEND
        • 非阻塞模式 O_NONBLOCK
      • F_SETFL——设置文件状态标记时,arg为arg|其他标识
        • 可更改的几个标识
        • O_APPEND
        • O_NONBLOCK
    • 获得/设置异步I/O所有权 — cmd
      • F_GETOWN
      • F_SETOWN
    • 获得/设置记录锁 — cmd
      • F_GETLK
      • F_SETLK
      • F_SETLKW
  • 示例:改变已经打开的文件的属性
    • 在文件打开过程中修改读写权限,如果不使用fcntl的话,必须先关闭文件,然后以新的权限打开文件。
    • 打开文件的时候权限为只读;修改文件的只读权限为追加 O_APPEND,指针会自动移到尾部,在文件尾部添加

image.png

4. dup和dup2函数

  • 作用:复制现有的文件描述符 —— 重定向文件描述符
  • 头文件:#include <unistd.h>
  • 声明:
    • int dup(int oldfd);
    • int dup2(int oldfd, int newfd);
  • 使用:

    • dup
      • 返回的是文件描述符表中没有占用的最小的文件描述符
    • dup2
      • old 和new不是同一个文件描述符,如果new是一个被打开的文件描述符,在拷贝前先关闭new
      • old和new是同一个文件描述符,不会关掉new,直接返回old

        5. 解决gcc编译过程中c99语法报错的问题

  • ~/.bashrc

  • alias gcc='gcc -std=gnu99'

    6. 索引节点inode

    保存的其实是实际的数据的一些信息,这些信息称为“元数据”(也就是对文件属性的描述)。 例如:文件大小,设备标识符,用户标识符,用户组标识符,文件模式,扩展属性,文件读取或修改的时间戳,链接数量,指向存储该内容的磁盘区块的指针,文件分类等等。

  • 注意数据分成:元数据+数据本身

  • 注意inode怎样生成的:每个inode节点的大小,一般是128字节或256字节。inode节点的总数,在格式化时就给定(现代OS可以动态变化),一般每2KB就设置一个inode。一般文件系统中很少有文件小于2KB的,所以预定按照2KB分,一般inode是用不完的。所以inode在文件系统安装的时候会有一个默认数量,后期会根据实际的需要发生变化。
  • 注意inode号:inode号是唯一的,表示不同的文件。其实在Linux内部的时候,访问文件都是通过inode号来进行的,所谓文件名仅仅是给用户容易使用的。当我们打开一个文件的时候,首先,系统找到这个文件名对应的inode号;然后,通过inode号,得到inode信息,最后,由inode找到文件数据所在的block,现在可以处理文件数据了。
  • inode和文件的关系:当创建一个文件的时候,就给文件分配了一个inode。一个inode只对应一个实际文件,一个文件也会只有一个inode。inodes最大数量就是文件的最大数量。
  • FILE* fp = open(“file”);