【Linux--基础IO】

目录

  • 一、系统文件接口
    • 1.1 open
    • 1.2 write
    • 1.3 read
    • 1.4 close
  • 二、文件描述符
  • 三、文件描述符的分配规则
  • 四、重定向
    • 4.1输出重定向的原理
    • 4.2dup2函数的系统调用
  • 五、缓冲区
    • 5.1代码及现象
    • 5.2原理解释
    • 5.3C语言FILE
  • 六、文件系统
    • 6.1磁盘的介绍
    • 6.1磁盘的分区管理
  • 7、软硬连接
    • 7.1软连接
    • 7.2硬连接
    • 7.3区别
  • 八、Linux动静态库
    • 8.1了解动静态库
      • 8.1.1静态库的特点
      • 8.1.2动态库的特点
    • 8.2静态库的打包与使用
      • 8.2.1静态库的打包
      • 8.2.1静态库的使用
    • 8.3动态库的打包与使用
      • 8.3.1动态库的打包
      • 8.3.1动态库的打包
      • 8.3.2动态库的使用

一、系统文件接口

1.1 open

int open(const char* pathname,int flags,mode_t mode)

open的第一个参数
open函数的第一个参数是pathname,表示要打开或创建的目标文件

若pathname以路径的方式给出,则需要创建该文件时,在pathname路径下进行创建
若pathname以文件名的方式给出,则需要创建该文件时,默认在当前路径下进行创建
open的第二个参数
在这里插入图片描述
open函数的第三个参数
表示创建文件的默认权限
open的返回值
open函数成功调用后返回打开文件的文件描述符,若调用失败则返回-1。

1.2 write

Linux系统接口中使用write函数向文件写入信息

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

将buf位置开始向后count字节的数据写入文件描述符为fd的文件当中。
若数据写入成功,则返回实际写入数据的字节数。
若数据写入失败,则返回-1。

1.3 read

inux系统接口中使用write函数向文件写入信息

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

从文件描述符为fd的文件读取count字节的数据到buf位置当中
若数据读取成功,则返回实际读取数据的字节数
若数据读取失败,则返回-1

1.4 close

使用close函数时传入需要关闭文件的文件描述符(即调用open函数的返回值)即可。若关闭文件成功则返回0;若关闭文件失败则返回-1。

int close(int fd)

二、文件描述符

在这里插入图片描述

三、文件描述符的分配规则

现象代码1:

#include <stdio.h>                                                                                                                                                               
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main()
{int fd1 = open("./log1.txt",O_RDWR | O_CREAT | O_TRUNC,0666);int fd2 = open("./log2.txt",O_RDWR | O_CREAT | O_TRUNC,0666);int fd3 = open("./log3.txt",O_RDWR | O_CREAT | O_TRUNC,0666);int fd4 = open("./log4.txt",O_RDWR | O_CREAT | O_TRUNC,0666);int fd5 = open("./log5.txt",O_RDWR | O_CREAT | O_TRUNC,0666);printf("fd1: %d\n",fd1);printf("fd2: %d\n",fd2);printf("fd3: %d\n",fd3);printf("fd4: %d\n",fd4);printf("fd5: %d\n",fd5);close(fd1);close(fd2);close(fd3);close(fd4);close(fd5);return 0;
}

现象结果1:
在这里插入图片描述
现象代码2:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main()
{close(0);close(2);                                                                                                                                                                    int fd1 = open("./log1.txt",O_RDWR | O_CREAT | O_TRUNC,0666);int fd2 = open("./log2.txt",O_RDWR | O_CREAT | O_TRUNC,0666);int fd3 = open("./log3.txt",O_RDWR | O_CREAT | O_TRUNC,0666);int fd4 = open("./log4.txt",O_RDWR | O_CREAT | O_TRUNC,0666);int fd5 = open("./log5.txt",O_RDWR | O_CREAT | O_TRUNC,0666);printf("fd1: %d\n",fd1);printf("fd2: %d\n",fd2);printf("fd3: %d\n",fd3);printf("fd4: %d\n",fd4);printf("fd5: %d\n",fd5);close(fd1);close(fd2);close(fd3);close(fd4);close(fd5);return 0;
}

