Linux系统编程(三)—— 文件编程(1)目录和文件

本文介绍了Linux系统中与文件和命令相关的知识,包括自定义实现ls命令、文件属性的获取(stat)、文件权限管理(umask、chmod)、文件系统(FAT、UFS)、链接(硬链接和符号链接)以及目录操作。重点讨论了命令的格式、文件大小计算、空洞文件的创建、文件权限的位图表示和目录的特殊权限位(粘住位)等概念。
摘要由CSDN通过智能技术生成

3.1 目录和文件

  • 贯穿始终的例子:做一个类似 ls 命令的实现。如myls

1、命令

  • (1)一个命令的格式:cmd --长格式 -短格式 非选项的传参
    比如 ls --allls -a,这两个结果是一样的:
    在这里插入图片描述

  • (2)为什么短格式可以,还要存在长格式呢?

    可能两个单词的首字母什么的会相同。或者两个单词的缩写撞在了一起。

  • (3)如果要创建一个名字为 -a 的文件,应该如何做?

    在这里插入图片描述
    解决办法:
    1)touch -- -a 命令后面加上两个减号,表示当前的命令结束;

    在这里插入图片描述

    2)touch ./-a 将路径写上,就不会被认作命令参数了。

    在这里插入图片描述

    上述两种方法,对于删除 -a 这个文件也同样适用。

  • (3)注意:ls -lls -n的区别:前者显示用户ID ,后者显示用户NAME

    在这里插入图片描述

  • (4)/etc/passwd/etc/group文件

    1)/etc/passwd文件,用户名 :group名 :用户ID :groupID
    在这里插入图片描述
    2)/etc/group文件, 用户名:组名 :组号
    在这里插入图片描述

2、如何获取文件属性:stat (系统调用)

在这里插入图片描述

  • 注意:这三个函数的格式,在linux当中,很多函数的封装是沿用这样的规律的,很多命名规则都是这样的
    (1)stat : 通过名字做这件事
    (2)fstat : 通过文件描述符来做这件事情
    (3)lstat : lstat 和 stat 在参数和返回上都一样,但是他们在link的处理上是不同的
    (stat面对符号链接文件时获取的是所指向文件的属性;而 lstat 面对符号链接是获取的是符号链接文件属性)

  • stat 结构体长这样,包含文件的全部内容:

    在这里插入图片描述
    在这里插入图片描述

  • 同时stat 也是一个命令:用来显示一个文件的属性, 所以stat这个命令是用stat这个函数封装出来的。
    在这里插入图片描述

  • 例子: 显示一个文件的大小:

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

static int flen(const char *fname) // static表示这个函数禁止外部扩展
{
    struct stat statres;
    if(stat(fname, &statres) < 0)
    {   
        perror("stat");
        exit(1);
    }   
    return statres.st_size;
}

int main(int argc, char **argv)
{
    if(argc < 2)
    {   
        fprintf(stderr, "Usage ... \n");
        exit(1);
    }   
    printf("%d \n", flen(argv[1]));
    exit(0);
}

运行结果:
在这里插入图片描述

  • 注意:
    实际上,windows中,一个文件的大小就是所占的磁盘的大小,但是在linux中,并不是这样的,一个文件所占的大小是由这个文件所占用的block大小和block数量决定的block是指一个文件所占用的块数, 永远指的是扇区数

3、空洞文件

我们做一个非常大的文件,但是占用磁盘空间很小,甚至不占用:

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

int main(int argc, char **argv)
{
    int fd; 
    if(argc < 2)
    {   
        fprintf(stderr, "Usage ...\n");
        exit(-1);
    }   

    fd = open(argv[1], O_WRONLY|O_CREAT|O_TRUNC, 0600);
    if(fd < 0)
    {   
        perror("open");
        exit(-1);
    }
    
    // 5G, 将文件尾指针一下子扯过去
    lseek(fd, 5LL * 1024LL * 1024LL * 1024LL - 1LL, SEEK_SET);

	// 使用一次系统调用,如果没有这次系统调用,当前文件是不占用空间的
    write(fd, "", 1);              
    close(fd);
    exit(0);
}

