【Linux】系统调用接口

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:要打开或创建的目标文件
    2.flags:打开文件时可以传入多个参数进程,用下面的一个或者多个常量进行“或”运算,构成flags.
    参数:
O_RDONLY  只读
O_WRONLY 只写
O_RDWR  读写打开
O_CRWAT 文件不存在创建它
O_APPEND  追加写
  • 返回值: 成功返回新打开文件的描述符
    失败返回 -1
  • open函数具体使用哪个由具体应用场景相关,如目标文件不存在,需要open创建,则选择三个参数的open,它的第三个参数表示创建文件的默认权限,否则使用两个参数的open。

close

       #include <unistd.h>

       int close(int fd);

  • 参数fd为要关闭文件的文件描述符
  • 返回值:成功返回 0,失败返回-1

write

       #include <unistd.h>

       ssize_t pwrite(int fildes, const void *buf, size_t nbyte,
              off_t offset);
       ssize_t write(int fildes, const void *buf, size_t nbyte);

  • 使用
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include<string.h>


int main()
{
  int fd = open("myfile",O_WRONLY | O_CREAT, 0644);
  if(fd < 0)
  {
      perror("open");
      return 1;
  }
  int count = 3;
  const char* buf = "hello taotao\n";
  while(count--)
  {
     write(fd,buf,strlen(buf));
  }
  close(fd);
  return 0;
}

上述代码使用open打开并创建一个新文件并在新文件中写入3行hello taotao。

read

       #include <unistd.h>

       ssize_t pread(int fildes, void *buf, size_t nbyte, off_t offset);
       ssize_t read(int fildes, void *buf, size_t nbyte);

  • 使用
#include<string.h>
#include<stdio.h>
#include<sys/types.h>
#include<stdlib.h>
#include<sys/stat.h>
#include<unistd.h>
#include<fcntl.h>

int main()
{
  int fd = open("myfile",O_RDONLY);
  if(fd < 0)
  {
    perror("open");
    exit(1);
  }
  const char* msg = "hello taotao\n";
  char buf[1024];
  while(1)
  {
    ssize_t s = read(fd,buf,strlen(msg));
    if(s > 0)
    {
      printf("%s",buf);
    }else{
      buf[s] = 0;
      break;
    }
  }
  close(fd);
  
}

文件描述符

前面我们在open提到了它的返回值:文件描述符,文件描述符号就是一个整数

  • Linux 进程默认情况下会有3个缺省打开的文件描述符号,分别是标准输入0,标准输出1,标准错误2.
  • 0, 1,2对应的物理设备一般是:键盘,显示器,显示器。
  • 本质
    在这里插入图片描述
    当我们打开文件时,OS在内存中要创建相应的数据结构来描述目标文件,于是就有了file结构体,表示一个已经打开的文件对象,而进程执行open系统调用,所以必须让进程与文件关联起来,每个进程都有一个指针*files,指向一张表files_struct,该表最重要的部分就是包含了一个指针数组,每个元素都是一个指向打开文件的指针!,所以本质上,文件描述符就是该数组的下标。
  • 文件描述符的分配单元
    在files_struct数组中,找到当前没有被使用的最小的一个下标,作为新的文件描述符。

重定向

#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include<stdlib.h>
int main()
{
  close(1);
  int fd =open("myfile",O_RDONLY | O_CREAT,00644);
  if(fd < 0)
  {
    perror("open");
    return 1;
  }
  printf("fd:%d\n",fd);
  fflush(stdout);
  close(fd);
  exit(0);
}

执行上述代码我们发现本应该输出在显示器上的内容,输出到了文件myfile中,其中fd为1,这种现象叫做重定向,常见的重定向有>,>>,<。
在这里插入图片描述
重定向的本质:
printf是c库当中的IO函数,一般在stout中输出,但是stdout底层访问文件的时候,找的还是fd:1,但此时,fd:1下标表示的内容已经变成myfile的地址,不再是显示器文件的地址,所以输出的任何消息都往文件里面写,进而完成重定向。

缓冲方式

  • 无缓冲:直接缓冲刷新
  • 行缓冲:按行缓冲刷新
  • 全缓冲:默认只有缓冲区应写满才会刷新非强制刷新

FILE

  • 因为IO相关函数与系统调用接口对应,并且库函数封装系统调用,所以,本质上,访问文件是通过fd访问的。所以c库当中的FILE结构体内部,必定封装了fd。
  • 一般c库函数写入文件时是全缓冲的,而写入显示器是行缓冲的
  • printf fwrite等库函数自带缓冲区,write系统没有缓冲区说明缓冲区是c库提供的。
  • printf fwrite等库函数发生重定向时,数据的写入方式由行缓冲变成了全缓冲。
  • 而我们放在缓冲区的数据,不会被立即刷新,甚至fork之后
  • 但是我们进程退出之后,会统一刷新,写入文件当中
  • fork的时候,父子数据会发生写时拷贝,所以当父进程准备刷新的时候,子进程也就有了同样一份数据,随即产生两份数据
  • write没有变化,说明没有所谓的缓冲

