Linux 文件IO

news2024/11/17 23:43:56

目录

linux下的文件分类:

文件描述符原理:(底层原理,可跳过)

虚拟文件系统:

内存中的inode与磁盘中的inode

open函数

函数原型:

形参列表:

代码:

close函数

errno函数

read函数

write函数

lseek函数

文件和目录:

stat函数

fstat函数

文件类型

access函数

符号链接(软连接):

硬链接:

unlink函数

文件的时间

mkdir函数

读目录  


在Linux下的一切接文件(Everything is file in Unix/Linux)。IEEE制定了一套POSIX标准,用于统一Unix系统对文件的接口操作。

linux下的文件分类:

   1、在Linux终端我们可以使用ls -|命令来查看当前目录下的文件信息,在显示文件的属性通常会以如下形式显示:

eg:(终端快捷键:Ctrl+alt+T)

2、在Linux中常见的文件类型有七种:(上图的第一个字母)

文件描述符原理:(底层原理,可跳过)

1、对于Linux内核而言,所有打开的文件都是通过文件描述符引用。

2、文件描述符是一个非负的整数,当使用open函数打开或者使用creat函数创建一个文件的时候,内核向用户进程返回一个文件描述符。当读或者写一个文件的时,该文件的文件描述符将作为参数传递给read或者write函数。

3、一个进程的文件描述符从0开始,并且0/1/2分别代表表标准输入STDIN_FILENO,标准输出STDOUT_FILENO和标准出错STDERR_FILENO。

4、在C语言中面对复杂的事物的时候都会有结构体这个方法,同样每个进程在Linux内核中都有一个task_struct结构体来维护这些进程的相关信息,称之为进程描述符,在操作系统理论中称之为PCB(process control block)。task_struct结构体中有个指针指向struct files struct结构体,称之为文件描述符表,,其中每个表项包含一个指向已经打开的文件指针。

操作理解:

我们首先在Linux终端中进入目录:

命令:cd + 空格 /usr/src/linux-headers-5.11.0-46-generic/include

打开linux/sched.h文件,找到struct task struct结构体,其中有一个成员变量files,是一个struct files_struct*类型的指针变量,描述该进程中被打开的文件.

命令:gedit linux/sched.h

如果显示没有该文件或目录则是么有下载linux内核源码

执行命令:
sudo apt-get install linux-source

ctrl+f查找task_struct  该结构体

因为task_struct结构体中有个指针指向struct files_struct结构体

该files指针就是进程中的文件描述符表

接下来我们进入到Linux/fdtable.h

最下边的数组保存的是进程中打开的所有文件

发现struct files_struct中有一个成员变量fd_array 是个指针数组,数组中的每个元素是struct file*进程中每打开一个文件就用一个struct file结构体来描述,fd array数组中就保存了指向用来描述被打开的文件的struct_file结构体,文件描述符其实就是fd array数组的一个下标!!!

下边我们打开linux/fs.h找到struct file的定义

struct file 结构体中有一个成员变量f_op的数据类型是struct file_operation *,也是在linux/fs.h中被定义的:

这个结构体内定义了一些操作文件的函数指针变量。

struct file 结构体中还有另外一个成员变量f_path,是struct path类型该结构体在linux/path.h中被定义

struct path 结构体中有成员变量dentry,是struct dentry*类型的。

struct dentry 目录项:目录项描述的是文件的逻辑属性,只存在一内存当中,并没有实际对应的磁盘上的描述,更确切的说是存在于内存的目录项缓存,为了提高查找性能而设计。注意不管是文件夹还是最终的文件都是属于目录项,所有的目录项在一起构成了一颗庞大的目录树,。例如: open一个文件/home/xxx/yyy.txt,那么/、home、xxx、yyytxt都是一个目录项,VFS在查找的时候,根据层一层的目录项找到对应的每个目录项的inode,那么沿着目录项进行操作就可以找到最终的文件。
注意:目录也是一种文件(所以也存在对应的inode)。打开目录,实际上就是打开目录文件。

该结构体在linux/dcache.h中定义,

查看的话同样在上述终端中输入 :gedit linux/dcache.h

struct dentry 结构体是描述这个文件所在的目录项信息,以及inode节点信息(struct inode *d_inode)

在查看函数的时候注意

注意:unsigned long i_ino;//inode号

inode 号是VFS中唯一标识一个文件编号

5、从上述描述可以知道如下结构图

虚拟文件系统:

1、VFS(Virtual Filesystem Switch) 称为虚拟文件系统或虚拟文件系统转换,是一个内核软件层,在具体的文件系统之上抽象的一层,用来处理与Posix文件系统相关的所有调用,表现为能够给各种文件系统提供一个通用的接口,使上层的应用程序能够使用通用的接口访问不同文件系统,同时也为不同文件系统的通信提供了媒介。

2、我们知道文件系统的种类有很多。除了Linux标准的文件系统Ext2/Ext3/Ext4外,还有很多种文件系统。linux通过叫做VFS的中间层对这些文件系统提供了完美的支持。在大部分情况下,用户通过libc和kernel的VFS交互,不需要关心底层文件系统的具体实现。

3、vfs就是对各种文件系统的一个抽象,它为各种文件系统提供了一个通用的接。

4、VFS具有庞大的体系有兴趣的可以去问一下度娘啦。

内存中的inode与磁盘中的inode

1、我们把内存中的inode结构称为VFS inode,而文件系统以EXT2为代表,把EXT2 inode作为磁盘上的inode代表。

