在Linux下安装新的第三方库时,经常要先配置环境变量从而让我们的编译器能够找到相应的头文件以及库文件。库文件在连接(静态库和共享库)和运行(仅限于使用共享库的程序)时被使用,其搜索路径是在系统中进行设置的。一般Linux系统把/lib和/usr/lib两个目录作为默认的库搜索路径,所以使用这两个目录中的库是不需要进行设置搜索路径即可直接使用。对于处于默认库搜索路径之外的库,需要将库的位置添加到库的搜索路径之中。设置库文件的搜索路径有下列几种方式,可任选其一使用:
ldconfig命令
ldconfig命令的用途, 主要是在默认搜寻目录(/lib和/usr/lib)以及动态库配置文件/etc/ld.so.conf内所列的目录下,
搜索出可共享的动态链接库(格式如lib*.so*),
进而创建出动态装入程序(ld.so)所需的连接和缓存文件.
缓存文件默认为/etc/ld.so.cache, 此文件保存已排好序的动态链接库名字列表.
这里面涉及到的关键内容有命令:ldconfig,配置文件目录:/etc/ld.so.conf.d,
配置文件在/etc/ld.so.conf内容由用户编辑,缓冲文件/etc/ld.so.cache。
详细查看一下里面的内容会发现:
1. 打开/etc/ld.so.conf,发现为:include /etc/ld.so.conf.d/ *.conf
2. 进入etc下的ld.so.conf.d文件夹内发现很多以.conf为后缀的文件。这里面的内容就是指向库位置的路径。比如,我使用的电脑力有个cuda.conf的文件,打开里面的内容为:/usr/local/cuda/lib64,跳转到该路径。
3. 在/usr/local/cuda/lib64内,里面有很多静态(.a)和动态库文件(.so)
下面举个例子,比如你在部署软件时,有些动态库安装在/usr/local/gcc9/lib64目录下,可以通过如下方法实现,
1)配置gcc.conf文件,里面加一行/usr/local/gcc9/lib64,然后将该文件放到/etc/ld.so.conf.d目录下;
2)执行 ldconfig命令
之后程序运行时,会自动增加在/usr/local/gcc9/lib64目录中搜索动态库。
在终端窗口,可以通过ldconfig的命令参数查看一些相关信息。
ldconfig -V,可以查看ldconfig的版本信息。
ldconfig -p 可以查看当前ldconfig保存的共享库文件名称
使用~/.bashrc配置
可以通过在.bashrc或者.cshrc中配置该环境变量,LD_LIBRARY_PATH的意思是告诉loader在哪些目录中可以找到共享库.
可以设置多个搜索目录, 这些目录之间用冒号分隔开.
同样是上面的例子,可以通过以上的方法来实现
在.bashrc或.cshrc中增加一行,
export LD_LIBRARY_PATH = /usr/local/gcc9/lib64:$LD_LIBRARY_PATH
即可。
那么这个bashrc文件在哪里呢?他在每个用户的用户文件夹下,就是终端键入‘cd ~’进入的那个。并且bashrc文件是一个隐藏文件夹,因此正确的访问路径应该是‘~/.bashrc’。
export PATH=/usr/local/cuda-10.0/bin:$PATH
export LD_LIBRARY_PATH=/usr/local/cuda-10.0/lib64:$LD_LIBRARY_PATH
这个是当时配置cuda时配置的,在~/.bashrc的最后。
export的作用是怎样的,它在UNIX教程里有这样的说明:一个变量创建时,它不会自动地为在它之后创建的shell进程所知。而命令export可以向后面的shell传递变量的值。当一个shell脚本调用并执行时,它不会自动得到原为脚本(调用者)里定义的变量的访问权,除非这些变量已经被显式地设置为可用。export命令可以用于传递一个或多个变量的值到任何后继脚本。
在终端export可以设置一些环境变量,也可以使用一些参数查看一些信息:
参 数:
-f 代表[变量名称]中为函数名称。
-n 删除指定的变量。变量实际上并未删除,只是不会输出到后续指令的执行环境中。
-p 列出所有的shell赋予程序的环境变量。
也可打印一些特定的环境变量值:
echo $PATH、echo $LD_LIBRARY_PATH等等
其设置规则为“PATH=$PATH:路径1:路径2:...:路径n ”。意思是可执行文件的路径包括原先设定的路径,也包括从“路径1”到“路径n”的所有路径。当用户输入一个一串字符并按回车后,shell会依次在这些路径里找对应的可执行文件并交给系统核心执行。那个“PATH:路径1:路径2:...:路径n”,意思是可执行文件的路径包括原先设定的路径,也包括从“路径1”到“路径n”的所有路径。当用户输入一个一串字符并按回车后,shell会依次在这些路径里找对应的可执行文件并交给系统核心执行。那个“PATH”表示原先设定的路径仍然有效,注意不要漏掉。还有其他的环境变量,例如动态库的路径:LD_LIBRARY_PATH,总之就是其他环境变量=$其他环境变量:... 。还需要注意“$”不要漏写。
小结:
上面介绍了两种库的配置方式,需要说明的是,ldconfig是配置动态库的管理命令,其目的为了让动态链接库为系统所共享。而bashrc文件时配置整个Linux环境变量的,通过设置特定的环境变量即可设置多种类型的变量,例如动态库路径的变量为:LD_LIBRARY_PATH,指明放置.so库路径、PATH,可执行的二进制文件路径、PKG_CONFIG_PATH,指明放置.pc库路径。
pkg-config和ldconfig
我们知道,linux编译源码包基本步骤无非是:configure,make,make install三部曲;configure过程中可能会遇
到无法找到某些头文件和动态库;原因有两个:
(1)系统压根就没有这些头文件和动态库。(locate XXXX.h/so 未找到)
(2)已经安装相关的头文件和动态库。但未将头文件和动态库拷贝到标准路径下。
对于第(2)中情况,我们可以通过配置PKG_CONFIG_PATH环境变量来解决头文件和动态库无法找到的问题;
先介绍一下pkg-config的用法:
pkg-config是生成链接参数的程序,其相关命令经常被用于Makefile文件用来找寻头文件和动态链接库,最常用的方式:
[root@localhost ~]# pkg-config --cflags --libs libcfg
打印信息:
-I/ext/corosync/include -L/ext/corosync/lib -lcfg
上述命令用于返回libcfg.so的链接路径,以及相关的头文件路径。上述命令为打印libcfg.pc文件内的--cflags --libs对应变量的信息。
pkg-config是如何找到libcfg.so的存放路径和头文件的存放路径的呢?pkg-config会查找libcfg.pc文件;该文件存
放了头文件和动态链接库的路径存放信息;libcfg.pc文件内容如下:
prefix=/ext/corosync
exec_prefix=${prefix}
libdir=/ext/corosync/lib
includedir=${prefix}/include
Name: cfg
Version: 1.3.3
Description: cfg
Requires:
Libs: -L${libdir} -lcfg
Cflags: -I${includedir}
那么pkg-config如何找到libcfg.pc文件的呢?相信同学们肯定猜到了,就是PKG_CONFIG_PATH环境变量;一般情况下动态库都存放在安装目录下的lib目录下,libcfg.so存放在/ext/corosync/lib/目录下;同时该目录下有一个pkgconfig目录,相信经常编译源代码的同学对这个目录并不陌生,该目录下存放的就是*.pc文件,存放着我们的动态库和头文件路径元数据。通过设置PKG_CONFIG_PATH为我们/ext/corosync/lib/pkgconfig;pkg-config命令就可以轻松找到*.pc文件,进而找到路径信息。
好了,经过上述设置后,configure便可以顺利的生成Makefile文件了。make,make install就可以了。由此也可以看出pkg-config就是向configure程序提供系统信息的程序,比如软件的版本啦,库的版本啦,库的路径啦,等等。这些信息只是在编译其间使用,到此为止,我们仅仅完成了程序的编译和链接,还有最后一步,程序运行;程序运行需要加载动态链接库并运行,也需要找寻动态链接库的路径,pkg-config仅负责编译链接时头文件和动态链接库的查找。对于程序运行时加载动态链接库,pkg-config就无能为力了,此时需要用到ldconfig命令,该命令为程序运行时提供动态链接库的运行时绑定。由此就用到了前面ldconfig介绍的一些功能了。
引用自:https://blog.csdn.net/u011956722/article/details/41042235
~/.bashrc 、/etc/bash.bashrc、/etc/profile、~/.bash_profile联系
上面介绍的~/.bashrc 只是其中一种配置方法,类似的还可以在其他文件中配置。例如:
/etc/profile:此文件为系统的每个用户设置环境信息,当用户第一次登录时,该文件被执行. 并从/etc/profile.d目录的配置文件中搜集shell的设置.
/etc/bashrc:为每一个运行bash shell的用户执行此文件.当bash shell被打开时,该文件被读取.
~/.bash_profile:每个用户都可使用该文件输入专用于自己使用的shell信息,当用户登录时,该 文件仅仅执行一次!默认情况下,他设置一些环境变量,执行用户的.bashrc文件.
~/.bashrc:该文件包含专用于你的bash shell的bash信息,当登录时以及每次打开新的shell时,该文件被读取.
~/.bash_logout:当每次退出系统(退出bash shell)时,执行该文件.
另外,/etc/profile中设定的变量(全局)的可以作用于任何用户,而~/.bashrc等中设定的变量(局部)只能继承/etc/profile中的变量,他们是"父子"关系.
~/.bash_profile 是交互式、login 方式进入 bash 运行的
~/.bashrc 是交互式 non-login 方式进入 bash 运行的
通常二者设置大致相同,所以通常前者会调用后者。
下面是在本机的几个例子:
1. 图形模式登录时,顺序读取:/etc/profile和~/.profile
2. 图形模式登录后,打开终端时,顺序读取:/etc/bash.bashrc和~/.bashrc
3. 文本模式登录时,顺序读取:/etc/bash.bashrc,/etc/profile和~/.bash_profile
4. 从其它用户su到该用户,则分两种情况:
(1)如果带-l参数(或-参数,--login参数),如:su -l username,则bash是lonin的,它将顺序读取以下配置文件:/etc/bash.bashrc,/etc/profile和~/.bash_profile。
(2)如果没有带-l参数,则bash是non-login的,它将顺序读取:/etc/bash.bashrc和~/.bashrc
5. 注销时,或退出su登录的用户,如果是longin方式,那么bash会读取:~/.bash_logout
6. 执行自定义的shell文件时,若使用“bash -l a.sh”的方式,则bash会读取行:/etc/profile和~/.bash_profile,若使用其它方式,如:bash a.sh, ./a.sh,sh a.sh(这个不属于bash shell),则不会读取上面的任何文件。
7. 上面的例子凡是读取到~/.bash_profile的,若该文件不存在,则读取~/.bash_login,若前两者不存在,读取~/.profile。
也可以按照下面这些步骤理解:
/etc/profile,/etc/bashrc 是系统全局环境变量设定
~/.profile,~/.bashrc用户家目录下的私有环境变量设定
当登入系统时候获得一个shell进程时,其读取环境设定档有三步
1. 首先读入的是全局环境变量设定档/etc/profile,然后根据其内容读取额外的设定的文档,如
/etc/profile.d和/etc/inputrc
2. 然后根据不同使用者帐号,去其家目录读取~/.bash_profile,如果这读取不了就读取~/.bash_login,这个也读取不了才会读取
~/.profile,这三个文档设定基本上是一样的,读取有优先关系
3. 然后在根据用户帐号读取~/.bashrc
至于~/.profile与~/.bashrc的共同点:都具有个性化定制功能
~/.profile可以设定本用户专有的路径,环境变量,等,它只能登入的时候执行一次
~/.bashrc也是某用户专有设定文档,可以设定路径,命令别名,每次shell script的执行都会使用它一次
亦或使用简约方式描述即为:
登陆shell
登陆shell时,首先执行/etc/profile
,之后执行用户目录下的~/.profile
,~/.profile
中会执行~/.bashrc
。
交互式非登陆shell
首先执行/etc/bash.bashrc
,之后执行~/.bashrc
source 命令
通过上面的介绍,我们知道如果配置了新的环境变量,想要这些配置的环境更新,除了关机重启就是使用source命令了。
source命令用法:
source FileName
作用:在当前bash环境下读取并执行FileName中的命令。
注:该命令通常用命令“.”来替代。
如:source .bash_rc 与 . .bash_rc 是等效的。
source命令(从 C Shell 而来)是bash shell的内置命令。点命令,就是个点符号,(从Bourne
Shell而来)是source的另一名称。同样的,当前脚本中配置的变量也将作为脚本的环境,source(或点)命令通常用于重新执行刚修改的初始化
文档,如 .bash_profile 和 .profile 等等。例如,假如在登录后对 .bash_profile 中的 EDITER 和
TERM 变量做了修改,则能够用source命令重新执行 .bash_profile 中的命令而不用注销并重新登录。
总之类似于使用ldconfig配置后使用命令‘ldconfig’更新一下。
上面的介绍部分引用自https://www.cnblogs.com/bandiao/p/10805749.html
etc、/bin、/dev、/lib、/sbin、/usr等目录作用
/bin 二进制可执行命令
/dev 设备特殊文件
/etc 系统管理和配置文件
/etc/rc.d 启动的配置文件和脚本
/home 用户主目录的基点,比如用户user的主目录就是/home/user,可以用~user表示
/lib 标准程序设计库,又叫动态链接共享库,作用类似windows里的.dll文件
/sbin 系统管理命令,这里存放的是系统管理员使用的管理程序
/tmp 公用的临时文件存储点
/root 系统管理员的主目录(呵呵,特权阶级)
/mnt 系统提供这个目录是让用户临时挂载其他的文件系统。
/lost+found 这个目录平时是空的,系统非正常关机而留下“无家可归”的文件(windows下叫什么.chk)就在这里
/proc 虚拟的目录,是系统内存的映射。可直接访问这个目录来获取系统信息。
/var 某些大文件的溢出区,比方说各种服务的日志文件
/usr unix system resrouces的缩写,最庞大的目录,要用到的应用程序和文件几乎都在这个目录。其中包含:
/usr/X11R6 存放X window的目录
/usr/bin 众多的应用程序
/usr/sbin 超级用户的一些管理程序
/usr/doc linux文档
/usr/include linux下开发和编译应用程序所需要的头文件
/usr/lib 常用的动态链接库和软件包的配置文件
/usr/man 帮助文档
/usr/src 源代码,linux内核的源代码就放在/usr/src/linux里
/usr/local/bin 本地增加的命令
/usr/local/lib 本地增加的库
实例
我们以安装opencv的Linux版本为例子,总结一下库的配置。具体过程可以查看该博客,这里只讨论配置这块。从第四步:
打开opencv.conf文件:(这个文件可能不存在,但是它会被创建。)
sudo vim /etc/ld.so.conf.d/opencv.conf
在最后一行添加该代码(/usr/local/lib)。表示编译之后生成的opencv库的位置所在
运行以下代码配置库:
sudo ldconfig
到这里就是使用ldconfig配置了动态库的位置。
到了第五步:
打开bash.bashrc:
sudo gedit /etc/bash.bashrc
在这个文件的末尾添加下面两行代码来添加环境变量:
PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/usr/local/lib/pkgconfig
export PKG_CONFIG_PATH
然后重启配置文件:source /etc/bash.bashrc
到这里为pkg-config设置环境变量PKG_CONFIG_PATH,告诉编译器,该路径下有一个.pc文件,该pc文件包含编译和链接时的头文件和库文件路径、版本信息等一些信息。
由于opencv4.0做了一些修改,这里在include时也做了一些改变,具体在/usr/local/lib/pkgconfig下的opencv.pc文件里。
#include <stdio.h>
#include <opencv4/opencv2/opencv.hpp>
using namespace cv;
int main(int argc, char** argv )
{
Mat image;
image = imread("/home/szx/Desktop/tt/2.png", 1 );
if ( !image.data )
{
printf("No image data \n");
return -1;
}
namedWindow("Display Image", WINDOW_AUTOSIZE );
imshow("Display Image", image);
waitKey(0);
return 0;
}
然后使用
命令行:g++ `pkg-config --cflags opencv` -o TestOpencv TestOpencv.cpp `pkg-config --libs opencv`,进行编译。形成了一个可执行文件。可以看出 g++ 后面原生的一些-L,-I的参数(用来设置动态库位置和头文件)都直接用pkg-config --cflags opencv命令来代替了,主语这里冒号,他是键盘上‘1’左边的那个键,不是普通的冒号。
运行该执行文件:./TestOpencv
大家可以打开opencv.pc看一下里面的内容,或者删除它以后再进行编译会产生什么样的错误。有一次安装opencv4.0的时候,结果没有生成open.pc文件,这里贴出相应代码:
prefix=/usr/local
exec_prefix=${prefix}
libdir=${exec_prefix}/lib
includedir_old=${prefix}/include/opencv4/opencv2
includedir_new=${prefix}/include/opencv4
Name: OpenCV
Description: Open Source Computer Vision Library
Version: 4.1.0
Libs: -L${exec_prefix}/lib -lopencv_dnn -lopencv_gapi -lopencv_highgui -lopencv_ml -lopencv_objdetect -lopencv_photo -lopencv_stitching -lopencv_video -lopencv_calib3d -lopencv_features2d -lopencv_flann -lopencv_videoio -lopencv_imgcodecs -lopencv_imgproc -lopencv_core
Libs.private: -ldl -lm -lpthread -lrt
Cflags: -I${includedir_old} -I${includedir_new}
可执行文件在执行时为什么要加 ./
原因是:可执行文件的目录没有包含在环境变量PATH中,linux系统只会搜索PATH变量。而windows下却可以是因为系统会搜索PATH变量和当前目录。因此,如果可执行文件是在/bin , /usr/bin 或是 /sbin ,usr/sbin 目录下(具体情况由环境变量决定),直接输入文件名即可运行,即,TestOpencv;如果可执行文件是在其他文件目录下时,采用一种不同的方式。具体做法是加上“./”再输入文件名,即 ./TestOpencv. 说了那么多废话,总之就是在当前文件下下执行TestOpencv。如果TestOpencv的路径在home/user/file/TestOpencv 中,那么在命令行中直接执行home/user/file/TestOpencv即可。想要在命令行中TestOpencv就能执行,就必须把这个可执行文件写入PATH变量,表明他的位置所在,或者直接放入/bin , /usr/bin 或是 /sbin ,usr/sbin 目录下
vscode 基础配置
vscode只是一个代码编辑工具,若想进行编译和调试需要先进行配置,前面我们已经介绍了直接采用命令行的一些编译的步骤和配置。那么在vscode中呢,首先编译程序用gcc或g++,而调试程序要用到gdb。安装都非常容易:
sudo apt-get install gdb
基础的配置方法也就是配置launch.json和task.json这两个文件。详细步骤可参考这里
其中,这里引用这篇文章介绍一下launch.json和task.json里面的具体信息,原作者是window系统,这里Linux系统进行对应一下即可:
在当前文件是C++的情况下,tasks可以被用来做编译,而launch用来执行编译好的文件。
// tasks.json
{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "2.0.0",
"tasks": [
{
"label": "Build", // 任务的名字叫Build,注意是大小写区分的,等会在launch中调用这个名字
"type": "shell", // 任务执行的是shell命令,也可以是
"command": "g++", // 命令是g++
"args": [
"'-Wall'",
"'-std=c++17'", //使用c++17标准编译
"'${file}'", //当前文件名
"-o", //对象名,不进行编译优化
"'${fileBasenameNoExtension}.exe'", //当前文件名(去掉扩展名)
],
// 所以以上部分,就是在shell中执行(假设文件名为filename.cpp)
// g++ filename.cpp -o filename.exe
"group": {
"kind": "build",
"isDefault": true
// 任务分组,因为是tasks而不是task,意味着可以连着执行很多任务
// 在build组的任务们,可以通过在Command Palette(F1) 输入run build task来运行
// 当然,如果任务分组是test,你就可以用run test task来运行
},
"problemMatcher": [
"$gcc" // 使用gcc捕获错误
],
}
]
}
launch.json文件
里面主要是编译器的参数,可以使用ctrl+space来查看有哪些可用参数
也可以在configurations中存在鼠标光标的情况下,点击右下自动出现的Add Configurations按钮
为什么选gdb不选 windows?因为这个不会执行预任务,也就没法编译文件了
为什么选 launch不选attach,是因为attach用来给正在执行的文件用的,比如网页中的组件,而launch是执行新文件
// launch.json
// https://code.visualstudio.com/Docs/editor/debugging#_launch-configurations
// author: huihut
// Available variables which can be used inside of strings.
// ${workspaceRoot}: the root folder of the team
// ${file}: the current opened file
// ${fileBasename}: the current opened file's basename
// ${fileDirname}: the current opened file's dirname
// ${fileExtname}: the current opened file's extension
// ${cwd}: the current working directory of the spawned process
{
"version": "0.2.0",
"configurations": [
{
"name": "(gdb) Launch", //这个应该是F1中出现的名字
"preLaunchTask": "Build", //在launch之前运行的任务名,这个名字一定要跟tasks.json中的任务名字大小写一致
"type": "cppdbg",
"request": "launch",
"program": "${fileDirname}/${fileBasenameNoExtension}.exe", //需要运行的是当前打开文件的目录中,名字和当前文件相同,但扩展名为exe的程序
"args": [],
"stopAtEntry": false, // 选为true则会在打开控制台后停滞,暂时不执行程序
"cwd": "${workspaceFolder}", // 当前工作路径:当前文件所在的工作空间
"environment": [],
"externalConsole": true, // 是否使用外部控制台,选false的话,我的vscode会出现错误
"MIMode": "gdb",
"miDebuggerPath": "c:/MinGW/bin/gdb.exe",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
]
}]
}
使用cmake
之所以使用cmake这种方法,是因为网上写的简单的编译方式(写task.json的方式)在面对大工程时十分无力,Include的路径的设置也比较麻烦,而使用CMakeLists.txt普适性无疑是最好的。其具体用法,在之前的文章中介绍过
在vscode中主要是下载两个插件即可,在vscode的扩展栏,输入CMake下载两个插件,CMake和CMake Tool
其中,CMake插件提供CMakeLists.txt的高亮显示,等等,而CMake Tool比较厉害,是整合在VS Code上的CMake插件。
安装好CMake Tool,并且在打开的文件夹内写好CMakeLists.txt之后,在VS Code下面会有如下工具条: