首页 > 代码库 > linux 文件系统

linux 文件系统

linux的文件系统是一个广义的文件系统,可以认为linux系统对任何设备和对象的操作都是等价于对文件的操作。

linux系统对所有可操作对象进行了高度的抽象,将其归纳为这么几类:

  1. 普通文件:就是狭义概念上的存储在磁盘中的文件,是纯粹的存储数据的文件,例如文本文件,图片文件,可执行文件等;

  2. 字符设备文件:通常指输入输出终端,键盘,串口等,通常可以用操作文件的方式来操作字符设备文件,因为对这类设备的读写其实是读写的字符流;

  3. 块设备文件:有硬盘,软盘和RAM等,块设备文件通常公国内存缓冲区读写数据,支持块数据读取和随机读取,读写性能更好;

  4. socket文件:网络通信的文件描述符,支持像操作普通文件一样操作网络数据的读写;

linux提供了虚拟文件系统架构,要求所有设备都提供一致的文件操作接口,方便用户像操作文件一样操作所有设备,因此可以说linux系统下,一切操作的都是文件。

 

文件描述符

文件描述符是对一个操作文件的抽象标识,它被用户空间和内核空间的枢纽共同使用。当用户空间对一个文件描述符进行操作时,例如对一个文件描述符进行操作时,当内核进行系统调用时,将根据该文件描述符找到对应的真实设备进行操作,并将结果返回用户空间。

文件描述符通常是一个整数标识,因此它根据系统不同,有一个上线值(通常是0-OPNE_MAX)。所以,我们在使用完一个文件后,应该尽快释放它(通常调用close函数)。

1. 打开文件和创建文件

头文件:sys/types.h sys/stat.h fcntl.h

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

int create(const char* filepath, int flags, mode_t mode);

打开和创建成功返回一个文件描述符,失败返回-1。通常filepath的字符长度也会有限制,如果超出长度会被截断。

flags的选项:

  O_RDONLY (0 只读)

  O_WRONLY (1 只写)

  O_RDWR (2 读写)  

  O_APPEND (写操作追加到末尾)

  O_CREATE (如果文件不存在,创建一个,需要mode来设置权限)

  O_EXCL (查看文件是否存在。如果同时指定O_CREATE,并且文件已经存在,会返回错误)

  O_TRUNC (将文件长度截断为0)

mode的选项:

  S_IRWXU 用户有读写和执行权限

  S_IRUSR 用户有读权限

  S_IWUSR 用户有写权限

  S_IXUSR 用户有执行权限

  S_IRWXG 组用户有读写和执行的权限

  S_IRGRP 组用户有读权限

  S_IWGRP 组用户有写权限

  S_IXGRP 组用户有执行权限

  S_IRWXO 其他用户有读写和执行权限

  S_IROTH 其他用户有读权限

  S_IWOTH 其他用户有写权限

  S_IXOTH 其他用户有执行权限

对文件的操作默认都是阻塞的,即必须等到文件操作返回才能继续。

2. 关闭文件

int close(int fd);

如果成功返回0,失败返回-1。在关闭文件后,系统可以再次使用该文件描述符,如果使用后不关闭,进程也会在退出时关闭所有打开的文件描述符,但是可能造成文件描述符不够用的情况,导致无法再打开新的文件。

3. 读取文件

头文件:unistd.h

ssize_t read(int fd, void* buf, size_t count);

从文件fd读取count字节的字节流并存到buf指向的内存地址中。读取成功返回读取成功的字节数,如果返回0标识读取到文件末尾,读取失败返回-1。ssize_t的定义是依赖平台,可能是long或者int。实际情况返回的字节数可能小于count指定的大小,表示无法读取到count大小的字节,已经将文件中当前剩余的所有字节全部读取出来。

4. 写文件

头文件:unistd.h

ssize_t write(int fd, const void* buf, size_t count);

向文件fd中写入指定count大小的数据,数据来自buf指向的内存地中的字节流。如果成功返回写入成功的字节数,失败返回-1。

5. 文件偏移

头文件:sys/types.h unistd.h

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

文件偏移量:文件操作的当前位置,所有文件操作都是从文件偏移量开始的。如果在打开文件的时候,指定了O_APPEND,文件偏移量就是文件的长度,即末尾处,否则偏移量为0,即文件起始位置。

lseek函数可以更新文件fd的偏移量,offset可为整数或负数,表示相对whence的偏移大小。

whence选项:

  SEEK_SET:文件的开始位置;

  SEEK_CUR:文件的当前位置;

  SEEK_END:文件的结尾位置;  

如果函数执行成功返回偏移量的值,可能为负数,失败返回-1。

当向文件写数据时,如果使用lseek设置偏移量超出了文件大小,当继续向文件写入数据时,中间的空白字节会用‘\0‘填充。

6. 获取文件状态

头文件 sys/types.h sys/stat.h unistd.h

int stat(const char* path, struct stat* buf);

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

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

如果获取成功返回0,失败返回-1。文件状态的数据写入buf指向的数据结构中。

7. 文件空间映射

头文件:sys/mman.h