运行这个文件,然后得到一个大文件,可以看到,虽然这个文件显示的是5G大小,但是它所占磁盘空间才8k, 很小很小。
在这里插入图片描述
然后cp 拷贝一下这个文件,你会发现拷贝以后的文件所占磁盘大小尾0, 为什么呢??

因为cp在拷贝的时候,会判断这个文件是否是空洞文件,如果中间有空字符,它会记住空字符的长度,但是不会存这个空字符,所以它拷贝以后的这个文件是0字节。

4、文件属性与访问权限问题

  • 当执行命令ll的时候,如下: (红框框中的内容是文件的权限信息):
    在这里插入图片描述

  • 文件的权限信息包括:(文件的权限信息存在于 st_mode 当中,st_mode的存放形式是以位图的形式存放的, 是16位的一个整型数。

  • 为什么是16位?
    (1)基本位是9位(owner读写执行权限 (421)+ group读写执行权限(421)+other读写执行权限(421));
    (2)加另外3位(粘住位,set groupID位,set userID位);
    (3)文件类型是7类,用二进制表示使用3位;

    =>加一起是15位,但是没有15位的整型数,所以是使用16位的整型数表示的。

  • st_mode存了两种类型:

    (1)文件类型(分为dcb-lsp七种文件类型)
    d——目录;
    c——字符设备文件;
    b——块设备文件;
    -——常规文件;
    l——符号链接文件;
    s——网络套接字socket文件;
    p——管道文件

    (2)文件权限(当前用户owner, 当前组用户group, 其他用户other)

  • 注意: st_mode是一个16位的位图,用于表示文件类型、文件访问权限、特殊权限位。

5、umask

  • umask是一条命令,用来查看当前umask的值是多少,也可以更改当前umask的值:
    在这里插入图片描述

  • 创建文件的话,权限会满足 0666& ~umask 的表达式。这种机制的存在就是为了防止产生权限过松的文件

  • umask还是 一个系统调用函数,如果在进程当中想要设置这个umask的值的化,可以调用umask这个函数, 如下:
    在这里插入图片描述

6、文件权限的更改与管理 :chmod,fchmod

  • chmod 当命令时的作用是在终端上更改文件权限:
    在这里插入图片描述
    可以看到,我们将 test.c 的other权限中,加上了 w 可读的权限

  • 由于 test.c 是没有执行权限的,然后我们使用 chmod a+x test.c,给他所有用户加上可执行权限。a+x 表示的是所有用户有可执行权限,所以我们还可以使用 u+xg+x, o+x
    在这里插入图片描述

  • 如果在一个进程当中操作一个文件的时候,需要临时改变一个文件的权限信息,就需要 chmod( ) 函数与 fchmod( ) 函数, 这个是两个个系统调用函数。(前者操纵的是文件路径名,后者操纵的是文件描述符)
    在这里插入图片描述

7、粘住位( t 位)

  • t 位原始的功能:给某一个当前二进制命令设置t位,设置t位的作用是把某一个命令的使用痕迹进行保留,为的是下次再装载这个模块的时候比较快。通俗的讲就是:在内存当中保留它的使用痕迹,下次装载会比较快。通常是给一个可执行的命令进行设计。
  • 现如今这个设计无所谓了,因为有了page的设计,本来使用的内存块就会留在内存当中,所以这个 t 位在慢慢的淡化,现在常用于给目录设置 t 位。
  • 目前目录设置t位的是 /tmp/
    在这里插入图片描述给这个目录加上t位以后,各个用户对于目录的操作,以及对目录下的文件的操作就比较特殊化了。

8、文件系统:FAT,UFS

  • 文件系统:实际上是文件或者数据的存储格式问题。归根结底是为了帮助我们存储或管理文件。
  • 比如说:钱存在中国银行叫人民币,存在美国银行叫美元,但是钱是一样的。数据也是一样,只是存在在了不同的文件系统当中。

(1)FAT文件系统

  • FAT的实质是一个静态存储的单链表,, 具体的实际结构如下:
struct{
	int next[N];
	char data[N][data_size];
}

正是因为N的存在,FAT存储的文件是有限的,不能超过N个数据块。

  • 拿到windows的时候,首先第一件事是要分区,为什么要分区,因为那个时候的FAT系统承载能力有限,过多的文件或者很大的文件当前承载不了,所以说要分区。

  • FAT文件系统最大的缺陷就是使用了静态单链表,单链表最大的缺陷是一个走向,指针不能往回指。如今FAT系统还是在使用的,比如说小u盘, 小SD卡,为了轻量级。

  • 360内存清理: 用一个进程不停的吃内存吃内存(要内存),在抢资源,留在内存中的常用的数据块就在往swap交换分区去挪,当进程强占资源到一定程度的时候,就会立马结束,然后给用户提示,内存清理成功,实际上全都挤到交换分区中去了。当你用的时候,交换到swap分区中的数据又会回来,在自己逗自己玩(火绒 yyds)。

  • 注意: 当内存换出率和换入率都在直线上升的时候,才是内存吃紧的时候。

  • FAT系统特别惧怕大文件

(2)UFS文件系统

在这里插入图片描述

  • inode结构体介绍
    在这里插入图片描述

  • 如何知道哪个 inode 用了,哪个 inode 没有用?

    这里其实用到了 inode 位图,有多少个 inode 就有多少个 inode 位图,这俩一一对应着。如果 inode 用了,则与其对应的 inode 位图为 1, 如果没用,则为 0 。(数据块和数据块位图也是一样的道理)

9、硬链接,符号链接

(1)硬链接:ln

  • 通过:ln 源文件 链接文件的命令格式,可以产生硬链接

  • 本质上有点类似两个指针指向同一块区域。
    在这里插入图片描述
    这里通过 stat 命令可以查看到文件 big.c 的inode号以及硬链接数目。再使用命令:ln big.c big_link.c,结果如下:
    在这里插入图片描述
    源文件的硬链接数目变成了2,再查看生成的 big_link.c 的文件属性:
    在这里插入图片描述
    可以看到 big.c 与 big_link.c 这两个文件的 inode 号其实是一样的,所以他俩其实是指向同一个文件(这就有点类似两个指针同时指向同一块内存空间一样)。

  • 删掉源文件,硬链接文件依然有效:
    在这里插入图片描述

(2)符号链接:ln -s

  • 没有软连接,只有符号链接,不要搞混了

  • 通过:ln -s 源文件 符号链接文件格式,可以产生符号链接文件

  • 符号链接有点类似 Windows 上面的快捷方式。生成的符号链接文件和源文件其实是两个东西(文件属性不一样)
    在这里插入图片描述
    可以看到生成 test_s_link.c软链接文件后,源文件 test.c 的文件属性并没有改变,Links数目还是1:
    在这里插入图片描述
    再看 test_s_link.c 文件属性:
    在这里插入图片描述
    可以看到 test_s_link.c 的 inode 号与源文件已经不一样了。再注意:该符号链接文件的 Size 大小,其实就是源文件 名字的大小(这里是6字节)。

  • 删掉源文件,软链接文件就无效了:
    在这里插入图片描述

  • 文件权限信息开通表示的是文件类型,l表示的是链接文件(本质就是符号链接文件)
    在这里插入图片描述

(3)系统调用函数:link( )

  • 命令ln就是由该系统调用函数 link( ) 封装出来的
    在这里插入图片描述

(4)系统调用函数:unlink( )

  • 注意:利用rm删除一个文件,实质是使该文件的硬链接数为0,没有任何进程线程打开或引用该文件,此时那块数据空间才会被释放掉。(只要还有内容引用到那数据块,那就还没有删掉,即便硬链接数目已经为0了)

  • 系统调用函数:unlink( )
    在这里插入图片描述注意:使用 unlink( )可以特别容易创建匿名文件。比如,首先利用 open( )函数产生一个文件,获得一个文件描述符 fd,然后马上 unlink( )掉这个文件。但是,该文件并没有立即被删除掉,只有当 close(fd) 后,该文件才会被彻底删除。

(5)标准库函数:remove( )

  • 该函数位于 man 手册第三章,rm命令就是由该函数封装而来的
    在这里插入图片描述

(6)系统调用函数:rename( )

  • 该函数位于 man 手册第二章,mv命令就是由该函数封装而来的
    在这里插入图片描述

(7)硬链接与符号链接的特点

  • 硬链接与目录项是同义词,且建立硬链接有限制:不能给分区建立,不能给目录建立。
  • 符号链接的优点:可以跨分区,可以给目录建立

10、utime

  • 利用 ll 命令可以看到的一个时间,指的是:MTIME
    在这里插入图片描述

系统调用函数:utime( )

  • 系统调用函数:utime( ),用于修改最后一次读和修改的时间(可以修改ATIME和MTIME)
    在这里插入图片描述

11、目录的创建和销毁

(1)系统调用函数:mkdir( )

  • 命令 mkdir 就是利用mkdir( )来封装的,用于创建一个目录
    在这里插入图片描述

(2)系统调用函数:rmdir( )

  • 命令 rmdir 就是利用rmdir( )来封装的,用于删除一个目录(只能是空目录)。对于非空目录,要用递归的方式来删除
    在这里插入图片描述

12、更改当前工作路径:chdir( )

  • 命令 cd 就是由系统调用函数 chdir( ) 封装而来的
    在这里插入图片描述

13、获取当前工作路径:getcwd( )

  • 命令 pwd 就是由标准库函数 getcwd( ) 封装而来的
    在这里插入图片描述

14、分析目录与读取目录内容

(1)认识 argc 与 argv

  • argc —— 表示运行程序时,传给主函数的命令行参数个数;
  • argv[ ] —— 指针数组,存指向放传给主函数的命令行参数的地址。

例如:

#include <stdio.h>

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

    for (int i = 0; i < argc; i++)
        puts(argv[i]);

    return 0;
}

