C++学习笔记day22-----UC

环境变量
加载到内存中的程序叫做进程,一个程序可以被多次加载,生成多个进程,每个进程之间相互独立。
在linux中,每一个进程都有自己独立的环境变量,举一个典型的例子:bash
env,在bash中可以通过env这个指令来查看当前bash的所有环境变量。
环境变量的格式如下:
name=value
在name和value中都是不允许存在空格。
一个bash中可以声明私有的变量,只能在bash内使用,无法继承给bash的子程序
echo,在bash中可以使用echo来查看一个变量的值,echo $name
|,管道符号,将管道之前的指令的标准输出作为管道之后的命令的标准输入
grep,筛选符号,可以在标准输入中/文件筛选出需要的内容,|grep “name”,grep “name” filename
以下是一个bash的环境变量,特别讲解一下PS1和PATH这两个环境变量
PS1

linxin@ubuntu:~$ echo $PS1
\[\e]0;\u@\h: \w\a\]${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$

PS1这个环境变量指的是bash中的提示符格式,就是“linxin@ubuntu:~$”这个东西,可以通过更改这个环境变量的值来制定提示符的格式。如下:

linxin@ubuntu:~$ export PS1="\W\$"
~$
~$cd /
/$cd home
home$cd linxin/
~$

将PS1的内容更改为”\W$”之后,bash的提示符就变成了当前目录的目录名称。
PATH
PATH是bash的环境变量,可以用如下命令来查看:

linxin@ubuntu:~$ env |grep "PATH"
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin

PATH的值,是一些用冒号分隔的路径。这些路径都是用于bash寻找命令的。
当在bash中输入“ls”这样一个指令的时候,bash会先到 /usr/local/sbin 中寻找是否有这个命令,按照PATH里的路径依次寻找,只有找到这个命令之后,才会停止寻找,并执行这个命令。如果找完所有的路径,依然找不到命令,就会报错“commond not find”。
如果想在运行当前目录下的可执行文件,且不想加上 ./ 符号,可以通过对PATH更改来实现。如下:

els$export PATH=$PATH:.
els$els

进入到els文件夹之后,用 “export PATH=$PATH:.”将当前目录添加到PATH的值中,然后直接运行els这个可执行文件。

上述的两个变量的值,都在当前的bash内有效,如果关掉这个bash,重新开启一个bash,它们就会失效,因为改变仅仅只作用于当前的bash。
在每个bash启动的时候,都会先调用 ~/.bashrc 这个脚本,可以将改变PATH和PS1的值的指令写入这个文件,那么每次开启一个新的bash的时候,都会对当前的bash进行设置。

最后强调一点,linux是严格区分大小写的,环境变量尽可能使用全部大写。
制作和使用动态库
动态库的命名方式:lib+库名.so
步骤一:将所有要加入到动态库中的.c源文件,都编译成目标文件(与目标位置无关的目标文件)
gcc -c -fPIC *.c,参数 -fPIC 就是编译成与位置无关的目标文件的选项。
步骤二:将所有要加入到动态库中的.o目标文件,进行打包
gcc -shared -o libpmath.so *.o,将当前目录下所有的.o文件都打包放入libpmath.so的动态库文件中。
步骤三:使用动态库
gcc main.c -L. -lpmath ,将主函数所在文件和库进行链接生成可执行文件。

上述三个步骤结束之后,运行最后生成的可执行文件,a.out 会得到如下报错:
error while loading shared libraries:libpmath.so:cannot open shared object file :NO such file or directory

当一个程序加载到内存的时候,系统会调用加载器来完成这项任务。
加载器遇到程序中对动态库中的函数的调用的时候,会到加载器的默认路径下去寻找这个动态库。
上述的报错,就是加载器在默认的路径里找不到相对应的动态库导致的。

可以通过ldd这个指令来验证上述原理。如下:

linxin@ubuntu:~/biaoc/els$ ldd a.out
    linux-vdso.so.1 =>  (0x00007ffd791c5000)
    libels.so => not found
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f39704b8000)
    /lib64/ld-linux-x86-64.so.2 (0x00007f3970a9b000)

在a.out可执行文件中,一共有四个关联的动态库,只有libels.so这个自定义的动态库是 not find,其他的几个动态库都能找到相应的内存地址。
解决这个问题的方法有四种,这里只介绍两种:
1、通过环境变量来解决
LD_LIBRARY_PATH,这个环境变量里的值,告诉加载器可以到哪些路径下找到相应的动态库文件。如下:

linxin@ubuntu:~/biaoc/els$ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:.
linxin@ubuntu:~/biaoc/els$ ldd a.out
    linux-vdso.so.1 =>  (0x00007fff70177000)
    libels.so (0x00007fbd1972c000)
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fbd1934c000)
    /lib64/ld-linux-x86-64.so.2 (0x00007fbd19b35000)

这一次,libels.so就有了对应的内存地址。
2、将自定义的动态库文件,放入到加载器默认的路径下,加载器默认的路径有/lib或者/usr/lib
(关闭之前bash,让LD_LIBRARY_PATH失效),如下:

linxin@ubuntu:~/biaoc/els$ a.out 
a.out: error while loading shared libraries: libels.so: cannot open shared object file: No such file or directory
linxin@ubuntu:~/biaoc/els$ sudo cp libels.so /lib
linxin@ubuntu:~/biaoc/els$ ldd a.out 
    linux-vdso.so.1 =>  (0x00007ffd373ad000)
    libels.so => /lib/libels.so (0x00007f3e0e2e2000)
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f3e0df02000)
    /lib64/ld-linux-x86-64.so.2 (0x00007f3e0e6eb000)
linxin@ubuntu:~/biaoc/els$ 

可以看到,这次libel.so的路径和上一次不同。

静态链接:,在生成可执行文件的时候,链接器会把main函数所在文件里有用到的静态库中的函数加载到可执行文件中,链接到一起。所以可执行文件是不依赖静态库的。
动态链接:,在可执行文件加载到内存的时候,由加载器负责将可执行文件用到的函数链接上来。所以可执行文件对动态库具有依赖性。

动态库又称为共享库。
当一个程序被两次加载的时候,回生成两个进程,这两个进程里都有一份静态库内容。
而动态库则不同,这两个进程对动态库中的函数调用,不是加载其函数实现,而是通过指针指向内存里动态库函数*所在地址,这就导致了,两个进程只需要加载一份动态库到内存即可。*

动态加载
动态加载与动态库加载是两码事。
动态库的加载发生在程序加载到内存中时,而动态加载则发生在程序运行过程中。
可以通过一组函数,让程序在运行过程中,按照需要加载动态库中的部分函数。

void *dlopen(const char *filename, int flags);
Link with -ldl.
功能:
加载动态库文件,返回一个地址(和加载的动态库关联的地址,这个地址会用于dlopen的其他API中)
参数:
filename:指定了要加载的动态库的库文件名称,
flags:
二选一
RTLD_LAZY:懒加载,在执行的时候,才绑定
RTLD_NOW:立即加载,在dlopen返回之前,已经绑定。
返回值:
错误 NULL
成功 加载的动态库的地址

int dlclose(void *handle);
功能:
动态库的引用技术减1,如果引用到0,动态库卸载。

char *dlerror(void);
功能:
获取最近一次调用动态库加载库文件函数的错误信息
返回值:
NULL 没有错误
非空:字符串 描述了最近一次调用库函数产生的错误信息

void *dlsym(void *handle, const char *symbol);
功能:
获取共享库里的函数的加载地址
参数:
handle:dlopen的返回值,动态库加载的地址
symbol:指定了要找的符号的名字,函数的名字属于符号名字的一种
返回值:
NULL 找不到这个符号
非空,找到的这个符号加载到内存的地址

写了一个Demo,这个Demo在运行过程中,加载了俄罗斯方块的动态库中,俄罗斯方块结构体的初始化函数,地图初始化函数,显示地图函数,并成功打印。注意,在使用上述的函数时,需要在目标文件链接时,将dl的库也链接进来,这些函数都是在动态库dl中。代码如下:

#include<stdio.h>
#include<dlfcn.h>
#include"/home/linxin/biaoc/els/els.h"
typedef ElsType *new_t(void);
typedef void init_t(ElsType*);
typedef void display_t(ElsType*);
int main(void){
    //加载动态库,libels
    void *p = dlopen("libels.so",RTLD_NOW);
    if(p == NULL){
        printf("%s\n",dlerror());
        return -1;
    }
    printf("loaded lib success\n");
    //在动态库文件中找函数,加载到内存的地址
    void *new = dlsym(p,"els_new");
    void *init = dlsym(p,"InitMap");
    void *display = dlsym(p,"Display");

    if(new == NULL || init == NULL || display == NULL){
        printf("%s\n",dlerror());
        return -1;
    }
    //已经获取到Display这个函数的入口地址f
    new_t *new_t_f = (new_t *)new;
    init_t *init_t_f = (init_t *)init;
    display_t *display_t_f = (display_t *)display;

    ElsType *els_t = new_t_f();
    init_t_f(els_t);
    display_t_f(els_t);
    //动态库的引用结束减一
    dlclose(p);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/displaymessage/article/details/80170489