2、内存中的inode结构:VFS inode 包含文件访问权限,属主,组,大小,生成时间,访问时间,最后修改信息等。 它是linux管理文件系统的最基本单位,也是文件系统连接任何子目录、文件的桥梁。inode结构中的静态信息取自物理设备上的文件系统,由文件系统指定的函数填写,它只存在于内存中,可以通过inode缓存访问。虽然每个文件都有相应的inode结点,但是只有在需要的时候系统才会在内存中为其建立相应的inode数据结构,建立的inode结构将形成一个链表,我们可以通过遍历这个链表去得到我们需要的文件结点VFS 也为已分配的inode构造缓存和hash table,以提高系统的性能。inode结构中的struct inode_operations *iop为我们提供一个inode操作列表,通过这个列表提供的函数我们可以对VFSinode结点进行各种操作。每个inode结构都有一个i结点号i_ino,在同一文件系统中每一个i结点号都是唯一的。

3、磁盘上的inode: EXT2通过使用inode来定义文件系统的结构以及描述系统中每个文件的管理信息,每个文件都有一个inode且只有一个,即使文件中没有数据,其索引结点也是存在的。每个文件用一个单独的Ext2 inode结构来描述,而且每一个inode都有唯一的标志号。Ext2 inode为内存中的inode结构提供了文件的基本信息,随着内存中inode结构的变化,系统也将更新Ext2 inode中相应的内容。Ext2 inode对应的是Ext2 inode结构。

4、什么是inode?

理解inode,要从文件储存说起,
文件储存在硬盘上,硬盘的最小存储单位叫做”扇区”(Sector)。每个扇区储存512字节(相当于0.5KB)。
操作系统读取硬盘的时候,不会一个个扇区地读取,这样效率太低,而是一次性连续读取多个扇区,即一次性读取一个"块”(block) 。这种由多个扇区组成的”块”,是文件存取的最小单位。"块”的大小,最常见的是4KB,即连续八个sector组成一个block。
文件数据都储存在“块“中,那么很显然,我们还必须找到一个地方储存文件的元信息,比如文件的创建者、文件的创建日期、文件的大小等等。这种储存文件元信息的区域就叫做inode,中文译名为"索引节点”。
每一个文件都有对应的inode,里面包含了与该文件有关的一些信息
5、磁盘上inode的大小
inode也会消耗硬盘空间,所以硬盘格式化的时候,操作系统自动将硬盘分成两个区域。一个是数据区,存放文件数据;另一个是inode区 (inode table) ,存放inode所包含的信息。
每个inode节点的大小,一般是128字节或256字节,inode节点的总数,在格式化时就给定,一般是每1KB或每2KB就设置一个inode。假定在一块1GB的硬盘中,每个inode节点的大小为128字节,每1KB就设置一个inode,那么inode table的大小就会达到128MB,占整块硬盘的12.8%.

6、文件查找

open函数

函数原型:

调用open函数可以打开或者创建一个文件。我们可以在ubuntu的终端中输入命令“man 2 open”查看open的函数帮助手册

命令:man 2 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);

函数解释:

形参列表:

1、形参pathname是要打开或者创建文件的名字,flags参数可以用来说明此函数的多个选项。

2、flag参数:(只列出部分)

  • O_APPEND 以追加的方式打开
  • O_RDWR 以可读可写的方式打开
  • O_RDONLY 以只读的方式打开
  • O_WRONLY 以只写的方式打开
  • O_CREAT 如果文件不存在则创建
  • O_TRUNC 如果文件存在则截短为零

注意:至少要使用 O_RDONLY, O_WRONLY, or O_RDWR 其中一个,如果还有其他的选项例如O_APPEND(追加方式),O_CREAT(如果文件不存在则创建),O_TRUNC(如果文件存在则截短为0)等,则和之前的选项使用或运算,例如:open(file, O_RDWR | O_APPEND)

3、mode参数:

如果flags的选项中有 O_CREAT, 则需要设置mode参数,用来设置被创建的新文件的权限。
该权限可以用八进制的数值表示,例如: open(file, O_RDWR | O_CREAT, 0755)O_APPEND 以追加方式打开。

解释0755

三个框分别代表文件的所有者对文件操作权限,

这个是八进制的表达方式,第一个零代表是8进制,三个一组,如果有权限则给1,0775就代表 111 101 101(文件权限rwx r-x r-x)

代码:

通过文件共享在win在win线创建工程到linux中编译

