【Linux】基础I/O

在C语言中我们学习到了文件的I/O,可以回顾一下:[(https://blog.csdn.net/mmwwxx123/article/details/81516082)


系统文件I/O

linux下所有设备都是以文件存在的,,可以说是一切皆文件,所以当我们需要用到这些设备的时候,首先就需要打开它们,下面我们来详细了解一下文件I/O操作。
用到的文件I/O有以下几个操作:打开文件、读文件、写文件、关闭文件等,对应用到的函数有:open、read、write、close、lseek(文件指针偏移)

  1. 打开文件
int open(const char *pathname, int flags);
int open(const char *pathname,int flags,mode_t mode);

 参数:
 a、pathname:打开或者创建的文件名字,如"text"
 b、 flags:
     O_RDONLY:只读打开
     O_WRONLY:只写打开
     O_RDWR:读、写打开
     O_CREAT:若此文件不存在则创建它,使用O_CREAT时后面要跟文件的访问权限位,如O_CREAT,0777
     O_APPEND:每次写时都追加到文件的尾端
     O_EXCL :如果同时指定了O_CREAT,而文件已经存在,则出错,用此可以测试一个文件是否存在,如果不存在,则创建此文件,这使测试和创建两者成为一个原子操作
     O_DSYNC:使每次write等待物理I/O操作完成,但是如果该写操作并不影响读取刚写入的数据,则不需等待文件属性被更新
     O_NONBLOCK :如果path引用的是一个FIFO、一个块特殊文件或一个字符特殊文件,则此选项为文件的本次打开操作和后续的I/O操作设置非阻塞方式
     O_NOCTTY:如果path引用的是  终端设备,则将该设备分配为此进程的控制终端
     O_SYNC:使每次write要等待物理I/O操作完成,包括有该write引起的文件属性更新所需的I/O
     O_TRUNC:如果文件存在,并且是常规文件而且以读写或者只写打开,则将其长度截断为0,如果文件是FIFO或终端设备文件,O_TRUNC标志被忽略,否则O_TRUNC不明确
     O_DIRECTORY:如果pathname引用的不是目录,则出错

返回值:
     成功:文件描述符
     失败:-1     
  1. 读文件
函数原型   ssize_t read(int fd, void *buf, size_t count);
  参数:
     a、fd:调用open后返回的文件描述符
     b、buf:用来存放从文件中读到的数据的缓冲区
     c、count:读取的字节数

  返回值:
     成功:读到的字节数,如果读到文件尾端,则返回0
     失败:-1
  1. 写文件
函数原型   ssize_t write(int fd, const void *buf, size_t count);
     参数:
     a、fd:调用open后返回的文件描述符
     b、buf:从来存放数据的缓冲区
     c、count:写入数据的字节数

返回值:
     成功:返回已写的字节数
     失败:-1
  1. 关闭文件
函数原型  int close(int fd);
     参数:
     a、fd:调用open后返回的文件描述符
  1. 文件偏移
函数原型   off_t lseek(int fd, off_t offset, int whence);
      参数:
      a、fd:调用open后返回的文件描述符
      b、offset  和参数whence有关,通常设置为0 (according  to thedirective whence as follows)
      c、whence:
         SEEK_SET: 将文件的偏移量设置为距文件开始处offset个字节
         SEEK_CUR:将文件的偏移量设置为其当前值加offset个字节,offset可为正或负
         SEEK_END: 将文件的偏移量设置为文件长度加offsetoffset可为正或负

返回值:
     成功:返回新的文件偏移量
     失败:-1

文件描述符 fd

对于内核而言,所有打开的文件都通过文件按描述符引用。文件描述符是一个非负整数。当打开一个现有文件或者创建一个新文件时,内核向进程返回一个文件描述符。当读、写一个文件时,使用open/creat返回的文件描述符标识该文件,将其作为参数传送给read或write。

linux系统下文件描述符0是标准输入,1是标准输出,2是标准出错,所以一般打开文件的时候文件描述符都是从3开始。 这里read和write是不能格式化读取和写入。
当打开文件时,操作系统在内存中要创建相应的数据结构来描述目标文件。于是就有了file结构体。表示一个已经打开的文件对象。而进程执行open系统调用,所以让进程与文件关联起来。每个进程都有一个指针*files,指向一张表fiiles_struct,该表包含一个指针数组,每个元素都是一个指向打开文件的指针。所以,本质上,文件描述符就是该数组的下标。
这里写图片描述

文件描述符的分配规则:

在file_struct 数组中,找到当前没有被使用的最小的一个下标,作为新的文件描述符。


重定向

1 #include<stdio.h>
  2 #include<stdlib.h>
  3 #include<sys/stat.h>
  4 #include<string.h>
  5 #include<sys/types.h>
  6 #include<fcntl.h>
  7 int main()
  8 {
  9         close(1);
 10         int fd=open("myfile",O_WRONLY|O_CREAT,0644);
 11         if(fd<0)
 12         {
 13                 perror("open");
 14                 return 1;
 15         }
 16         printf("fd: %d\n",fd);
 17         fflush(stdout);
 18         close(fd);
 19         exit(0);
 20 }

运行程序后,发现本来输出到显示器上的内容,输出到了文件myfile中,这就叫输出重定向。
常见的重定向有:>,>>,<.
重定向的本质如图:
这里写图片描述
printf一般往stdout中输出,但是stdout底层访问文件时,找的还是fd:1,但此时,fd:1下标所表示的内容已经变成了myfile的地址,不再是显示器文件的地址,所以,输出的任何消息都会往文件中写入,进而实现输出重定向。
函数dup和dup2
这两个函数都可用来复制一个现存的文件描述符,返回的新文件描述符与参数fieldes共享同一个文件表项.

      #include <unistd.h>

       int dup(int oldfd);
       int dup2(int oldfd, int newfd);

由dup返回的新文件描述符一定是当前可用文件描述符中的最小数值。用dup2则可以用fnewfd参数指定新描述符的数值。如果newfd已经打开,则先将其关闭。如若oldfd等于newfd,则dup2返回newfd,而不关闭它。


文件系统

我们用ls -l可以查看文件信息:
这里写图片描述
这些信息都有:模式,硬链接数,文件所有者,组,大小,最后修改时间,文件名。
相应的,我们用stat命令也可以查看信息:
这里写图片描述

因此在文件系统中它们相对应的如图:
这里写图片描述

创建一个新文件有以下步骤:

  • 存储属性
    内核先找到一个空闲的i节点(664231),内核把文件信息记录在其中。
  • 存储数据
    内核找到三个空闲块:例如:300,500,800。将内核缓冲区的第一块数据复制到300,下一块复制到500,以此类推。
  • 记录分配情况
    文件内容按顺序存放。内核在inode上的磁盘分布区记录了上述块列表。
  • 添加文件名到目录
    新的文件名abc,内核将入口(例664231,abc)添加到目录文件。文件名和inode之间的对应关系将文件名和文件内容及属性连接起来。

由以上可知:真正找到磁盘上文件的并不是文件名,而是inode。

硬链接

硬连接指通过索引节点来进行连接。在Linux的文件系统中,保存在磁盘分区中的文件不管是什么类型都给它分配一个编号,称为索引节点号(Inode Index)。在Linux中,多个文件名指向同一索引节点是存在的。一般这种连接就是硬连接。硬连接的作用是允许一个文件拥有多个有效路径名,这样用户就可以建立硬连接到重要文件,以防止“误删”的功能。其原因如上所述,因为对应该目录的索引节点有一个以上的连接。只删除一个连接并不影响索引节点本身和其它的连接,只有当最后一个连接被删除后,文件的数据块及目录的连接才会被释放。也就是说,文件真正删除的条件是与之相关的所有硬连接文件均被删除。

由于硬链接是有着相同 inode 号仅文件名不同的文件,因此硬链接存在以下几点特性:

  • 文件有相同的 inode 及 data block;
  • 只能对已存在的文件进行创建;
  • 不能交叉文件系统进行硬链接的创建;
  • 不能对目录进行创建,只可对文件创建;
  • 删除一个硬链接文件并不影响其他有相同 inode 号的文件。
软链接

另外一种连接称之为符号连接(Symbolic Link),也叫软连接。软链接文件有类似于Windows的快捷方式。它实际上是一个特殊的文件。在符号连接中,文件实际上是一个文本文件,其中包含的有另一文件的位置信息。

软链接与硬链接不同,若文件用户数据块中存放的内容是另一文件的路径名的指向,则该文件就是软连接。软链接就是一个普通文件,只是数据块内容有点特殊。软链接有着自己的 inode 号以及用户数据块。因此软链接的创建与使用没有类似硬链接的诸多限制:

  • 软链接有自己的文件属性及权限等;
  • 可对不存在的文件或目录创建软链接;
  • 软链接可交叉文件系统;
  • 软链接可对文件或目录创建;
  • 创建软链接时,链接计数 i_nlink 不会增加;
  • 删除软链接并不影响被指向的文件,但若被指向的原文件被删除,则相关软连接被称为死链接(即 dangling link,若被指向路径文件被重新创建,死链接可恢复为正常的软链接)。

我们用ln 创建一个硬链接文件;用 ln -s创建一个软连接文件:
这里写图片描述
从上面的结果中可以看出,硬连接文件b.txt与原文件a.txt的inode节点相同,然而符号连接文件的inode节点不同。

这里写图片描述
当删除原始文件a.txt后,硬连接b.txt不受影响,但是符号连接a.s文件无效.


动态库和静态库

  1. 静态函数库

    这类库的名字一般是libxxx.a;利用静态函数库编译成的文件比较大,因为整个 函数库的所有数据都会被整合进目标代码中,他的优点就显而易见了,即编译后的执行程序不需要外部的函数库支持,因为所有使用的函数都已经被编译进去了。当然这也会成为他的缺点,因为如果静态函数库改变了,那么你的程序必须重新编译。

生成静态库:

ar -rc

  1. 动态函数库

    这类库的名字一般是libxxx.so;相对于静态函数库,动态函数库在编译的时候 并没有被编译进目标代码中,你的程序执行到相关函数时才调用该函数库里的相应函数,因此动态函数库所产生的可执行文件比较小。由于函数库没有被整合进你的程序,而是程序运行时动态的申请并调用,所以程序的运行环境中必须提供相应的库。动态函数库的改变并不影响你的程序,所以动态函数库的升级比较方便。

生成动态库:

猜你喜欢

转载自blog.csdn.net/mmwwxx123/article/details/81515147