C语言标准库的接口
stdin/stdout/stderr
重定向
系统文件接口
文件描述符:
我们上面说的open 等系统调用接口,其返回值为一个int,注意这里的int指的是内核中用于管理文件系统的数组的下标。
如上图,再一个进程task_struct中,会有一个files* 类型的指针,其指向的正是以恶搞file_struct的结构体。而在这么一个结构体里面,又存在着一个存储着file*的指针数组,数组的下标从0开始,一直到n(n实际上也是有上限的)。每打开一个新的文件之后,其就会为
动静态库
对于动静态库,我们最直白的理解就是:
对于动态库而言,在执行的时候,我们所用的库函数是通过链接的形式进行的;
而对于静态库而言,我们执行的时候,我们需要先将第三方库的所有代码拷贝到本地,然后再生成我们的可执行程序。
我们可以来简单的实验一下:
我们写入一个简单的程序:
这里,我们就可以查看到其所链接的动态库。
关于动态库,其通常为libname.so.version
就是说,去掉前面的lib,去掉后面的.so.version,那么最终得到的就是一个库的名字。
所以,这里上面所展示的库,就是C库。
我们可以看看这个库的目录
(实际上,其是一个软连接)
其具体的原理:
当我们的代码自上而下运行的时候, 发现你的代码中并未包含printf的实现,于是,其会到所包含的动态库中去查找printf这部分的代码,将其执行完毕之后,再返回来。
这么做的好处就是其不需要将整个库里面的代码都拷贝到我们的代码当中,直接从外部的动态库中链接。这么做也有利于节省空间。我需要了,才去调用。
并且整个系统的动态库代码也可以就只有一份。
动态库体积小,但是这么做,也使得代码的执行要依赖外部的库。
如果用静态库,那么其文件的大小可能就会是用静态库大小的十几倍。
我们可以将两个文件对比一下:
那么其执行的过程和动态库相比,也是有区别的:
当我的代码执行到printf的时候,我会到库中去找到这个printf的执行、定义,然后不是想动态库那样,直接在动态库里将printf的内容执行完毕之后返回,而是将库里的printf代码拷贝到本地的代码当中,然后去执行。
所以,静态链接优点也显而易见了:将对应的代码拷贝进程序中,不依赖第三方库而独立运行。可以移植。在生成可执行程序之后,在任何地方都可以运行(注意,这里指不依赖第三方库指的是在生成可执行程序之后,将可执行程序运行起来的时候)
静态库的时候在链接的时候纳入进来。
而静态库可能比较占资源(占硬盘和内存的资源,占硬盘是因为代码会拷贝进文件当中;而占内存是因为如果有多个文件,如果大家都是静态链接,在运行起来、加载到内存的时候,那么在内存中就会有多份重复的代码,占用内存的资源)
我们再来从进程地址空间的角度来理解一下动静态库:
对于动态库,如果我在执行我的文件过程中(比如mybin),需要去链接一个动态库(比如是libc.so),那么在进程执行的时候,该动态库文件会被加载到物理内存当中,然后再通过页表的映射关系,映射到堆区和栈区中间的那一块空间,然后我们在代码区的代码就会直接去所映射的地方去执行。而如果是静态库,那么所需要的静态库的代码就都会加载到代码区,然后在代码区中执行。
注意,这里将库里面的代码导入,也不是一股脑的无脑式的直接塞入,而是以页为单位的、分批式地、以需求为导向的导入。
一个程序当中的一批头文件,告诉了你什么方法可以用,参数接口是什么意思(相当于函数的声明);而一个或多个库文件,其是这些接口具体的实现,供我们动静态去链接。
在文件的生成过程当中,我们会经历预处理、编译、汇编、链接等阶段,那么链接的本质,就是将生成的.o文件进行打包,然后再去链接库里面的代码。
我们也可以自己手动生成一个库。
我们先在myadd的文件夹中写好所需要的myadd.h和myadd.c(在里面有Add函数的声明和实现)
然后再将其拷贝到当前目录下,
再创建一个源文件,用于执行的,里面可以调用Add函数。(注意也要包含头文件)
然后,将所有的.o文件打包,生成库。
我们这里就先生成一个静态库。
然后呢,我们创建一个文件夹(假如叫mylib),然后将该静态库、myadd.h都给拷贝进去,然后该删的都给其删掉。
就剩下这样:(myfile.c是有main函数接口的文件)
然后,我们需要将Makefile里面的内容改一下:(否则我们在myfile.c里面包含的头文件以及Add函数都是找不到的)
怎么改呢?
我们需要加上几个选项:
当然,你也可以后面再跟上个-static表示静态链接,但是这里呢由于笔者的服务器上暂时还没有装,就不加了。
就是说,C库我们是动态链接的,而Add函数,我们是静态链接的。
make以后:
这样以后,可执行文件就生成了。
可以运行下试试看。
(注意:上述的.h .o文件可以有多个,方法是一样的)
如果不像带上-I -L这些乱七八糟的选项,参考环境变量的方法。就是把这.h文件拷贝到系统默认的路径底下,但是l(小L)后面的还是要带的(也可以有方法不带)
(Makefile可以自动帮我们补充绝对路径)
我们平时所说的写代码就是拷贝拷贝,就是说,从网站上,将别人的代码、别人写好的库拷贝下来,链接到你的代码当中。你通过.h文件可以知道每一个函数应该怎么去用,而别人为你写好的库,通过你的链接调用,就可以实现你的二次开发。
那关于动态库的生成呢?
我们还是先去写几个简单的文件:
如图:
然后在Makefile里面:
然后直接make,就会发现生成了一个.so的动态库
如果想要形成一个发布版本的呢?
我们可以加上这些东西:
那么我们接下来,想要生成发布版本,直接make output
再次查看,我们就会发现已经生成了一个mylib的文件
那么如果我想将我写的代码给别人,直接将我的mylib给别人就可以了。
这就叫做发布。
然而,在运行的时候,还是存在一定的问题的:
我们用写一份代码,然后用这份动态库链接生成可执行文件。
如下图:
(剩下的.o和.h文件都是我们之前常写的实验代码)
生成可执行文件之后,我们会发现,其似乎并不能很好的运行:
它说啥呢?
它说找不到这么一个动态库文件?
那就奇怪了。
我们明明已经告诉了它该如何找、怎样去找了。为什么还会存在问题呢?
理由其实很简单。
我们在Makefile里面做的事情,实际上是在编译的时候,告诉编译器我的库在什么地方。
但是生成可执行程序之后呢?
有没有告诉你的操作系统你的库在哪里呢?
没有。
那怎么办呢?
我们需要编辑更改一个环境变量:LD_LIBRARY_PATH
我们这样来操作:
这样的话,就将我们运行动态库的环境变量设置完毕了。这里的路径就找到了。
为什么要设置呢?以前为什么没有设置过?
因为一般情况下,我们所有的动态库都是在系统默认的动态库路径当中。而我们这里所用的是第三方库,所以必须要将路径带上。
我们再来运行的时候,就没有问题了。
我们现在来看看加上重定向的shell该怎么弄。