现象结果2:
在这里插入图片描述
总结:文件描述符是从最小但是没有被使用的fd_array数组下标开始进行分配的

四、重定向

重定向包含了输入重定向、追加重定向、输出重定向。
其中的原理本质上都一样。

4.1输出重定向的原理

输出重定向的本质是,将本应该输出到A文件的数据输出到B文件中。
若想让本应该输出到"显示器文件"的数据输出到log.txt文件当中,可以在打开log.txt文件之前将文件描述符为1的文件关闭(即将“显示器文件”关闭)。当我们后续打开log.txt文件时所分配到的文件描述符就是1。

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{close(1);int fd = open("./log.txt",O_RDWR | O_CREAT | O_TRUNC, 0666);if(fd < 0){perror("opern error:");return 1;}printf("hello world\n");fflush(stdout);//为什么需要刷新?阅读后续章节《缓冲区》close(fd);return 0;
}

在这里插入图片描述

4.2dup2函数的系统调用

在Linux环境下还可以使用dup2()系统调用来实现重定向。dup2()本质上是通过fd_array数组中地址元素的拷贝完成重定向的。

#include <unistd.h>
int dup2(int oldfd, int newfd);

函数功能: 将fd_array[oldfd]的内容拷贝到fd_array[newfd]当中
函数返回值: 若调用成功则返回newfd,否则返回-1

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
int main()
{int fd = open("log.txt", O_WRONLY | O_CREAT, 0666);if (fd < 0){perror("open");return 1;}close(1);dup2(fd, 1);printf("hello printf\n");fprintf(stdout, "hello fprintf\n");return 0;
}

在这里插入图片描述

五、缓冲区

5.1代码及现象

在这里插入图片描述

5.2原理解释

在这里插入图片描述

5.3C语言FILE

因为库函数是对系统调用接口的封装,本质上访问文件都是通过文件描述符fd进行访问的,所以C库当中的FILE结构体内部必定封装了文件描述符fd。
在/usr/include/stdio.h头文件中可以看到下面这句代码,也就是说FILE实际上就是struct _IO_FILE结构体的一个别名。

typedef struct _IO_FILE FILE;
struct _IO_FILE {int _flags;       /* High-order word is _IO_MAGIC; rest is flags. */
#define _IO_file_flags _flags//缓冲区相关/* The following pointers correspond to the C++ streambuf protocol. *//* Note:  Tk uses the _IO_read_ptr and _IO_read_end fields directly. */char* _IO_read_ptr;   /* Current read pointer */char* _IO_read_end;   /* End of get area. */char* _IO_read_base;  /* Start of putback+get area. */char* _IO_write_base; /* Start of put area. */char* _IO_write_ptr;  /* Current put pointer. */char* _IO_write_end;  /* End of put area. */char* _IO_buf_base;   /* Start of reserve area. */char* _IO_buf_end;    /* End of reserve area. *//* The following fields are used to support backing up and undo. */char *_IO_save_base; /* Pointer to start of non-current get area. */char *_IO_backup_base;  /* Pointer to first valid character of backup area */char *_IO_save_end; /* Pointer to end of non-current get area. */struct _IO_marker *_markers;struct _IO_FILE *_chain;int _fileno; //封装的文件描述符
#if 0int _blksize;
#elseint _flags2;
#endif_IO_off_t _old_offset; /* This used to be _offset but it's too small.  */#define __HAVE_COLUMN /* temporary *//* 1+column number of pbase(); 0 is unknown. */unsigned short _cur_column;signed char _vtable_offset;char _shortbuf[1];/*  char* _save_gptr;  char* _save_egptr; */_IO_lock_t *_lock;
#ifdef _IO_USE_OLD_IO_FILE
};

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

六、文件系统

6.1磁盘的介绍