(具体操作方式参考这篇博客http://t.csdnimg.cn/rQoB6)

进入到共享文件当中之后右键进入到终端

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

int main() {
    int fd;//file discriptor 文件描述符

    fd = open("1.c",O_RDWR);//如果需要打开的文件不存在则open失败
    printf("fd: %d\n", fd);
    if(-1 == fd)
    {
        perror("open error:");
        return 0;
    }
    return  0;
}

输入命令:gcc main.c -o main

生成了一个新的文件main,继续输入命令

命令:./main

执行main文件

返回-1则说明文件打开失败,没有该文件

在文件中新建一个1.c再次编译运行

修改代码再打开一次文件

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

int main() {
    int fd;//file discriptor 文件描述符

    fd = open("1.c",O_RDWR);//如果需要打开的文件不存在则open失败
    printf("fd: %d\n", fd);
    if(-1 == fd)
    {
        perror("open error:");
        return 0;
    }
    fd = open("1.c",O_RDWR);//如果需要打开的文件不存在则open失败
    printf("fd: %d\n", fd);
    if(-1 == fd)
    {
        perror("open error:");
        return 0;
    }
    return  0;
}

编译运行

文件描述符的大小随依次打开的次数增加。

close函数

功能:关闭指定文件 

必填头文件:#include <unistd.h>

函数原型:int close(int fd);

fd:需要关闭的文件的文件描述符

  • 成功返回0
  • 失败返回-1,errno被设置

示例代码:

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

int main(int argm,int** argv) {
    int fd;//file discriptor 文件描述符
    fd = open("2.c",O_RDWR|O_CREAT,0664);//如果需要打开的文件不存在creat
    printf("fd: %d\n", fd);
    if(-1 == fd)
    {
        perror("open error:");
    }

    fd = open("2.c",O_RDWR);
    printf("fd: %d\n", fd);
    if(-1 == fd)
    {
        perror("open error:");
    }
    fd=close(fd);

    printf("fd: %d\n", fd);

    return  0;
}

效果图:

注意:关闭文件描述符可以在此被使用

errno函数

1、errno是在“errno.h”中申明的一个进程全局变量,当在程序中调用系统函数失败(返回2(%d))的时候会根据失败的原因自动设置errno的值,成打开文件返回0(%d)。

2、可是使用perror函数打印出错的原因

3、errno被当做一个下标访问出错信息的元素

当文件没打开的时候(效果显示)

printf("errno : %d",errno);

perror(“errMsg:”);

当close负数的是(效果显示)

close(-100);

printf("errno : %d",errno);

perror(“errMsg:”);

疑问:为什么可以打印不同的出错原因???

errno的实质是字符指针数组,其本质是数组,数组内的元素为字符指针

例如:char *errMsgArray[4];

不同的errno返回值代表不同的下标,且分别指向不同的字符串(编译调试的时候可以使用该方式,根据不同的下标返回不同的错误类型)

read函数

功能:从指定文件中读取数据

必填头文件:#include <unistd.h>

函数原型:ssize_t read(int fd, void *buf, size_t count);

  • fd:读取文件的文件描述符
  • buf:读取数据在内存空间中存储的地址
  • count:期待读取数据的最大字数

返回值:

  • 文件实际读取的字节数
  • 成功返回实际读取的字节数,实际读取到的字节数可能比希望读取的count值要小
  • 如果读取到文件的末尾返回0

代码历程:

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

int main() {
    //打开文件
    int fd;//文件描述符
    fd = open("1.c",O_RDWR);
    if(-1 == fd)
    {
        perror("open file error");
        return 0;
    }

    //读取数据
    char data[100] = {0};
    char *p_data;

    p_data = (char *)malloc(100);
    memset(p_data,0,100);

    int n;
#if 0
    //从fd这个文件描述符所代表的文件中最多读取99个字节到p_data这个指针所指向的栈空间中
    n=read(fd,p_data,99);
    printf("n:%d\r\n",n);//代表真是读到的字节数
    printf("p_data:\r\n%s",p_data);//读取到的数据
#endif
#if 1 //多次读取文件的内容,知道读取到文件的末尾
while(n=read(fd,p_data,99))
{
    printf("n:%d\r\n",n);//代表真是读到的字节数
//    printf("p_data:\r\n%s",p_data);//读取到的数据
    //堆空间用完之后要先对之前读取的内容清空掉
    memset(p_data,0,100);
}

#endif
//

    return 0;
}

一次读取文件的效果显示:

多次读取文件直到结束的效果显示:

write函数

功能:向一个文件中写入数据

必填头文件:#include <unistd.h>

函数原型:ssize_t write(int fd, const void *buf, size_t count);

  • fd:读取文件的文件描述符
  • buf:写入的数据在内存空间的存储地址
  • count:期待写入数据的最大字数

返回值:

  • 成功返回写入的字节数
  • 失败返回0

示例代码:

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

int main() {
    //打开文件
    int fd;//文件描述符
//    fd = open("1.c",O_RDWR);//仅仅是可读可写的方式打开
    fd = open("1.c",O_RDWR | O_APPEND);//可读可写并且追加的方式打开
    if(-1 == fd)
    {
        perror("open file error");
        return 0;
    }

    char* buf = "hello world";
    //将buf这个指针指向的所有空间中的字节写入到fd指向的文件中
//    write(fd,buf,strlen(buf));

//如果不是以追加(O_APPEND)的方式打开文件,那么将会从文件的开始写入数据
//之前写入的数据将会被覆盖掉
    write(fd,"abc",3);
    close(fd);


    return 0;
}

注意:要先确认文件是以什么样的方式打开的,若是不追加则在文件开头从新写入,并且覆盖掉新的内容,若是以追加的方式打开的则直接在原来的内容后边添加新的内容

lseek函数

功能:类似于光标的位置

  1. ,每个打开的文件都有一个与其相关联的“当前文件偏移量”,通常是一个非负整数,用以度量从文件开始处计算字节数。通常文件的读写操作都是从当前文件的偏移量处开始,并使偏移量增加所读写的字节数。
  2. 按系统默认情况,当打开一个文件的时候除非指定文件O_APPEND(追加选项),否则该偏移量被设置为0;
  3. 可是使用lseek显示为一个打开的文件设置偏移量。

必填头文件:

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

函数原型:off_t lseek(int fd, off_t offset, int whence);

  • fd:读取文件的文件描述符
  • offset:偏移量
  • whence:可以是SEEK_SET(文件指针的开始),SEEK_CUR(文件指针当前位置),SEEK_END(文件指针尾)

返回值:

  • 成功返回距离文件开头位置的偏移量
  • 失败返回-1,errno被设置

示例代码:

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

int main() {
    //打开文件
    int fd;//文件描述符
//    fd = open("1.c",O_RDWR);//仅仅是可读可写的方式打开
    fd = open("1.c",O_RDWR | O_APPEND);//可读可写并且追加的方式打开
    if(-1 == fd)
    {
        perror("open file error");
        return 0;
    }

    int n;
    n = lseek(fd,0,SEEK_SET);
    printf("n:%d\r\n",n);

    //从光标的当前位置设置偏移0个字节,目的想知道当前光标距离文件开头的偏移量
    n = lseek(fd,0,SEEK_CUR);
//  文件虽然以O_APPEND方式打开,其实光标初始位置还在文件的开头,只是我们写入数据的时候会自动移动到文件的末尾
    printf("n:%d\r\n",n);

    //将光标移动到文件的末尾,返回值就就是文件中字符的个数
    n = lseek(fd,0,SEEK_END);
    printf("n:%d\r\n",n);
    
    return 0;
}

代码效果显示:

文件和目录:

前边几个函数是对文本的基本操作,例如文件的打开,读写,文件的定位等等。下边的函数或者关联词是对文件的属性或者类型以及特殊文件的学习。

stat函数

功能:根据文件名获取文件的信息

必填头文件:

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

函数原型:int stat(const char *pathname, struct stat *statbuf);

  • pathname:需要查看的文件的名字
  • statbuf:保存文件信息结构体指针

返回值:

  • 成功返回0
  • 失败返回-1,errno被设置

示例代码:

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

int main() {
    //打开文件
    int fd;//文件描述符
    struct stat st;//定义一个结构体将文件信息放入这个结构体当中
    fd = stat("1.c",&st);
    if(-1 == fd)
    {
        perror("open file error");
        return 0;
    }
    printf("dev:%ld\r\n",st.st_dev);
    printf("ino:%lu\r\n",st.st_ino);
    printf("mode:%hu\r\n",st.st_mode);
    printf("size:%lu\r\n",st.st_size);


    return 0;
}

效果显示:

fstat函数

功能:根据文件描述符获取文件的信息

必填头文件:

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

函数原型: int fstat(int fd, struct stat *statbuf);

  • fd:需要查看的文件的文件描述符
  • statbuf:保存文件信息的结构体指针

返回值:

  • 成功返回0
  • 失败返回-1,errno被设置

示例代码:

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

int main() {
    //打开文件
    int fd;//文件描述符
    struct stat st;
    fd = open("1.c",O_RDWR);
    if(-1 == fd)
    {
        perror("open file error");
        return 0;
    }

int ret;
    ret = fstat(fd,&st);
    printf("dev:%ld\r\n",st.st_dev);
    printf("ino:%lu\r\n",st.st_ino);
    printf("mode:%hu\r\n",st.st_mode);
    printf("size:%lu\r\n",st.st_size);


    return 0;
}

代码效果显示:

注意:fstat函数和stat函数使用起来两者没有什么差别,之前前者获取文件信息的方式是文件描述符,后者是文件名。

文件类型

  1. 在<sys/stat.h>头文件中提供了以下文件类型宏
  2. 以上的宏通过传递struct stat结构体中的st_mode成员判断文件的类型
  3. 如果文件类型判断正确则返回逻辑真,否则则为逻辑假

示例代码:

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

int main() {
    //打开文件
    int fd;//文件描述符
    struct stat st;
    fd = open("1.c",O_RDWR);
    if(-1 == fd)
    {
        perror("open file error");
        return 0;
    }

int ret;
    ret = fstat(fd,&st);
   if(S_ISREG(st.st_mode))
   {
       printf("Is Regular File\r\n");
   }


    return 0;
}

代码效果显示:

access函数

功能:对指定的文件进行权限测试

必填头文件:

   #include <unistd.h>

函数原型:int access(const char *pathname, int mode);

  • pathname:需要查看的文件的名字
  • mode:指定access的作用,取值如下:
  • F_OK值为0,判断文件是否存在
  • X_OK值为1,判断对文件是否可执行权限
  • W_OK值为2,判断对文件是否有写到权限
  • R_OK值为4,判断对文件是否有读的权限
  • 注:后三种可以使用或“|”的方式一起使用,

返回值:

  • 如果测试权限存在返回0
  • 如果测试权限不存在返回-1,errno被设置

示例代码:

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

int main() {
    //测试某个文件是否存在
    int ret;
    ret = access("1.c",F_OK);
    printf("ret:%d\r\n",ret);
    if(access("1_1.txt",F_OK))//如果文件不存在返回-1(非0)
    {
//        创建一个文件
    creat("1_1.txt",0644);
    }

//    测试文件是否可写
    if(!access("1_1.txt",W_OK))
    {
        printf("write OK!\r\n");
    }

//    测试文件是否可读
    if(!access("1_1.txt",R_OK))
    {
        printf("read OK!\r\n");
    }

    //    测试文件是否可执行
    if(!access("1_1.txt",X_OK))
    {
        printf("exec OK!\r\n");
    }

    return 0;
}

执行效果图:

符号链接(软连接):

1、符号链接也叫做软连接类似于windows系统的快捷方式,它实际上是一个特殊的文件,其中保存的是另一个文件的位置信息

2、创建符号链接的命令:

ln -s 目标文件 符号链接文件

例如:

ln -s 1.c link.c

效果显示:

,link.c保存的是所链接文件的位置而不是保存的链接文件的内容,这个一点从文件的大小上就可以看出来1.c为12字节,而link.c为3字节。

在其他文件中链接该路径的文件:(只需要将目标文件的路径填写正确即可)

3、符号链接文件中的内容

无法使用cat命令因为使用cat命令打开的文件是1.c所链接的文件里边的内容。

注意:如果我们直接打开软链接文件看到的是链接文件中的内容。如果需要查看软链接中的具体内容我们可以使用readlink命令。

注意:软链接文件与原文件是两个独立的文件,只是在软链接文件中保存了原文件的位置,所以我们可以通软链接文件去访问原文件。

怎么证明???

根据前边的inode可知,不同的文件有不同的inode编号只需要查看1.c和link.c的编号即可。

因为两个文件的inode的编号不一样则说明这是两个不同的文件。

4、如果删除软链接所链接的文件,软链接就会失效。

变红是因为失效了。

但是把软链接文件删掉,并不会将源文件删除。

硬链接:

1、硬链接是指通过索引节点(inode)来进行链接。

2、在Linux系统中,多个文件名指向同一个索引节点(Inode)是正常且允许的。一般这种链接就称之为硬链接。硬链接的作用之一是允许一个文件拥有多个有效路径名,这样用户就可以建立硬链接到重要的文件,防止“误删”源数据。但是硬链接只能在同一个文件系统中的文件进行链接,不能对目录进行创建。

3、硬链接的创建方式

ln 源文件 硬链接文件

例如:

ln 1.c 2.c

4、硬链接文件的验证

硬链接文件和原文件其实是同一个文件,只是文件名称不一样,使用stat命令查看文件的信息的时候inode是相同的。而且硬链接(Links的值为2 ,表示有两个文件名称1.c   2.c)

5、硬链接文件的删除

rm 1.c

删除1.c后2.c还是存在的,只是硬链接数量少了一个 

当硬链接数被减到0的时候,文件这时候也就被“删除了”,没法在文件目录中显示,但是其实文件的内容依然存储在硬盘上。

命令rm在执行删除操作的时候只是删除的硬链接操作,并没有完全删除

使用场景:

  • 对文件进行备份,(使用命令cp可以实现文件的拷贝,但是没法对文件进行同步的更新,而使用硬链接不仅实现了拷贝效果,也是实现了文件的同步更新。)

对硬链接的理解,实际上就是一个文件的别名,不管是1.c还是2.c打开的都是同一个文件

unlink函数

1、说明

执行unlink()函数并不一定会真正的删除文件,他先会检查文件系统中此文件的链接是否为1,如果不是1则说明此文件还有其他链接对象,因此只对此文件的链接数进行减一操作。若链接数为1,并且在此时没有任何进程打开该文件,此内容才会真正的被删除掉。在有进程打开此文件的情况下,则暂时不会删除,直到所有打开该文件的进程全部结束时就会被删除。

2、ulink函数原型

必要头文件:#include <unistd.h>

 int unlink(const char *pathname);

3、形参列表

  • pathname:需要删除的文件的名字

4、返回值:

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

示例代码:

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

int main() {

//打开或者创建一个函数
	int fd;//文件描述符
	fd = open("1.txt",O_CREAT | O_RDWR | O_TRUNC,0644 );
	if(-1 == fd)
	{
		perror("open error");
		return 0;
	}
	//删除文件 把文件的硬链接数-1
	unlink("1.txt");//对于临时文件最好是使用创建完之后就使用该函数准备删除文件
//如果此时删除了文件是没办法写入的,也没办法读取
	write(fd,"yixingyunfei",12);
	//此时写完数据之后光标还在末尾要想读出数据来得先把光标放到文件的开头
	lseek(fd,0,SEEK_SET);

	//读数据
	char buf[128] = {0};
	read(fd,buf,128);
	printf("buf:%s\r\n",buf);
	while(1);//是程序保持运行状态,

    return 0;
}

说明:unlink的这种特性经常被程序用来确保即使在程序崩溃时,它所创建的临时文件也不会遗留下来。进程用open或create创建一个文件,然后立刻调用unlink。因为该文件仍旧是打开的,所以不会将其内容删除。只有当进程关闭该文件或终止时,该文件的内容才被删除。

效果显示:

程序执行不结束一直在while循环里边

文件的时间

1、文件时间是Linux重要的属性之一,在Linux操作系统的文件时间属性包含三个时间:最近访问,最近更改,最近改动(状态或者属性的改动)时间。

2、可以使用 stat命令查看一个文件的三个时间。时间分别保存在stuct stat结构体的三个成员变量。

可以通过stat命令来获取三个时间戳具体使用看stat部分。

3、stat结构体中存储的时间类型为time_t,是一个从1970年1月1日0点0分0秒到对应的时间秒数。

示例代码:

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

int main() {

//打开或者创建一个函数
	int fd;//文件描述符
	fd = open("1.txt",O_CREAT | O_RDWR | O_TRUNC,0644 );
	if(-1 == fd)
	{
		perror("open error");
		return 0;
	}
	struct stat st;
	fstat(fd,&st);
	printf("%ld\r\n",st.st_atime);
    printf("%ld\r\n",st.st_mtime);
    printf("%ld\r\n",st.st_ctime);

    return 0;
}

这是一个很大的时间,并不好看。

4、使用localtime函数将time_t类型转化为时间结构体。

必要头文件:#include <time.h>

函数原型:struct tm *localtime(const time_t *timep);

timep:需要转换的数据的地址

tm结构体:

示例代码:

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

int main() {

//打开或者创建一个函数
	int fd;//文件描述符
	fd = open("1.txt",O_CREAT | O_RDWR | O_TRUNC,0644 );
	if(-1 == fd)
	{
		perror("open error");
		return 0;
	}
	struct stat st;
	fstat(fd,&st);
	printf("%ld\r\n",st.st_atime);
    printf("%ld\r\n",st.st_mtime);
    printf("%ld\r\n",st.st_ctime);

    struct tm *t;
    t=localtime(&st.st_atime);
    printf("%d-%d-%d  %d:%d:%d\r\n",t->tm_year+1900,t->tm_mon+1,t->tm_mday,t->tm_hour,t->tm_min,t->tm_sec);

    //获取当前时间的年月日时分秒
    time_t t1;
    t1 = time(NULL);
    t=localtime(&t1);
    printf("%d-%d-%d  %d:%d:%d\r\n",t->tm_year+1900,t->tm_mon+1,t->tm_mday,t->tm_hour,t->tm_min,t->tm_sec);


    return 0;
}

效果显示:

mkdir函数

1、必加头文件:

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

2、函数原型

mkdir函数可以创建一个目录。

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

3、形参列表

  • pathname:需要创建的目录的名字/路径
  • mode:目录访问权限,可以八进制的方式表示。

4、返回值:

  • 成功返回0
  • 失败返回-1,并且errno被设置

示例代码:

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

int main() {

//创建一个目录
int ret;
    ret=mkdir("test",0644);
    if(-1 == ret)
    {
        perror("mkair error:");
        return 0;
    }

    return 0;
}

代码效果:

蓝色字体即代表一个目录

读目录  

1、打开目录:opendir函数

必要头文件: 

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

函数原型:DIR *opendir(const char *name);

opendir函数打开name所指定的目录,成功返回一个指向该目录的指针,失败返回NULL,errno被设置

2、读取目录:readdir

每次只会读取一个文件

      必要头文件: #include <dirent.h>

      函数原型:struct dirent *readdir(DIR *dirp);

读取dirp所指向的目录,失败或者读取到了目录的末尾则返回NULL 

示例代码:(当我们在main目录中有一个test目录test目录下有1.c 2.c 3.c)

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

int main() {

//打开一个目录
    DIR *dir;
    dir = opendir("test");
    if(NULL == dir)
    {
        perror("opendir error");
        return 0;
    }

    //读取目录下的文件
    struct dirent *ent;
    //因为readdir函数每次只能读取一个文件,所以如果我们需要读取目录下的所有文件我们需要循环使用readir函数
    while(ent = readdir(dir))
    {
        printf("%s\r\n",ent->d_name);

    }
    return 0;
}

代码效果:

这里咱们得test目录下只有三个文件(1.c 2.c 3.c)但有注意到"."和“..”另两个文件,

这是因为在linux目录下任何文件都有两个文件"."和“..”,我们可以使用ls -la查看,这两个目录是隐藏的。

代码示例:

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

int main() {

//打开一个目录
    DIR *dir;
    dir = opendir("test");
    if(NULL == dir)
    {
        perror("opendir error");
        return 0;
    }

    //读取目录下的文件
    struct dirent *ent;
    //因为readdir函数每次只能读取一个文件,所以如果我们需要读取目录下的所有文件我们需要循环使用readir函数
    while(ent = readdir(dir))
    {
//        printf("%s\r\n",ent->d_name);
//        使用stat函数获取该文件的属性
    struct stat st;
    int ret;
    ret = stat(ent->d_name,&st);
//    判断文件的类型
    if(-1 == ret)
    {
        printf("*****************\r\n");
        printf("%s is error\r\n",ent->d_name);
       perror("stat error:");
        printf("*****************\r\n");
        continue;
    }
        if(S_ISREG(st.st_mode))//判断是否为普通文件
        {
            printf("%s is reg_file\r\n",ent->d_name);
        }
        else if(S_ISDIR(st.st_mode))//判断是否为目录文件
    {
        printf("%s is dir\r\n",ent->d_name);
    }

    }
    return 0;
}

效果显示:

解释:在这里为什么1.c和2.c和3.c为什么找不到呐,但是这三个文件名明明在test目录中呀???

这是因为main可执行文件在readdis的目录中,在使用stat进行查找的时候,查找的目录就是在main可执行文件相同的目录中查找,而不会去test的目录中查找。

如果想要去test目录中读取则需要在文件名字前边加上:test

示例代码:

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

int main() {

//打开一个目录
    DIR *dir;
    dir = opendir("test");
    if(NULL == dir)
    {
        perror("opendir error");
        return 0;
    }

    //读取目录下的文件
    struct dirent *ent;
    //因为readdir函数每次只能读取一个文件,所以如果我们需要读取目录下的所有文件我们需要循环使用readir函数
    while(ent = readdir(dir))
    {
//        printf("%s\r\n",ent->d_name);
//        使用stat函数获取该文件的属性
    struct stat st;
    int ret;
    //对读取到的文件的名字加上:test
    char file_name[128]={0};
    strcpy(file_name,"test/");
    strcat(file_name,ent->d_name);//字符拼接
    ret = stat(file_name,&st);
//    判断文件的类型
    if(-1 == ret)
    {
        printf("*****************\r\n");
        printf("%s is error\r\n",ent->d_name);
       perror("stat error:");
        printf("*****************\r\n");
        continue;
    }
    if(S_ISREG(st.st_mode))//判断是否为普通文件
    {
        printf("%s is reg_file\r\n", ent->d_name);
    }
    else if(S_ISDIR(st.st_mode))//判断是否为目录文件
    {
        printf("%s is dir\r\n",ent->d_name);
    }

    }
    return 0;
}

代码效果:

3、递归读取一个目录下的完整内容(目录+文件)

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <dirent.h>
//目录的打开顺序
//1、opendir打开目录
//2、readdir读取目录
//3、stat获取文件信息
//4、判断文件是否是目录是的话继续递归打开目录
//成功返回0,失败返回-1
int read_dis(const char *dirname)
{
    //打开一个目录
    DIR *dir;
    dir = opendir("test");
    if(NULL == dir)
    {
        perror("opendir error");
        return -1;
    }

    //读取目录下的文件
    struct dirent *ent;
    //因为readdir函数每次只能读取一个文件,所以如果我们需要读取目录下的所有文件我们需要循环使用readir函数
    while(ent = readdir(dir))
    {
//        判断该文件是否为“.”'..',要排除“.”'..'对递归的影响
if(strcmp(ent->d_name,".")==0 || strcmp(ent->d_name,"..")==0)
{
    continue;
}
//        printf("%s\r\n",ent->d_name);
//        使用stat函数获取该文件的属性
        struct stat st;
        int ret;
        //对读取到的文件的名字加上:test
        char file_name[128]={0};
        strcpy(file_name,dirname);
        strcat(file_name,"/");
        strcat(file_name,ent->d_name);//字符拼接
        ret = stat(file_name,&st);
//    判断文件的类型
        if(-1 == ret)
        {
            printf("*****************\r\n");
            printf("%s is error\r\n",ent->d_name);
            perror("stat error:");
            printf("*****************\r\n");
            continue;
        }
        if(S_ISREG(st.st_mode))//判断是否为普通文件
        {
            printf("%s is reg_file\r\n", ent->d_name);
        }
        else if(S_ISDIR(st.st_mode))//判断是否为目录文件
        {
            printf("%s is dir\r\n",ent->d_name);
            read_dis(file_name);//递归打开这个目录里边的文件
        }

    }
    return 0;
}

int main() {

    read_dis("test");//相对路径要确保可执行文件和test在同一个目录下
    return 0;

}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1423227.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

CTF-WEB的入门真题讲解

EzLogin 第一眼看到这个题目我想着用SQL注入 但是我们先看看具体的情况 我们随便输入admin和密码发现他提升密码不正确 我们查看源代码 发现有二个不一样的第一个是base64 意思I hava no sql 第二个可以看出来是16进制转化为weak通过发现是个弱口令 canyouaccess 如果…

计算机网络-调度算法-2(时间片轮转 优先级调度算法 多级反馈队列调度算法 多级队列调度算法)

文章目录 总览时间片轮转时间片大小为2时间片大小为5若按照先来先服务算法 优先级调度算法例题&#xff08; 非抢占式优先级调度算法&#xff09;例题&#xff08; 抢占式优先级调度算法&#xff09;补充 思考多级反馈队列调度算法例题 小结多级队列调度算法 总览 时间片轮转 …

排序链表---归并--链表OJ

https://leetcode.cn/problems/sort-list/submissions/499363940/?envTypestudy-plan-v2&envIdtop-100-liked 这里我们直接进阶&#xff0c;用时间复杂度O(nlogn)&#xff0c;空间复杂度O(1)&#xff0c;来解决。 对于归并&#xff0c;如果自上而下的话&#xff0c;空间复…

sqlmap的使用

2024.1.31 sqlmap支持五种不同的注入模式&#xff1a; 1、布尔盲注2、时间盲注3、报错注入4、联合注入5、堆叠注入 检测注入 GET请求的基本格式 ​python sqlmap.py -u <测试网址> Ps:不知道为什么我的sqlmap使用时前面要加python&#xff0c;而大部分其他教程没提到…

3d模型导入草图大师模型变乱什么原因怎么解决?---模大狮模型网

3D模型在导入草图大师后出现混乱可能有多种原因&#xff0c;以下是一些可能的原因和解决方法&#xff1a; 模型尺寸问题&#xff1a;如果3D模型的尺寸在导入草图大师时与画布尺寸不匹配&#xff0c;可能导致模型混乱。解决方法是在3D建模软件中调整模型的尺寸&#xff0c;使其适…

五大架构风格之一:数据流风格

数据流风格详细介绍 系统架构数据流风格是一种软件体系结构风格&#xff0c;它强调了系统内部不同部分之间的数据流动。这种风格侧重于描述系统中的数据处理过程&#xff0c;以及数据是如何从一个组件传递到另一个组件的。以下是系统架构数据流风格的详细介绍&#xff1a; 1 基…

idea激活教程(2020.1.4及以上版本)

首先点击试用版本&#xff0c;进入软件&#xff0c;再依次进行一下操作 一、在idea的Plugins配置中添加Z大的插件市场 上图中加载出来的插件是默认的&#xff0c;大家不用在意&#xff0c;直接点击“Manage Plugin Repositoryies…”打开配置弹窗 点击号&#xff0c;添加一行…

【C/C++】C/C++编程——整型(一)

整型 C 中的整型是基本的数据类型之一&#xff0c;用于表示没有小数部分的数。这包括正整数、负整数以及零。C 提供了多种整型&#xff0c;以适应不同大小的数值需求和优化内存使用。 整型的种类 C 中的整型可以根据其大小&#xff08;即占用的字节数&#xff09;和能够表示…

【C++】类和对象_1_定义和定义域

1.类的定义&#xff1a; class className {//类体&#xff1a;由成员函数和成员变量组成 };//注意后面的分号class为定义类的关键字&#xff0c;ClassName为类的名字&#xff0c;{}中为类的主体&#xff0c;注意类定义结束时后面分号不能省略。 类体中内容称为类的成员&#x…

Nginx 部署指定文件夹下的项目(本地测试)

1、配置 vue.config.js&#xff0c;指定生成环境的包 //部署生产环境和开发环境下的URLpublicPath: process.env.NODE_ENV production ? "/marketing" : "/",///npm run build 或 varn build 生成文件的日录名称(要利baseUrl的牛产环境路一致)(默认dist…

【每日一题】2670. 找出不同元素数目差数组-2024.1.31

题目&#xff1a; 2670. 找出不同元素数目差数组 给你一个下标从 0 开始的数组 nums &#xff0c;数组长度为 n 。 nums 的 不同元素数目差 数组可以用一个长度为 n 的数组 diff 表示&#xff0c;其中 diff[i] 等于前缀 nums[0, ..., i] 中不同元素的数目 减去 后缀 nums[i …

Django模型(二)

一、更新数据库表结构 不管是新增模型,还是修改已有模型后,只需要执行行命令即可: 1.1、创建迁移 在项目根目录的cmd中运行: $ python manage.py makemigrations model_app备注 model_app是子应用的名称,如果不指定,那么就是对所有 INSTALLED_APPS 中的应用都进行预备…

linux 磁盘标签类型MBR转换为GPT

[rootlocalhost /]# fdisk -l 磁盘 /dev/sda&#xff1a;299.4 GB, 299439751168 字节&#xff0c;584843264 个扇区 Units 扇区 of 1 * 512 512 bytes 扇区大小(逻辑/物理)&#xff1a;512 字节 / 512 字节 I/O 大小(最小/最佳)&#xff1a;512 字节 / 512 字节 磁盘标签类…

软考(高级)在犹豫是否需要报班,不知大家有什么建议?

据我观察&#xff0c;软考是一门可以通过自学掌握的考试&#xff0c;并不争议。然而&#xff0c;尽管如此&#xff0c;我还是不建议大部分同学选择自学&#xff0c;因为相比报班而言&#xff0c;自学的成本反而较高。软考的难度并不低&#xff0c;往年的总体通过率仅为20%&…

注册亚马逊店铺用动态IP可以吗?

注册亚马逊店铺可以用动态IP&#xff0c;只要是独立且干净的网线就没问题&#xff0c;亚马逊规则要求一个IP地址只能出现一个亚马逊店铺&#xff0c;若使用不当会导致关联账户。 固定ip可以给我们的账户带来更多的安全&#xff0c;要知道关联问题是亚马逊上的一个大问题&#…

Sketch 99.5中文 优秀的网站和移动应用设计软件

Sketch for mac用于数字世界的图形设计。在一个屡获殊荣的软件包中提供强大的工具和优雅的界面。因为做美丽的事情应该是一种快乐&#xff0c;而不是负担。 软件下载&#xff1a;Sketch 99.5中文激活版下载 Sketch支持每层多个填充&#xff0c;边框和阴影&#xff1b;具有强大的…

【开源】基于JAVA+Vue+SpringBoot的民宿预定管理系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 用例设计2.2 功能设计2.2.1 租客角色2.2.2 房主角色2.2.3 系统管理员角色 三、系统展示四、核心代码4.1 查询民宿4.2 新增民宿4.3 新增民宿评价4.4 查询留言4.5 新增民宿订单 五、免责说明 一、摘要 1.1 项目介绍 基于…

【Pwn | CTF】BUUCTF test_your_nc1

天命&#xff1a;时隔两年&#xff0c;又杀回了pwn这里 拿到题目的提示&#xff0c;测试你的nc工具 这题直接连接就可以了&#xff0c;windows装了nc工具&#xff0c;直接耍 nc node5.buuoj.cn 28930 下面给一点nc命令的解释&#xff0c;文心一言得出来的 nc命令是一个用于网…

大创项目推荐 题目:基于深度学习的中文汉字识别 - 深度学习 卷积神经网络 机器视觉 OCR

文章目录 0 简介1 数据集合2 网络构建3 模型训练4 模型性能评估5 文字预测6 最后 0 简介 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 基于深度学习的中文汉字识别 该项目较为新颖&#xff0c;适合作为竞赛课题方向&#xff0c;学长非常推荐&#xff01; &a…

P1228 地毯填补问题

地毯填补问题 题目描述 相传在一个古老的阿拉伯国家里&#xff0c;有一座宫殿。宫殿里有个四四方方的格子迷宫&#xff0c;国王选择驸马的方法非常特殊&#xff0c;也非常简单&#xff1a;公主就站在其中一个方格子上&#xff0c;只要谁能用地毯将除公主站立的地方外的所有地…