void* mmap(void* start, size_t length, int prot, int flags, int fd, off_t offset);

该函数将文件映射到内存中,然后就可以采用内存操作,不必再使用read和write函数,性能更好。

start:内存的开始地址,但是通常不需要指定,设置为NULL,表示由系统决定映射的地址,在返回值中体现;

length:表示映射的地址长度,即从文件映射到内存中的数据大小;

prot:映射区域的保护方式,可以由多个值进行组合;

  PROT_EXEC 可执行区域

  PROT_READ 可读取区域

  PROT_WRITE 可写区域

flags:设定映射区域的类型,选项和对映射区域是否可以操作,可以由多个值进行组合;

  MAP_FIXED 如果参数start指定的地址无法建立映射,映射会失败,通常不指定该值,将start设置为NULL,由系统选定映射地址;

  MAP_SHARED:映射区域是多进程共享的,对映射区域的操作都会影响原来的文件;

  MAP_PRIVATE:映射区域的写操作都会产生一个副本,并且写操作不会影响原来的文件;

  MAP_DENYWRITE:对文件的写入操作被禁止,只能通过对映射区域的操作来实现对文件的操作;

  MAP_LOCKED:将映射区域锁定,不会被虚拟内存重置;

int munmap(void* start, size_t length);

取消文件映射,通常在映射区域操作完成后调用,然后关闭文件;

 

 1 int fd = open("leo.txt", O_RDWR | O_CREAT, S_IRWXU); 2         if (fd == -1) { 3                 printf("create file failed."); 4         } 5         else { 6                 char buf[] = "leo is me!"; 7                 write(fd, buf, strlen(buf)); 8  9                 char* ptr = (char*)mmap(NULL, strlen(buf), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);10                 if ((char*)-1 == ptr) {11                         printf("mmap failed.");12                 }13                 else {14                         memcpy(ptr, "123", 3);15                         munmap(ptr, strlen(buf));16                         close(fd);17                 }18         }

 

8. 文件属性操作

头文件:unistd.h 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);

对文件的属性进行修改,失败返回-1。

fcntl的功能:

  复制文件描述符:cmd = F_DUPFD,返回值是新的文件描述符。新的文件描述符是大于或等于第三个参数的尚未使用的文件描述符中的最小值;

  获取或设置文件描述符:cmd = F_GETFD/F_SETFD;

  获取或设置文件状态值:cmd = F_GETFL/F_SETFL;

    文件状态值:

    O_RDONLY 只读

    O_WRONLY 只写

    O_RDWR 读写

    O_APPEND 将写入添加到末尾

    O_NONBLOCK 非阻塞方式

    O_SYNC 异步方式

    O_ASYNC 同步方式

    正确获得O_RDONLY,O_WRONLY和O_RDWR标志位的方法是和O_ACCMODE进行与操作才能获得;

int fd = open("leo.txt", O_RDWR | O_CREAT, S_IRWXU);
if (fd == -1) {
  printf("create file failed.");
}
else {
  int flags = fcntl(fd, F_GETFL, 0);
  if (flags < 0) {
    printf("get flags failed123333.");
  }
  else {
    int accmode = flags & O_ACCMODE;
    if (accmode == O_RDONLY) {
      printf("file only read. \n");
    }
    else if (accmode == O_WRONLY) {
      printf("file only write \n");
    }
    else if (accmode == O_RDWR) {
      printf("file can be read and write. \n");
  }
  else
  {
    printf("accmode invalid. \n");
  }

  flags |= O_APPEND;
  int result = fcntl(fd, F_SETFL, &flags);
  if (result < 0) {
    printf("set file flag failed. \n");
  }

  int newflags = fcntl(fd, F_GETFL, 0);
  if (newflags & O_APPEND) {
    printf("file append. \n");
  }
  if (newflags & O_NONBLOCK) {
    printf("file non block. \n");
  }

    close(fd);
  }
}

  获取或设置文件信号的发送对象:cmd = F_GETTOWN/F_SETTOWN/F_GETSIG/F_SETSIG;

int uid = fcntl(fd, F_GETOWN); // 获取接受信号SIGIO和SIGURG的进程idint uid = fcntl(fd, F_SETOWN, 10000); // 将接受信号SIGIO和SIGURG的进程设置为id为10000的进程

  获取或设置记录锁:cmd = F_GETLK/F_SETLK/F_SETLKW;

  获取或设置文件租约:cmd = F_GETLEASE/F_SETLEASE;

9. 文件输入输出控制

头文件:sys/iotl.h

int ioctl(int device, int request, ...);

通过向设备发送命令来控制设备,失败返回-1。device为一个打开的设备号,其他参数根据设备驱动程序来决定。

 

#include "stdlib.h"#include "stdio.h"#include <unistd.h>#include <fcntl.h>#include <linux/cdrom.h>#include <sys/ioctl.h>int main(void){int fd = open("/dev/cdrom", O_RDONLY | O_NONBLOCK);    if (fd > 0) {        if (!ioctl(fd, CDROMEJECT, NULL)) {            printf("ok \n");        }        else {            printf("fail \n");        }    }    getchar();    return 0;}

 

linux 文件系统