inode文件系统

  • inode是指在许多Unix文件系统中的一种数据结构。每个inode保存了文件系统的一个文件系统对象(文件的字节数,文件拥有者的ID,所在组ID,文件的读写执行权限,文件的链接数,文件数据lock的位置,文件的时间戳(1.ctime:inode上一次变动的时间2.mtime指文件内容上一次变动的时间3.atime文件上一次打开的时间),文件最后的访问时间ACCESS,文件属性最后的修改时间Change,文件内容最后的修改时间Modify)信息数据,但不包括数据内容或者文件名。
  • 文件系统创建(格式化)时,就把存储区域分为两大连续的存储区域。一个用来保存文件系统对象的元信息数据,这是由inode组成的表,每个inode默认是256字节或者128字节。inode节点的字数在格式化时就已经给定了,一般是1kb或每2kb就设置一个inode;另一个用来保存“文件系统对象”的内容数据,划分为512字节的扇区,以及由8个扇区组成的4K字节的块。块是读写时的基本单位。一个文件系统的inode的总数是固定的。这限制了该文件系统所能存储的文件系统对象的总数目。典型的实现下,所有inode占用了文件系统1%左右的存储容量。

在这里插入图片描述

  • 使用stat 文件名可以查看该文件的inode信息

  • 使用ls -li会显示文件元数据
    在这里插入图片描述
    从左☞右分别表示:文件inode号,权限,硬链接数,文件所有者,组,大小,最后修改时间,文件名

  • ds -i命令会显示下图信息
    在这里插入图片描述

  • inode不包含文件名或目录名的字符串,只包含文件或目录的“元信息”。Unix的文件系统的目录也是一种文件。打开目录,实际上就是读取“目录文件”。目录文件的结构是一系列目录项(dirent)的列表。每个目录项,由两部分组成:所包含文件或目录的名字,以及该文件或目录名对应的inode号码。文件系统中的一个文件是指存放在其所属目录的“目录文件”中的一个目录项,其所对应的inode的类别为“文件”;文件系统中的一个目录是指存放在其“父目录文件”中的一个目录项,其所对应的inode的类别为“目录”。可见,多个“文件”可以对应同一个inode;多个“目录”可以对应同一个inode。文件系统中如果两个文件或者两个目录具有相同的inode号码,那么就称它们是“硬链接”关系。实际上都是这个inode的别名。换句话说,一个inode对应的所有文件(或目录)中的每一个,都对应着文件系统某个“目录文件”中唯一的一个目录项。创建一个目录时,实际做了3件事:在其“父目录文件”中增加一个条目;分配一个inode;再分配一个存储块,用来保存当前被创建目录包含的文件与子目录。被创建的“目录文件”中自动生成两个子目录的条目,名称分别是:“.”和“…”。前者与该目录具有相同的inode号码,因此是该目录的一个“硬链接”。后者的inode号码就是该目录的父目录的inode号码。所以,任何一个目录的"硬链接"总数,总是等于它的子目录总数(含隐藏目录)加2。即每个“子目录文件”中的“…”条目,加上它自身的“目录文件”中的“.”条目,再加上“父目录文件”中的对应该目录的条目。通过文件名打开文件,实际上是分成三步实现:首先,操作系统找到这个文件名对应的inode号码;其次,通过inode号码,获取inode信息;最后,根据inode信息,找到文件数据所在的block,读出数据。

  • 目录也是文件包括文件名与文件名的inode号,权限r/w针对自己,要读取inode节点内的信息需要执行权限x。

  • 创建一个文件的步骤在这里插入图片描述

软硬链接

  • 软链接
    软链接是通过名字引用另一个文件,
    使用ln -s 目标文件建立软链接

  • 硬链接
    我们已经了解到真正找到硬盘上的文件并不是文件名,而是inode,在Linux上可以让多个文件名对应于同一个inode。删除一个文件不会影响相同inode的文件。

使用ln 源文件 目标文件建立硬链接
判断目录几个文件就是硬链接数字减2

  • 区别:硬链接没有创文件,只创了文件名与已有的inode建立映射关系。软链接有独立的inode(存放对应目标文件的路径)删除目标文件,打开软链接文件会报错。
    在这里插入图片描述
    上图中file.c与popen.c是硬链接,file1.c与popen.c为软链接。

猜你喜欢

转载自blog.csdn.net/weixin_41892460/article/details/83271492
今日推荐