运行结果:
在这里插入图片描述
在这里插入图片描述

(2)glob( )函数:解析模式或通配符

  • glob( ) 函数用于文件系统中路径名称的模式匹配,globfree( )用来释放空间
    在这里插入图片描述

  • 参数:
    pattern——要分析的路径;
    flags——选择匹配模式,如是否排序,或者在函数第二次调用时,是否将匹配的内容追加到pglob中,不进行特殊模式匹配则写0;
    errfunc——glob函数执行出错会执行的函数,出错的路径会回填到epath中,出错的原因回填到eerrno中。如不关注错误可设置为NULL
    pglob —— 解析出来的结果放在这个参数里

  • 返回值:成功 0 失败 非0

  • 这里可以留意一下 glob_t 结构体:
    在这里插入图片描述
    gl_pathc 与 gl_pathv 有点类似于 argc 和 argv

  • 案例:

#include <stdio.h>
#include <stdlib.h>
#include <glob.h>


#define PAT "/home/mutouren/tmp/test*"

int main() {
    
    glob_t globres;

    int err = glob(PAT, 0, NULL, &globres);
    if (err) {
        printf("Error code = %d\n", err);
        exit(1);
    }   

    for (int i = 0; i < globres.gl_pathc; i++)
        puts(globres.gl_pathv[i]);

	globfree(&globres); // 释放空间

    exit(0);
}

