Linux基础IO(二)
文章目录
缓冲区
为什么会有缓冲区
其实缓冲区的意义是节省进程进行数据 IO 的时间。
怎么理解呢?我来举个例子:
孩子上大学了,学校里的水果又很贵,孩子给家里人说了,家里人知道了要给孩子每隔一段时间送点水果。下面有两个选项:
- 1.亲自开车给孩子送
- 2.发快递
相信大家大部分人会选择发快递吧,这样既省时间,又省事。
当然操作系统也是这样做的,它也有自己的缓冲区。当我们刷新用户缓冲区的数据时,并不是直接将数据直接刷新到磁盘或显示器上,而是先刷新到OS缓冲区,然后由操作系统将数据刷新到磁盘或者显示器。
缓冲区刷新策略
-
无缓冲:缓冲区一出现数据,立即刷新
-
行缓冲:缓冲区每出现一行数据就刷新一次
-
全缓冲:缓冲区被填满后再刷新
还有两种方式强制刷新缓冲区:
- fflush()等函数
- 进程退出
为什么会有缓冲区刷新策略呢?
因为操作系统要求的是高效:一次写入外设等设备的时间消耗是远小于多次写入外设的效率的。其中时间消耗大部分是在等待外设就绪,例如缓冲区等待磁盘的时间就远小于缓冲区数据写入磁盘的时间,比如10秒时间内。9秒的时间是在等磁盘准备就绪,1秒是在写入数据。
操作系统可是个聪明人,它可不会傻傻的浪费自己宝贵的时间。
我们通过一段代码来加对深缓冲策略的理解:
int main()
{
printf("aaaaaaaaaaaaaa\n");
fputs("bbbbbbbbbbbbbb\n", stdout);
write(1, "xxxxxxxxxxxxxx\n", 14);
fork();
return 0;
}
在我们直接运行程序时:
但是当我们重定向进文件时:
- 第一种情况是行缓冲,我们每使用的
\n
都是行缓冲。 - 第二种情况是全缓冲,因为我们把程序重定向到一个文件中,文件是全缓冲,又因为我们使用
fork
创建了一个子进程,而进程间互不干扰,所以都会打印一次字母a和字母b。而字母x是系统调用write,我们可以看作系统是没有缓冲区的(其实有,只是我们并不清楚规则)所以只打印一次。
系统有缓冲区吗?
用户写入数据到外设,先将数据写入系统缓冲区,再由系统刷新到外设中。
因为操作系统是进行软硬件资源管理的软件,再加上我们之前学过的层级结构图(具体细节请看我之前写过的初识操作系统的文章),用户区的数据要刷新到具体外设必须经过操作系统,所以操作系统是肯定有自己的缓冲区的。
文件系统
文件分为磁盘文件和内存文件,我们之前一直说的都是内存文件,下面我们来谈谈磁盘文件。
什么是inode
我们都知道文件=内容+属性,其中内容就是数据,属性就是文件名、创建日期和文件大小等。
我们输入ll
命令可以查看当前路径下的文件:
文件属性是这样的:
我们还可以查看这些文件的inode
[wsj@VM-8-4-centos day03]$ ll -i
//或者
[wsj@VM-8-4-centos day03]$ ls -i -l
inode到底是什么呢?
inode是一个保存元信息的结构,且因为系统中有大量的文件,为了区分每个文件,就给每个文件一个inode号,这个inode号就和我们每个人有自己的身份证号一样。
所以说inode是一个文件属性的集合。
软硬链接
软链接
我们可以通过以下命令创建一个软链接:
[---@VM-8-4-centos day03]$ ln -s mycode a_mycode
我们通过ls -l -i
可以看到软链接的文件和源文件的inode
是不同的,而且文件大小也是差异很大的。
软链接又叫做符号链接,软链接文件相对于源文件来说是一个独立的文件,它有自己的inode号,但是该文件只包含了源文件的路径名,所以软链接文件的大小要比源文件小得多。
其实软链接就类似于Windows操作系统当中的快捷方式。
所以说,运行源文件和运行快捷方式(软链接)是一样的结果。
硬链接
[---@VM-8-4-centos day03]$ ln mycode b_mycode
我们通过ls -l -i
可以看到软=硬链接的文件和源文件的inode
是相同的,而且文件大小差不多一模一样,硬链接个数也从1变成了2。
硬连接其实就是文件的一个别名,硬链接个数就是文件别名的个数。
和软连接相比,硬链接的源文件被删除后,硬链接仍然可以执行。
硬链接可以让多个不在或者同在一个目录下,能够修改同一个文件,且其中一个修改后,所有与其有硬链接的文件都一起修改了。
热知识
当我们创建一个普通文件时,他的硬链接是1,而我们创建一个目录文件他的硬链接是2,这是为什么?
原因是该dir
目录下必须有有两个隐藏文件.
和..
。它们分别是上级目录和当前目录,这就是我们常看别人用的cd ..
的原理。(inode也是相同的)
总结
- 软链接有独立的inode,硬链接没有。
- 软链接不能独立运行,硬链接能独立运行。
- 软链接相当于快捷方式,硬链接本质没有创建文件,只是对当前文件的复制加上对原来inode的映射,并写入当前目录。
文件的三个时间
- Access:最后访问时间
- Modify:文件最后修改时间
- Change:属性最后修改时间
[---@VM-8-4-centos day03]$ stat day03.cc
一般来说,Modify改变会引起Change一起变。因为修改文件内容时,文件的大小一般也会随之改变。但是Change改变却不会引起Modify改变,因为修改文件属性一般不会影响到文件内容。
使用touch命令可以将文件都更新为最新时间。
动静态库
什么是动静态库
- 动态库:程序在运行的时候才去链接动态库的代码,多个程序共享使用库的代码。(*.so)
- 静态库:程序在编译链接的时候把库的代码链接 (拷贝) 到可执行文件中,程序运行的时候将不再需要静态库。(*.a)
怎么理解动静态库
原理
生成一个可执行程序需要经过:预处理、编译、汇编、链接,4个阶段。
最终在汇编完成后就会*.o
文件,将这样的经常使用的多个*.o
文件组合起来就是一个简单的库。因为当需要使用到这个库中的哪个*.o
文件,我们直接进行最后一步链接就行,省去了前3个步骤。
实际上,库都是这样做的。把常用的头文件或者函数调用汇编后组合成一个库,以供大家方便使用。
使用
我们可以通过以下命令查看可执行文件链接的库:
[---@VM-8-4-centos day03]$ ldd mycode
我们可以通过ls命令查看libc.so.6
库的情况:
[wsj@VM-8-4-centos day03]$ ls /lib64/libc.so.6 -l
我们还可以通过以下命令来查看这个库的详细信息:
[---@VM-8-4-centos day03]$ file /lib64/libc-2.17.so
我们可以看到这是一个动态库,而且还是一个共享的目标文件库。
我们使用的gcc/g++都是动态链接的,如果想要静态链接,我们要在编译时加上-static
。
我这边直接加在makefile
里:
这样我们make
生成的就是静态链接的可执行程序了。
这可比我们之前生成的动态链接的可执行程序大多了,但是它不依赖其他的库。
我们再使用file命令,也能看出它是一个静态链接的可执行程序。
制作一个库
前置准备
add.h
#pragma once
#include <stdio.h>
extern int add(int x, int y);
add.c
#pragma once
#include "add.h"
int add(int x, int y)
{
return x + y;
}
sub.h
#pragma once
#include <stdio.h>
extern int sub(int x, int y);
sub.c
#pragma once
#include "sub.h"
int sub(int x, int y)
{
return x - y;
}
main.c
#include "add.h"
#include "sub.h"
int main()
{
int a = 10, b = 20;
int c = add(a, b);
int d = sub(a, b);
printf("%d + %d = %d\n", a, b, c);
printf("%d - %d = %d\n", a, b, d);
return 0;
}
自制静态库
首先,我们将它们汇编为可重定向二进制目标文件:
gcc -c *.c -o *.o
然后,我们使用以下命令将目标文件打包为静态库:
- ar命令是gnu的归档工具,常用于打包静态库
-r
:如果静态库文件当中的目标文件有更新,就用新的目标文件替换旧的目标文件。-c
:生成静态库文件。-t
:列出静态库中的文件。-v
:显示详细的信息。
[---@VM-8-4-centos day03]$ ar -rc liblocal.a add.o sub.o
我们可以使用-v-t选项查看生成的静态库的详细信息:
[wsj@VM-8-4-centos day03]$ ar -vt liblocal.a
最后,将头文件和生成的静态库联系起来。
[---@VM-8-4-centos day03]$ mkdir -p mymathlib/include
[---@VM-8-4-centos day03]$ mkdir -p mymathlib/lib
[---@VM-8-4-centos day03]$ cp *.h mymathlib/include/
[---@VM-8-4-centos day03]$ cp liblocal.a mymathlib/lib/
使用自制的静态库
将main.c
和mymathlib
移动到一个新的目录下。
使用gcc
的选项-I
和-L
生成可执行程序。
-I
:指定头文件路径-L
:指定库文件路径-l
:指定路径下的哪一个库
[---@VM-8-4-centos day04]$ gcc -o mycode main.c -I ./mymathlib/include -L ./mymathlib/lib -l local
然后,我们正常运行即可。
我们也可以将我们的库拷贝到系统目录下,这样系统就可以找到我们使用的库,我们就不用指定路径了:
[---@VM-8-4-centos day04]$ sudo cp mymathlib/include/* /usr/include/
[---@VM-8-4-centos day04]$ sudo cp mymathlib/lib/liblocal.a /lib64/
自制动态库
首先生成*.o
文件。
-fPIC
:形成位置无关码
[---@VM-8-4-centos day03]$ gcc -fPIC -c add.c sub.c
然后,使用-shared
选项打包目标文件为库。
最后,将头文件和库联系起来。
[---@VM-8-4-centos day03]$ mkdir -p mymathlib/include
[---@VM-8-4-centos day03]$ mkdir -p mymathlib/lib
[---@VM-8-4-centos day03]$ cp *.h mymathlib/include/
[---@VM-8-4-centos day03]$ cp liblocal.so mymathlib/lib/
使用自制的动态库
将main.c
和mymathlib
移动到同一个目录下。
使用以下命令(和刚才使用静态库基本一样):
[wsj@VM-8-4-centos day04]$ gcc -o mycode main.c -I ./mymathlib/include -L ./mymathlib/lib -l local
我们此刻执行后却是无法运行。
原因是生成的可执行程序依赖的动态库没有被找到。
解决方法:
方法1:将动态库拷贝到系统目录下。
sudo cp mymathlib/lib/liblocal.so /lib64
方法2:添加环境变量
[---@VM-8-4-centos day04]$ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/wsj/bit/day04/mymathlib/lib
方法3:配置/etc/ld.so.conf.d/
[---@VM-8-4-centos day04]$ echo /home/wsj/bit/day04/mymathlib/lib > matH.conf
[---@VM-8-4-centos day04]$ sudo cp matH.conf /etc/ld.so.conf.d
完成任意一种解决方法后运行即可。
感谢您的阅读,欢迎指正。