磁盘是一种永久性存储介质,在计算机中,磁盘几乎是唯一的机械设备,与磁盘相对应的就是内存,内存是掉电易失存储介质,目前所有的普通文件都是在磁盘中存储的。
在这里插入图片描述
磁盘中最基本的单元是扇区–512字节/4kb,可以把整个磁盘由无数个扇区组成。
要把数据存到磁盘中,首先要解决的定位扇区问题:
那一面(根据磁头Header)?哪个磁道Cylinder?哪个扇区Sector。
通过上面三个来定位扇区的方式又称为CHS寻址。
在这里插入图片描述

6.1磁盘的分区管理

计算机为了更好的管理磁盘,会对磁盘进行分区。而对于每一个分区来说,分区的头部会包括一个启动块(Boot Block),对于该分区的其余区域,EXT2文件系统会根据分区的大小将其划分为一个个的块组(Block Group).
在这里插入图片描述
如何理解创建一个空文件?
通过编列inode位图的方式,找到一个空间的inode
在inode Tbale中找到对应的inode,并将文件的属性信息填充到inode结构中。
将该文件的文件名与inode添加到目录文件的数据块中。
如何理解对文件写入信息?
通过文件的inode编号找到对应的inode结构。
通过inode结构找到存储该文件内容的数据块,并将数据写入到数据块
若不存在数据块或申请的数据块已经被写满,则通过遍历块位图的方式找到一个空闲的块号,并在数据块区当中找到对应的空闲块,再将数据写入数据块,最后将建立数据块与inode结构的对应关系。
如何理解删除一个文件?
将该文件对应的inode在inode位图当中置为无效。
将该文件申请过的数据块在块位图当中置为无效。
为什么拷贝文件的时候特别慢,而删除文件却特别块?
因为拷贝文件需要创建文件、然后对文件进行写入操作,这个过程需要先申请inode号然后填充文件的属性,再申请数据块,将文件内容写入到数据块最后建立数据块与inode结构的对应关系。
而删除文件只需要把对应的inode号与数据块在位图中设置无效即可。
如何理解目录?
目录也是文件,
目录有自己的属性信息,目录的inode结构当中存储的就是目录的属性信息,比如目录的大小,目录的拥有者等。
目录也有自己的内容,目录的数据块当中存储的就是该目录下的文件名以及对应文件的inode映射关系。

7、软硬连接

7.1软连接

在这里插入图片描述
软连接有独立的inode,也有独立的数据块,数据块的内容是指向的文件的路径。
软链接就类似于Windows操作系统当中的快捷方式。
在这里插入图片描述

但是软链接文件只是其源文件的一个标记,当删除了源文件后,链接文件不能独立存在,虽然仍保留文件名,但却不能执行或是查看软链接的内容了。

7.2硬连接

在这里插入图片描述
硬连接文件的inode号与源文件的inode号相同,并且文件的属性也相同。可以认为硬链接是源文件的别名,一个inode有几个文件名该inode的硬链接数就是多少

为什么刚创建的目录的硬链接数是2?
在这里插入图片描述
在这里插入图片描述

原因:每个目录创建后,该目录下默认会有两个隐含文件.和…,它们分别代表当前目录和上级目录。这个dir与.是一样的。

7.3区别

1.软链接是一个独立的文件,有独立的inode,而硬链接没有独立的inode。
2.软链接相当于快捷方式,硬链接本质没有创建文件,只是建立了一个文件名和已有的inode的映射关系,并写入当前目录。

八、Linux动静态库

8.1了解动静态库

一堆源文件和头文件最终变成一个可执行程序需要经历以下四个步骤:
1.预处理: 完成头文件展开、去注释、宏替换、条件编译等,最终形成xxx.i文件
2.编译: 完成词法分析、语法分析、语义分析、符号汇总等,检查无误后将代码翻译成汇编指令,最终形成xxx.s文件
3.汇编: 将汇编指令转换成二进制指令,最终形成xxx.o文件
4.链接: 将生成的各个xxx.o文件进行链接,最终形成可执行程序
实际上,所有库本质是一些目标文件(xxx.o)的集合,库的文件当中并不包含主函数而只是包含了大量的方法以供调用,可以认为动静态库本质是可执行程序的"半成品"

  • 在Linux当中,以.so为后缀的是动态库,以.a为后缀的是静态库
  • 在Windows当中,以.dll为后缀的是动态库,以.lib为后缀的是静态库
    通过ldd命令可以查看可执行程序所依赖的库文件
    在这里插入图片描述