这段代码可以打印出:/home/mutouren/tmp/ 目录下所有以 test开头的文件
在这里插入图片描述

(3)opendir( ) 与 closedir( ):打开或关闭目录文件

打开目录文件

在这里插入图片描述
关闭目录文件 (从这里可以看出,生成的 DIR 类型的数据实在堆上的)

在这里插入图片描述

(4)readdir( ):读取目录文件

  • 用于读取一个目录文件,保存在一个 dirent 结构体变量当中
    在这里插入图片描述
    dirent结构体的定义:
    在这里插入图片描述
  • 例子:打印 /home/mutouren/tmp 目录下的全部文件
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>

#define PAT "/home/mutouren/tmp"

int main() {
    DIR* dp; 
    struct dirent* cur;

    dp = opendir(PAT);
    if (NULL == dp) {
        perror("opendir()");
        exit(1);
    }   

    while ((cur = readdir(dp)) != NULL)
        puts(cur->d_name);

    closedir(dp);

    return 0;
}

运行结果:
在这里插入图片描述

(5)du 命令

  • du 命令的作用:会显示指定的目录或文件所占用的磁盘空间。
    在这里插入图片描述
    这里 test.c 所占磁盘空间为 4K,也等于stat出的文件属性中的 blocks 数目除以2
    在这里插入图片描述

参考资料:请多多支持原博主

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值