根据上图可以看出来test可执行程序依赖/lib64/lib.so.6,本质上是一个软连接,链接的源文件是/lib64/libc-2.17.so。这个libc-2.17.so就是C的动态库,去掉库的前缀,再去掉后缀,剩下的就是库的名字。

而gcc/g++默认进行的是动态链接,想使用静态链接需添加 -static 选项,且静态链接生成的可执行程序并不依赖其他库文件。
在这里插入图片描述
而且 使用静态库的可执行程序的大小 明显大于 使用动态库的可执行程序的大小

8.1.1静态库的特点

静态库是程序在编译链接的时候把库的代码复制到可执行文件当中的,生成的可执行程序在运行的时候将不再需要静态库,因此使用静态库生成的可执行程序的大小一般比较大

  • 优点:使用静态库生成可执行程序后,该可执行程序可独自运行,不再依赖库了
  • 缺点:使用静态库生成可执行程序会占用大量空间,特别是当有多个静态程序同时加载而这些静态程序使用的都是相同的库,这时在内存当中就会存在大量的重复代码

8.1.2动态库的特点

动态库是程序在运行的时候才去链接相应的动态库代码的,不必将库的代码复制到可执行文件当中,使得可执行程序的大小较使用静态库而言更小,节省磁盘空间。一个与动态库链接的可执行文件仅仅包含它用到的函数入口地址的一个表,而不是外部函数所在目标文件的整个机器码。
在这里插入图片描述

在可执行文件开始运行前,外部函数的机器码由操作系统从磁盘上的该动态库中复制到内存中,这个过程称为动态链接。操作系统采用虚拟内存机制使得物理内存中的一份动态库被所有要使用该库的进程共用,节省了内存空间。

8.2静态库的打包与使用

8.2.1静态库的打包

第一步:将打包的源文件生成对应的目标文件

func1.o:func1.cgcc -c func1.c -o func1.o
func2.o:func2.cgcc -c func2.c -o func2.o

在这里插入图片描述
第二步:使用ar命令将所有的目标文件打包成静态库

  • -r(replace):若静态库文件当中的目标文件有更新,则用新的目标文件替换旧的目标文件
  • (create):建立静态库文件
libfunc.a:func1.o func2.oar -rc libfunc.a func1.o func2.o

在这里插入图片描述
此外,我们可以用ar命令的- t 和 -v 选项查看静态库当中的文件。

-t:列出静态库中的文件
-v(verbose):显示详细的信息
在这里插入图片描述
第三步:将头文件和生成的静态库组织

mkdir -p my_lib/include
mkdir -p my_lib/lib
cp ./*.h ./my_lib/include 
cp ./*.a ./my_lib/lib

在这里插入图片描述
makefile完整版

libfunc.a:func1.o func2.oar -rc $@ $^
func1.o:func1.cgcc -c $^ -o $@
func2.o:func2.cgcc -c $^ -o $@.PHONY:output
output:mkdir -p my_lib/includemkdir -p my_lib/libcp ./*.h ./my_lib/include cp ./*.a ./my_lib/lib.PHONY:clean
clean:rm -rf ./my_lib ./*.o ./*.a      

8.2.1静态库的使用

方案一:使用选项

-I:指定头文件搜索路径
-L:指定库文件搜索路径
-l:指明需要链接库文件路径下的具体哪一个库
在这里插入图片描述
方案二:将头文件和库文件拷贝到系统路径下

sudo cp my_lib/include/* /usr/include/
sudo cp my_lib/libfunc.a /lib64/

实际上拷贝头文件和库文件到系统路径下的过程,就是安装库的过程。但并不推荐将个人编写的头文件和库文件拷贝到系统路径下,会对系统文件造成污染。所以我就不想演示这个过程了。

8.3动态库的打包与使用

8.3.1动态库的打包

第一步:将要打包的源文件生成对应的目标文件
此时需要添加 -fPIC 选项 ,即产生位置无关码

test1.o:test1.cgcc -fPIC -c test1.c -o test1.o
test2.o:test2.cgcc -fPIC -c test2.c -o test2.o

-fPIC作用于编译阶段,告诉编译器产生与位置无关的代码,此时产生的代码中没有绝对地址,全部都使用相对地址,从而代码可以被加载器加载到内存的任意位置都可以正确的执行(通过页表与共享区建立映射关系)。所以共享库被加载时,在内存的位置不是固定的 。
第二步:使用-shared选项将所有目标文件打包为动态库

libtest.so:test1.o test2.ogcc -shared $^ -o $@

第三步:组织头文件与生成的动态库

mkdir -p my_lib/include
mkdir -p my_lib/lib
cp ./*.h ./my_lib/include 
cp ./*.so ./my_lib/lib       

makefile完整版

 libtest.so:test1.o test2.ogcc -shared $^ -o $@
test1.o:test1.cgcc -fPIC -c test1.c -o test1.o
test2.o:test2.cgcc -fPIC -c test2.c -o test2.o.PHONY:output
output:mkdir -p my_lib/includemkdir -p my_lib/libcp ./*.h ./my_lib/include cp ./*.so ./my_lib/lib                                                                                                                                                       .PHONY:clean
clean:rm -rf *.so *.o my_lib

8.3.1动态库的打包

第一步:将要打包的源文件生成对应的目标文件

此时需要添加 -fPIC 选项 ,即产生位置无关码

func1.o:func1.cgcc -fPIC -c $@ -o $^
func2.o:func2.cgcc -fPIC -c $@ -o $^

-fPIC作用于编译阶段,告诉编译器产生与位置无关的代码,此时产生的代码中没有绝对地址,全部都使用相对地址,从而代码可以被加载器加载到内存的任意位置都可以正确的执行(通过页表与共享区建立映射关系)。所以共享库被加载时,在内存的位置不是固定的 。
第二步:使用-shared选项将所有目标文件打包为动态库

libtest.so:func1.o func2.ogcc -shared $^ -o $@

第三步:组织头文件与生成的动态库

mkdir -p my_lib/include
mkdir -p my_lib/lib
cp ./*.h ./my_lib/include 
cp ./*.so ./my_lib/lib       

makefile完整版

libtest.so:func1.o func2.ogcc -shared $^ -o $@
func1.o:func1.cgcc -fPIC -c $@ -o $^
func2.o:func2.cgcc -fPIC -c $@ -o $^.PHONY:output
output:mkdir -p my_lib/includemkdir -p my_lib/libcp ./*.h ./my_lib/include cp ./*.so ./my_lib/lib                                                                                                                                                       .PHONY:clean
clean:rm -rf *.so *.o my_lib

8.3.2动态库的使用

接下来使用与静态库相同的方式,用-I选项指定头文件搜索路径,用-L选项指定库文件搜索路径,最后用-l选项指明库的名称。但是可以发现可执行程序貌似找不到这个动态库的位置。
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Windows本地如何添加域名映射?(修改hosts文件)

1. DNS(域名系统) Domain Name System(域名系统)&#xff1a;为了加快定位IP地址的速度, 将域名映射进行层层缓存的系统. 目的&#xff1a;互联网通过IP&#xff08;10.223.146.45&#xff09;定位浏览器建立连接&#xff0c;但是我们不易区别IP&#xff0c;为了方便用户辨识I…

使用cmake构建的工程的编译方法

1、克隆项目工程 2、进入到工程目录 3、执行 mkdir build && cd build 4、执行 cmake .. 5、执行 make 执行以上步骤即可完成对cmake编写的工程进行编译 &#xff0c;后面只需执行你的编译结果即可 $ git clone 你想要克隆的代码路径 $ cd 代码文件夹 $ mkdir bu…

【GAMES101】三维变换

games101的第四节课讲了三维变换和观察变换&#xff0c;我们这里先记录一下三维变换的知识&#xff0c;后面再讲观察变换 齐次坐标下的三维变换 类似于解决之前二维变换平移的问题&#xff0c;三维变换下用齐次坐标通过增加一个维度来表示&#xff0c;第四个维度为1表示这是个…

【计算机网络学习之路】HTTP请求

目录 前言 HTTP请求报文格式 一. 请求行 HTTP请求方法 GET和POST的区别 URL 二. 请求头 常见的Header 常见的额请求体数据类型 三. 请求体 结束语 前言 HTTP是应用层的一个协议。实际我们访问一个网页&#xff0c;都会像该网页的服务器发送HTTP请求&#xff0c;服务…

Verilog学习 | 用initial语句写出固定的波形

initial beginia 0;ib 1;clk 0;#10ia 1; #20ib 0;#20ia 0; endalways #5 clk ~clk; 或者 initial clk 0;initial beginia 0;#10ia 1; #40ia 0; endinitial beginib 1;#30 ib 0; endalways #5 clk ~clk;

Linux Component概述和高通V4l2驱动模型

1 Linux为什么要引入Component框架&#xff1f; 为了让subsystem按照一定顺序初始化设备才提出来的。 subsystem中由很多设备模块&#xff0c;内核加载这些模块的时间不确定。子系统内有些模块是需要依赖其它模块先初始化才能进行自己初始化工作(例如v4l2 subdev和v4l2 video …

CentOS系统中设置反向代理服务器的步骤

在CentOS系统中设置反向代理服务器可以帮助你隐藏原始服务器的细节&#xff0c;并提高服务器的安全性。以下是在CentOS系统中设置反向代理服务器的步骤概述&#xff1a; 安装反向代理软件&#xff1a; 常见的反向代理软件包括Nginx和Apache。你可以选择其中之一来作为你的反向…

postgresql从入门到精通 - 第37讲:postgres物理备份和恢复概述

PostgreSQL从小白到专家&#xff0c;是从入门逐渐能力提升的一个系列教程&#xff0c;内容包括对PG基础的认知、包括安装使用、包括角色权限、包括维护管理、、等内容&#xff0c;希望对热爱PG、学习PG的同学们有帮助&#xff0c;欢迎持续关注CUUG PG技术大讲堂。 第37讲&#…

找到字符串中所有字母异位词

题目描述 2. 解题思路&#xff1a; 创建两个整型的哈希表&#xff0c;双指针控制一个里面只放p,一个里面只放长度是p长度中s&#xff0c;不断增加 减少去比对 bool isMath(int *a, int *b) {for (int i 0; i < 26; i) {if (a[i] ! b[i]) {return false;}}return true; } i…

Redission分布式锁原理初探

什么是分布式锁&#xff0c;为什么需要分布式锁 在多线程并发请求当中&#xff0c;为了保证我们的资源同一时刻只有一个线程进行操作&#xff08;如商品超卖问题、购票系统等&#xff09;&#xff0c;我们通常要添加锁机制&#xff0c;如ReentrantLock&#xff0c;也就是可重入…

Volumetric Lights 2 HDRP

高清晰度渲染管道,包括先进的新功能,如半透明阴影图和直接灯光投射加上许多改进。 插件是一个快速,灵活和伟大的前瞻性光散射解决方案的高清晰度渲染管道。只需点击几下,即可改善场景中的照明视觉效果。 兼容: 点光源 聚光灯 碟形灯 矩形灯 通过覆盖摄像机周围大面积区域的…

nacos服务的分级存储

举例说明 一个服务可以有多个实例&#xff0c;比如我们当前有4个实例&#xff0c;都叫做nacos-user-service服务 ip地址端口服务器所属地区集群192.168.xxx.xxx18080广东GD192.168.xxx.xxx18081广东GD192.168.xxx.xxx18082广西GX192.168.xxx.xxx18083广西GX所以我们可以将nacos…