[实践OK]入门教学之Linux如何解决动态库的版本控制, Linux如何解决动态库的版本控制,C语言HelloWorld示例,readelf -d libhello.so.0.0.0 |grep SONAME,以及动态链接的进程映射之/proc/pid/maps。 分析/proc/[pid]/maps中的各个内存区域的大小。
核心中的核心:
gcc -shared -Wl,-soname,libhello.so.0 -o libhello.so.0.0.0 hello.o #在生成可执行文件时就得有:libhello.so.0文件,即使是软链接也成,得有。动态链接版本库的标准用法,不标准用libhello.so 也成:gcc -shared -Wl,-soname,libhello.so -o libhello.so.0.0.0 hello.o
背景:共享库,主版本升级,即接口发生变化。
Linux 系统提供一个命令 ldconifg 专门为生成共享库的soname 文件,以便程序在加载时后通过soname 找到共享库。
1)real name:
咱生成的实体文件是:real name ==>libhello.so.0.0.0
readelf -d libhello.so.0.0.0 |grep SONAME
0x000000000000000e (SONAME) Library soname: [libhello.so.0]
1.1.1234的是共享库的版本号,其主版本号+小版本号+build号。主板号,代表当前动态库的版本,如果动态库的接口有变化,那么这个版本号就要加1;后面的两个版本号(小版本号 和 build 号)是告诉你详细的信息,比如为一个hot-fix 而生成的一个版本,其小版本号加1,build号也应有变化。 这个文件名包含共享库的代码。
2)soname:
动态库的soname( Short for shared object name),其是应用程序加载dll 时候,其寻找共享库用的文件名。ldconfig会自动生成,格式为前面的realname:
lib + math+.so + ( major version number)
3)link name:
共享库的连接名(link name),是专门为build 阶段连接而用的名字。这个名字就是lib + math +.so ,比如libmath.so。其是不带任何版本信息的。
例子,在编译时需要link name ,专门为build 阶段连接而用的名字,否者报错/usr/bin/ld: cannot find -lhello:(共享库的连接名(link name),是专门为build 阶段连接而用的名字。例子叫: libhello.so)
gcc -o main main.o -lhello -L.
/usr/bin/ld: cannot find -lhello
解决办法:
ln -sf libhello.so.0 libhello.so
4)ldconfig自动找SONAME,生成SONAME名的共享库,如下:
最后,ldconfig时,是找那个SONAME,然后自动生成对应的共享库:
inux 系统提供一个命令 ldconifg 专门为生成共享库的soname 文件,以便程序在加载时后通过soname 找到共享库。
实践并证明如下(生成SONAME名:gcc -shared -Wl,-soname,libhello.so.0 -o libhello.so.0.0.0 hello.o ,名字叫:libhello.so.0 ,这块即使-L. 引用了,非软链接是libhello.so.0.0.0, 它会提示:/bin/ld: cannot find -lhello,这个:-soname指定名字后,-L会找它,无论是实体的libhello.so.0还是软链接都得有,否则会提示cannot find -lhello,当然,你可以直接写成:gcc -shared -Wl,-soname,libhello.so -o libhello.so.0.0.0 libhello.o ,gcc -o main main.o -lhello -L. ,它会找libhello.so文件是否存在,当然可以软链接:mv libhello.so libhello.so.0.0.0 ,ln -s libhello.so.0.0.0 libhello.so,再编译就能通过:gcc -o main main.o -lhello -L. ,至于运行还是得找:LD_LIBRARY_PATH和ldconfig进行帮忙。):
readelf -d /home/xiangdong/test/c/libhello.so.0.0.0|grep SONAME
0x000000000000000e (SONAME) Library soname: [libhello.so.0]
cp /home/xiangdong/test/c/libhello.so.0.0.0 /usr/lib64/.
ldconfig -p|grep hello
ldconfig
ldconfig -p|grep hello
libhello.so.0 (libc6,x86-64) => /usr/lib64/libhello.so.0 (这个自动由chkconfig生成的so就是前面生成的SONAME名,这一句:gcc -shared -Wl,-soname,libhello.so.0 -o libhello.so.0.0.0 hello.o)
升级问题:
1)共享库,小版本升级,即接口不变.
当升级小版本时,共享库的soname 是不变的,所以需要重新把soname 的那个连接文件指定新版本就可以。 调用ldconfig命令,系统会帮你做修改那个soname link文件,并把它指向新的版本呢。这时候你的应用程序就自动升级了。
2)共享库,主版本升级,即接口发生变化。
共享库,主版本升级,即接口发生变化。
当升级主版本时,共享库的soname 就会加1.比如libhello.so.0.0.0 变为 libhello.so.1.0.0. 这时候再运行ldconfig 文件,就会发现生成两个连接 文件。
ln -s libhello.so.0---->libhello.so.0.0.0
ln -s libhello.so.1----->libhello.so.1.0.0
尽管共享库升级,但是你的程序依旧用的是旧的共享库,并且两个之间不会相互影响。
问题是如果更新的共享库只是增加一些接口,并没有修改已有的接口,也就是向前兼容。但是这时候它的主版本号却增加1. 如果你的应用程序想调用新的共享库,该怎么办? 简单,只要手工把soname 文件修改,使其指向新的版本就可以。(这时候ldconfig 文件不会帮你做这样的事,因为这时候soname 和real name 的版本号主板本号不一致,只能手动修改)。
比如: ln -s libhello.so.0 ---> libhello.so.1.0.0
但是有时候,主版本号增加,接口发生变化,可能向前不兼容。这时候再这样子修改,就会报错,“xx”方法找不到之类的错误。
总结一下,Linux 系统是通过共享库的三个不同名字,来管理共享库的多个版本。 real name 就是共享库的实际文件名字,soname 就是共享库加载时的用的文件名。在生成共享库的时候,编译器将soname 绑定到共享库的文件头里,二者关联起来。 在应用程序引用共享库时,其通过link name 来完成,link时将按照系统指定的目录去搜索link名字找到共享库,并将共享库的soname写在应用程序的头文件里。当应用程序加载共享库时,就会通过soname在系统指定的目录(path or LD_LIBRARY)去寻找共享库。
当共享库升级时,分为两种。一种是主板本不变,升级小版本和build 号。在这种情况下,系统会通过更新soname( ldconfig 来维护),来使用新的版本号。这中情况下,旧版本就没有用,可以删掉。
另外一种是主版本升级,其意味着库的接口发生变化,当然,这时候不能覆盖已有的soname。系统通过增加一个soname(ldconfig -p 里面增加一项),使得新旧版本同时存在。原有的应用程序在加载时,还是根据自己头文件的旧soname 去寻找老的库文件。
5.如果编译的时候没有指定,共享库的soname,会怎么样?
这是一个trick 的地方。第一系统将会在生成库的时候,就没有soname放到库的头里面。从而应用程序连接时候,就把linkname 放到应用程序依赖库里面。或者换句话说就是,soname这时候不带版本号。 有时候有人直接利用这点来升级应用程序,比如,新版本的库,直接拷贝到系统目录下,就会覆盖掉已经存在的旧的库文件,直接升级。 这个给程序员很大程度的便利性,如果一步小心,就会调到类似windows的Dll hell 陷阱里面。建议不要这样做。
=================================================================================================
Makefile有一句:
DYLIB_MAKE_CMD=$(CC) -shared -Wl,-soname,$(DYLIB_MINOR_NAME) -o $(DYLIBNAME) $(LDFLAGS)
编译下看看输出:
[hiredis]# make clean
rm -rf libhiredis.so libhiredis.a hiredis-test examples/hiredis-example* *.o *.gcda *.gcno *.gcov
[hiredis]# make
cc -std=c99 -pedantic -c -O3 -fPIC -Wall -W -Wstrict-prototypes -Wwrite-strings -g -ggdb net.c
cc -std=c99 -pedantic -c -O3 -fPIC -Wall -W -Wstrict-prototypes -Wwrite-strings -g -ggdb hiredis.c
cc -std=c99 -pedantic -c -O3 -fPIC -Wall -W -Wstrict-prototypes -Wwrite-strings -g -ggdb sds.c
cc -std=c99 -pedantic -c -O3 -fPIC -Wall -W -Wstrict-prototypes -Wwrite-strings -g -ggdb async.c
cc -shared -Wl,-soname,libhiredis.so.0.11 -o libhiredis.so net.o hiredis.o sds.o async.o
静态编译:
ar rcs libhiredis.a net.o hiredis.o sds.o async.o
一)这个/home/test/rpmbuild/BUILD/ngx_http_monitor_module-2.2.0/hiredis 目录下面也并没有:
ls libhiredis.so.0.11
ls: cannot access libhiredis.so.0.11: No such file or directory
readelf -d libhiredis.so
静态就是纯静态的:
ldd libhiredis.a
ldd: warning: you do not have execution permission for `./libhiredis.a'
not a dynamic executable
Dynamic section at offset 0xa7f0 contains 21 entries:
Tag Type Name/Value
0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
0x000000000000000e (SONAME) Library soname: [libhiredis.so.0.11] <===就是这个就是-soname,libhiredis.so.0.11编译进去的。
二)Nginx启动后,并找不到这个:
ldd /usr/local/nginx/sbin/nginx
libhiredis.so.0.11 => not found
Starting nginx: /usr/local/nginx/sbin/nginx: error while loading shared libraries: libhiredis.so.0.11: cannot open shared object file: No such file or directory
=============================================================================================
怎么样实现把共享库的soname 提取出来,写在自己的共享库的头文件里面?
第二个是动态库的soname( Short for shared object name),依赖该shared library的应用程序中记录了它的soname,所以应用程序加载该shared library的时候,其寻找共享库用的soname文件名。其格式为
lib + math+.so + ( major version number)
它是一个指向名字为real name的shared library的软链接。
当升级主版本时,共享库的soname 就会加1.比如libhello.so.0.0.0 变为 libhello.so.1.0.0. 这时候再运行ldconfig 文件,就会发现生成两个连接 文件。
ln -s libhello.so.0---->libhello.so.0.0.0
ln -s libhello.so.1----->libhello.so.1.0.0
尽管共享库升级,但是你的程序依旧用的是旧的共享库,并且两个之间不会相互影响。
问题是如果更新的共享库只是增加一些接口,并没有修改已有的接口,也就是向前兼容。但是这时候它的主版本号却增加1. 如果你的应用程序想调用新的共享库,该怎么办? 简单,只要手工把soname 文件修改,使其指向新的版本就可以。(这时候ldconfig 文件不会帮你做这样的事,因为这时候soname 和real name 的版本号主板本号不一致,只能手动修改)。
比如: ln -s libhello.so.0 ---> libhello.so.1.0.0
但是有时候,主版本号增加,接口发生变化,可能向前不兼容。这时候再这样子修改,就会报错,“xx”方法找不到之类的错误。
这就是接下来要介绍的第三个共享库的名字,link name,顾名思义,就是在编译过程,link 阶段用的文件名。 其将soname 和real name 关联起来。
第三个名字,共享库的连接名(link name),是专门为build 阶段连接而用的名字。这个名字就是lib + math +.so ,比如libmath.so。其是不带任何版本信息的。在共享库编译过程中,连接(link) 阶段,编译器将生成一个共享库及real name,同时将共享库的soname,写在共享库文件里的文件头里面。可以用命令 readelf -d sharelibrary 去查看。
在应用程序引用共享库时,其会用到共享库的link name。在应用程序的link阶段,其通过link名字找到动态库,并且把共享库的soname 提取出来,写在自己的共享库的头文件里面。当应用程序加载时就会通过soname 去给定的路径下寻找该共享库。
参考:
http://blog.163.com/kefeng_1984/blog/static/166615272014714114141296/
http://blog.chinaunix.net/uid-23592843-id-223539.html
代码文件如下结构:
1.生成共享库,关联real name 和soname 。
[root@test libhelo]#gcc -shared -W,soname,-libhello.so.0 -o libhello.so.0.0.0 hello.o #这句不行因为写错了,在soname前面得有一个小横杠,在libhello.so.0前面没有小横杠,得用下一句能正常产生带SONAME的动态链接库,互联网害人,这篇文章也是这么写的@ https://www.xuebuyuan.com/501443.html ,正确来自@http://emb.hqyj.com/Column/Column277.htm ,gcc -shared -Wl,-soname,libmylib.so.1 -o libmylib.so.1 *.o ,参考后如下:
将会生成共享库libhello.so.0.0.0,可以用系统提供的工具查看共享库的头:
tree -L 1 /usr/local/src/dll/libhello
/usr/local/src/dll/libhello
├── libhello.c
├── libhello.h
├── libhello.o
├── libhello.so.0.0.0
└── main.c
查看共享库的头:
[root@test libhelo]# readelf -d libhello.so.0.0.0 |grep Shared
0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
2.应用程序,引用共享库。
ln -s libhello.so.0.0.0 libhello.so.0
/usr/local/src/dll/libhello/libhello.so.0 -> libhello.so.0.0.0
ls /usr/local/src/dll/libhello
libhello.c libhello.h libhello.o libhello.so.0 libhello.so.0.0.0 main.c
新加动态链接库的Path,文件内容如下:
cat /etc/ld.so.conf.d/libhello.conf
/usr/local/hello/lib
由于动态链接库的编译能过,但是运行不行,找不到SO文件也就是缺SO,过一下ldconfig,ldconfig
ldconfig: /usr/local/hello/lib/libhello.so.0 is not a symbolic link
因为libxerces-c-3.0.so正常情况下应该是一个符号链接,而不是实体文集件,修改其为符号链接即可
原因是全拷贝过/usr/local/hello/lib后,软链接变成实体文件了,重新拷贝后正常:
cp -rf libhello.so* /usr/local/hello/lib/.
总之不能是实体文件,解决方法就是软链接,形成如下软链接:
libhello.so.0.0.0
libhello.so.0 -> libhello.so.0.0.0
libhello.so -> libhello.so.0
这样就ok了,
ldconfig -p|grep hello
libhello.so.0 (libc6,x86-64) => /usr/local/hello/lib/libhello.so.0
libhello.so (libc6,x86-64) => /usr/local/hello/lib/libhello.so
./main
Hello, library world./n
ldd main
linux-vdso.so.1 => (0x00007ffc659a7000)
libhello.so.0 => /usr/local/hello/lib/libhello.so.0 (0x00007f740d43d000)
libc.so.6 => /lib64/libc.so.6 (0x00007f740d070000)
/lib64/ld-linux-x86-64.so.2 (0x00007f740d64a000)
汇总编译的步骤,如下:
cat make.sh
gcc -g -Wall -fPIC -c libhello.c -o libhello.o
gcc -shared -Wl,-soname,libhello.so.0 -o libhello.so.0.0.0 libhello.o
gcc -g -Wall -c main.c -o main.o -I.
gcc -o main main.o -lhello -L.
ln -sf libhello.so.0.0.0 libhello.so.0
ln -sf libhello.so.0 libhello.so
软链接是左边是存在待被指向的实体文件,右边是软链接的软链接文件(仅仅是一个链接文件)。
完成。想继续探讨引入了动态链接库在RSS,VSZ上的一个系统不一致研究并查看:
https://jackxiang.com/post/842/
查看编译出来的最终程序里面的soname:
readelf -d main | grep libhello
[root@test libhelo]# readelf -d main | grep libhello
0x0000000000000001 (NEEDED) Shared library: [libhello.so]
ldd libhello.so.0
linux-vdso.so.1 => (0x00007ffeb05dd000)
libc.so.6 => /lib64/libc.so.6 (0x00007fb1f5628000)
/lib64/ld-linux-x86-64.so.2 (0x0000003771000000)
运行程序:
[root@test libhelo]# ./hello
./hello: error while loading shared libraries: libmyhello.so: cannot open shared object file: No such file or directory
[root@test libhelo]# vi /etc/ld.so.conf
/tmp/libhelo
[root@test libhelo]# ldconfig
[root@test libhelo]# ./hello
Hello, library world./n
通常在软件编译时出现的usr/bin/ld: cannot find -lxxx的错误,主要的原因是库文件并没有导入的ld检索目录中。
gcc -o main main.o -lhello -L.
/usr/bin/ld: cannot find -lhello
collect2: ld returned 1 exit status
成功解决办法:
ln -sf libhello.so.0 libhello.so # 来自:https://www.cnblogs.com/timeisbiggestboss/articles/7044947.html
1。确认库文件是否存在,比如-l123, 在/usr/lib, /usr/local/lib,或者其他自定义的lib下有无lib123.so, 如果只是存在lib123.so.1,那么可以通过ln -sv lib123.so.1 lib123.so,建立一个连接重建lib123.so.
临时解决方案采用变量:
命令行:export LD_LIBRARY_PATH=./lib:$LD_LIBRARY_PATH
或者 export LD_LIBRARY_PATH=/home/other/test/lib:$LD_LIBRARY_PATH
配置之后最好运行一下:# ldconfig
临时设置变量 LD_LIBRARY_PATH ,下次开机,一切设置将不复存在;如何把这个值持续写到 LD_LIBRARY_PATH 里呢?
我们可以在 ~/.bashrc 或者 ~/.bash_profile 中加入 export 语句,前者在每次登陆和每次打开 shell 都读取一次,后者只在登陆时读取一次。
我的习惯是加到 ~/.bashrc 中,在该文件的未尾,可采用如下语句来使设置生效:
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib
修改完后,记得关掉当前终端并重新打开一个新的终端,从而使上面的配置生效。
来自:https://www.cnblogs.com/wainiwann/p/4210343.html
彻底解决方式:(cat /etc/ld.so.conf.d/libiconv.conf
/usr/local/libiconv/lib64 ldconfig -p|grep iconf)
1。确认库文件是否存在,比如-l123, 在/usr/lib, /usr/local/lib,或者其他自定义的lib下有无lib123.so, 如果只是存在lib123.so.1,
那么可以通过ln -sv lib123.so.1 lib123.so,建立一个连接重建lib123.so.
2。检查/etc/ld.so.conf中的库文件路径是否正确,如果库文件不是使用系统路径,/usr/lib, /usr/local/lib, 那么必须在文件中加入。
3。ldconfig 重建ld.so.cache文件,ld的库文件检索目录存放文件。尤其刚刚编译安装的软件,必须运行ldconfig,才能将新安装的
库文件导入ld.so.cache.
4。测试,gcc -l123 --verbose.
5.写入系统的动态链接库目录(依然不行,最后还得这行才生效: ln -sf libhello.so.0 libhello.so ):
cat /etc/ld.so.conf.d/hello.conf
/home/xiangdong/test/c
ldconfig
ldconfig -p|grep xiangdong
libhello.so.0 (libc6,x86-64) => /home/xiangdong/test/c/libhello.so.0
ldconfig -p|grep conf
libfontconfig.so.1 (libc6,x86-64) => /usr/lib64/libfontconfig.so.1
libfontconfig.so (libc6,x86-64) => /usr/lib64/libfontconfig.so
依然出现找不到动态链接,看来是编辑步骤有问题,反查一下:
gcc -o main main.o -lhello -L.
/usr/bin/ld: cannot find -lhello
collect2: ld returned 1 exit status
ln -sf libhello.so.0 libhello.so #就好了,来处:https://www.cnblogs.com/timeisbiggestboss/articles/7044947.html
readelf -d main | grep libhello
0x0000000000000001 (NEEDED) Shared library: [libhello.so.0]
./main
./main: error while loading shared libraries: libhello.so.0: cannot open shared object file: No such file or directory
运行该程序,需要指定共享库的路径。 有两种办法,第一种使用环境变量“LD_LIBRARY_PATH”. 两外一种办法就是将共享库拷贝到系统目录(path 环境变量指定的其中一个目录)。
暂停! 我们还没有解决一个问题是,程序只知道soname,怎么从soname 找到共享库,即real name 文件呢? 这需要我们定义一个link文件,连接到共享库本身。
ln -s libhello.so.0.0.0 libhello.so.0
当然这个路径需要放到LD_LIBRARY_PATH环境变量中。
核对前面的知识,也就是ldconfig会找soname进行链接,如下的libhello.so.0:
gcc -lmyhello --verbose
Using built-in specs.
Target: x86_64-redhat-linux
Configured with: ../configure --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --enable-shared --enable-threads=posix --enable-checking=release --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-libgcj-multifile --enable-languages=c,c++,objc,obj-c++,java,fortran,ada --enable-java-awt=gtk --disable-dssi --enable-plugin --with-java-home=/usr/lib/jvm/java-1.4.2-gcj-1.4.2.0/jre --with-cpu=generic --host=x86_64-redhat-linux
Thread model: posix
gcc version 4.1.2 20080704 (Red Hat 4.1.2-46)
/usr/libexec/gcc/x86_64-redhat-linux/4.1.2/collect2 --eh-frame-hdr -m elf_x86_64 --hash-style=gnu -dynamic-linker /lib64/ld-linux-x86-64.so.2 /usr/lib/gcc/x86_64-redhat-linux/4.1.2/../../../../lib64/crt1.o /usr/lib/gcc/x86_64-redhat-linux/4.1.2/../../../../lib64/crti.o /usr/lib/gcc/x86_64-redhat-linux/4.1.2/crtbegin.o -L/usr/lib/gcc/x86_64-redhat-linux/4.1.2 -L/usr/lib/gcc/x86_64-redhat-linux/4.1.2 -L/usr/lib/gcc/x86_64-redhat-linux/4.1.2/../../../../lib64 -L/lib/../lib64 -L/usr/lib/../lib64 -lmyhello -lgcc --as-needed -lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s --no-as-needed /usr/lib/gcc/x86_64-redhat-linux/4.1.2/crtend.o /usr/lib/gcc/x86_64-redhat-linux/4.1.2/../../../../lib64/crtn.o
/usr/bin/ld: cannot find -lmyhello
collect2: ld returned 1 exit status
命令行标记告诉GCC产生的代码不要包含对函数和变量具体内存位置的引用,这是因为现在还无法知道使用该消息代码的应用程序会将它连接到哪一段内存地址空间。这样编译出的hello.o可以被用于建立共享链接库。建立共享链接库只需要用GCC的”-shared”标记即可。
gcc -o hello main.c -L. -lmyhello
gcc -shared -fPIC -o libmyhello.so hello.o
gcc -o hello main.c -L. -lmyhello
反之,去掉这个参数会出现打不到这个libmyhello.so。
===========================最终实践成功代码备查===================================
动态链接的实现方法:
root@yum_rpmbuild7_yz_bj_yz_10_73_234_251 libhello]# tree -L 1
.
├── hello
├── hello.c
├── hello.o
├── libhello.c
├── libhello.h
├── libhello.o
├── libhello.so -> libhello.so.0
├── libhello.so.0 -> libhello.so.0.0.0
├── libhello.so.0.0.0
└── make.sh
cat hello.c
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include "libhello.h"
int main() {
int i,j;
pid_t pid;
pid_t pid_c;
int status;
int *p[300];
for(i=0;i<300;i++){
p[i]=(int *)malloc(262013*sizeof(int));
for(j=0;j<262013;j++){
*(p[i]+j)=1;
}
}
hello();
pid = fork();
if(pid == 0) //返回子进程
{
printf("child pid: %d\n", getpid());
for(i=0;i<200;i++){
for(j=0;j<262013;j++){
*(p[i]+j)=7;
}
}
for(i=0;i<2;i++){
for(j=0;j<20;j++){
printf("p=%d\n",*(p[i]+j));
}
}
for(i=200;i<202;i++){
for(j=0;j<20;j++){
printf("p=%d\n",*(p[i]+j));
}
}
sleep(30);
} else {
printf("pid: %d\n", pid);//父进程中返回子进程的pid
printf("father pid: %d\n", getpid());
do
{
pid_c=waitpid(pid,&status,WNOHANG);
if(pid_c==0)
{
printf("NO child process exit\n");
sleep(1);
}
}while(pid_c==0);
if(WIFEXITED(status))
{
printf("child process %d will exit normally\n",pid_c);
printf("return code is %d\n",WEXITSTATUS(status));
} else
{
printf("child process %d will exit normally\n",pid_c);
}
}
return 0;
}
cat libhello.c
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
void hello(void)
{
printf("libhello begin\n");
int i,j;
int *p[300];
for(i=0;i<300;i++){
p[i]=(int *)malloc(42816*sizeof(int));
for(j=0;j<42816;j++){
*(p[i]+j)=1;
}
}
printf("Hello, library world.\n");
printf("libhello end\n");
}
cat libhello.h
/* libhello.h - demonstrate library use. */
void hello(void);
cat make.sh
gcc -g -Wall -fPIC -c libhello.c -o libhello.o
gcc -shared -Wl,-soname,libhello.so.0 -o libhello.so.0.0.0 libhello.o
gcc -g -Wall -c hello.c -o hello.o -I.
ln -sf libhello.so.0 libhello.so #注意顺序,下面这行要用
gcc -o hello hello.o -lhello -L.
ln -sf libhello.so.0.0.0 libhello.so.0
cp -rf libhello.so* /usr/local/hello/lib/.
ldconfig
ldconfig -p|grep hello
cat /etc/ld.so.conf.d/hello.conf
/usr/local/hello/lib
cat makeclean.sh
rm -rf *.o
rm -rf *.so.*
rm -rf libhello.so
rm -rf hello
查看rss,vsz:
watch -n 1 ps -o"ppid,pid,rss,vsz,comm" -C fork
用Top查看共享内存RES就是RSS,SHR就是共享内存:
top
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
34920 root 20 0 365916 358072 376 S 0.0 9.2 0:00.48 hello
34922 root 20 0 365916 357812 116 S 0.0 9.2 0:00.30 hello
cat /proc/35753/maps
7fbfa3483000-7fbfa3484000 rw-p 00001000 fd:01 1855296 /usr/local/hello/lib/libhello.so.0.0.0
分析/proc/[pid]/maps中的各个内存区域的大小:
cat maps | sed -e "s/\([0-9a-f]\{8\}\)-\([0-9a-f]\{8\}\)/0x\1 0x\2/" | awk '{printf("\033[0;33m[%8d Page]\033[0m \033[0;35m[%8d KB]\033[0m %s\n", (strtonum($2) - strtonum($1))/4096, (strtonum($2) - strtonum($1))/1024, $0)}'
PHP的一个页情况:
来自:https://www.cnblogs.com/long123king/p/3770156.html
lsof -nPp 35781
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
hello 35781 root cwd DIR 253,1 4096 1855279 /usr/local/src/dll/libhello
hello 35781 root rtd DIR 253,1 4096 2 /
hello 35781 root txt REG 253,1 10408 1855288 /usr/local/src/dll/libhello/hello
hello 35781 root mem REG 253,1 2173512 1836244 /usr/lib64/libc-2.17.so
hello 35781 root mem REG 253,1 9240 1855296 /usr/local/hello/lib/libhello.so.0.0.0
hello 35781 root mem REG 253,1 164240 1843207 /usr/lib64/ld-2.17.so
RES:resident memory usage 常驻内存
1、进程当前使用的内存大小,但不包括swap out
2、包含其他进程的共享
3、如果申请100m的内存,实际使用10m,它只增长10m,与VIRT相反
4、关于库占用内存的情况,它只统计加载的库文件所占内存大小
SHR:shared memory 共享内存
1、除了自身进程的共享内存,也包括其他进程的共享内存
2、虽然进程只使用了几个共享库的函数,但它包含了整个共享库的大小
3、计算某个进程所占的物理内存大小公式:RES – SHR
4、swap out后,它将会降下来
使用cat /proc/进程ID/maps 查看其进程映射:PHP-FPM
cat /proc/59379/maps
f90571b0000-7f90571b5000 r-xp 00000000 08:02 652827 /lib64/libnss_dns-2.12.so
7f90573b5000-7f90573b6000 rw-p 00005000 08:02 652827 /lib64/libnss_dns-2.12.so
7f90575c3000-7f90575c4000 rw-p 0000d000 08:02 653188 /lib64/libnss_files-2.12.so
7f907382a000-7f907382f000 rw-p 00066000 08:03 671978 /usr/local/php/ext/redis.so
7f9073a44000-7f9073a45000 rw-p 00015000 08:02 652808 /lib64/libgcc_s-4.4.7-20120601.so.1
7f9073d34000-7f9073d36000 rw-p 000ef000 08:03 914203 /usr/lib64/libstdc++.so.6.0.13
7f907417f000-7f9074180000 rw-p 00030000 08:03 787148 /usr/local/libmemcached/lib/libmemcached.so.11.0.0
7f9074395000-7f9074398000 rw-p 00015000 08:03 671976 /usr/local/php/ext/memcached.so
7f90745f0000-7f90745f7000 rw-p 00058000 08:03 671973 /usr/local/php/ext/opcache.so
556217d91000-556217dcd000 :虚拟地址空间的起始地址和结束地址
r-xp :此段地址空间的属性,r表示可读,x表示可执行,p表示私有段(s表示共享段)
00000000 :针对有名映射,指本段映射地址在文件中的偏移;对匿名映射,为vm_area_struct->vm_start
fd:00 :所映射的文件所属设备的设备号,其中fd为主设备号,00为次设备号
559540 :文件的索引节点号
/usr/sbin/chronyd :所映射的文件名。有有名映射而言,是映射的文件名。对匿名映射来说,是此段内存在进程中的作用
原文链接:https://blog.csdn.net/ma2595162349/article/details/88540326
lsof -nPp 59379 #https://jackxiang.com/post/9701/
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
php-fpm 59379 www mem REG 8,3 1322021 787148 /usr/local/libmemcached/lib/libmemcached.so.11.0.0
php-fpm 59379 www mem REG 8,3 1563318 671978 /usr/local/php/ext/redis.so
pid的值为什么在父子进程中不同。“其实就相当于链表,进程形成了链表,父进程的pid(p 意味point)指向子进程的进程id, 因为子进程没有子进程,所以其fpid为0.
gcc -shared -Wl,-soname,libhello.so.0 -o libhello.so.0.0.0 hello.o #在生成可执行文件时就得有:libhello.so.0文件,即使是软链接也成,得有。动态链接版本库的标准用法,不标准用libhello.so 也成:gcc -shared -Wl,-soname,libhello.so -o libhello.so.0.0.0 hello.o
背景:共享库,主版本升级,即接口发生变化。
Linux 系统提供一个命令 ldconifg 专门为生成共享库的soname 文件,以便程序在加载时后通过soname 找到共享库。
1)real name:
咱生成的实体文件是:real name ==>libhello.so.0.0.0
readelf -d libhello.so.0.0.0 |grep SONAME
0x000000000000000e (SONAME) Library soname: [libhello.so.0]
1.1.1234的是共享库的版本号,其主版本号+小版本号+build号。主板号,代表当前动态库的版本,如果动态库的接口有变化,那么这个版本号就要加1;后面的两个版本号(小版本号 和 build 号)是告诉你详细的信息,比如为一个hot-fix 而生成的一个版本,其小版本号加1,build号也应有变化。 这个文件名包含共享库的代码。
2)soname:
动态库的soname( Short for shared object name),其是应用程序加载dll 时候,其寻找共享库用的文件名。ldconfig会自动生成,格式为前面的realname:
lib + math+.so + ( major version number)
3)link name:
共享库的连接名(link name),是专门为build 阶段连接而用的名字。这个名字就是lib + math +.so ,比如libmath.so。其是不带任何版本信息的。
例子,在编译时需要link name ,专门为build 阶段连接而用的名字,否者报错/usr/bin/ld: cannot find -lhello:(共享库的连接名(link name),是专门为build 阶段连接而用的名字。例子叫: libhello.so)
gcc -o main main.o -lhello -L.
/usr/bin/ld: cannot find -lhello
解决办法:
ln -sf libhello.so.0 libhello.so
4)ldconfig自动找SONAME,生成SONAME名的共享库,如下:
最后,ldconfig时,是找那个SONAME,然后自动生成对应的共享库:
inux 系统提供一个命令 ldconifg 专门为生成共享库的soname 文件,以便程序在加载时后通过soname 找到共享库。
实践并证明如下(生成SONAME名:gcc -shared -Wl,-soname,libhello.so.0 -o libhello.so.0.0.0 hello.o ,名字叫:libhello.so.0 ,这块即使-L. 引用了,非软链接是libhello.so.0.0.0, 它会提示:/bin/ld: cannot find -lhello,这个:-soname指定名字后,-L会找它,无论是实体的libhello.so.0还是软链接都得有,否则会提示cannot find -lhello,当然,你可以直接写成:gcc -shared -Wl,-soname,libhello.so -o libhello.so.0.0.0 libhello.o ,gcc -o main main.o -lhello -L. ,它会找libhello.so文件是否存在,当然可以软链接:mv libhello.so libhello.so.0.0.0 ,ln -s libhello.so.0.0.0 libhello.so,再编译就能通过:gcc -o main main.o -lhello -L. ,至于运行还是得找:LD_LIBRARY_PATH和ldconfig进行帮忙。):
readelf -d /home/xiangdong/test/c/libhello.so.0.0.0|grep SONAME
0x000000000000000e (SONAME) Library soname: [libhello.so.0]
cp /home/xiangdong/test/c/libhello.so.0.0.0 /usr/lib64/.
ldconfig -p|grep hello
ldconfig
ldconfig -p|grep hello
libhello.so.0 (libc6,x86-64) => /usr/lib64/libhello.so.0 (这个自动由chkconfig生成的so就是前面生成的SONAME名,这一句:gcc -shared -Wl,-soname,libhello.so.0 -o libhello.so.0.0.0 hello.o)
升级问题:
1)共享库,小版本升级,即接口不变.
当升级小版本时,共享库的soname 是不变的,所以需要重新把soname 的那个连接文件指定新版本就可以。 调用ldconfig命令,系统会帮你做修改那个soname link文件,并把它指向新的版本呢。这时候你的应用程序就自动升级了。
2)共享库,主版本升级,即接口发生变化。
共享库,主版本升级,即接口发生变化。
当升级主版本时,共享库的soname 就会加1.比如libhello.so.0.0.0 变为 libhello.so.1.0.0. 这时候再运行ldconfig 文件,就会发现生成两个连接 文件。
ln -s libhello.so.0---->libhello.so.0.0.0
ln -s libhello.so.1----->libhello.so.1.0.0
尽管共享库升级,但是你的程序依旧用的是旧的共享库,并且两个之间不会相互影响。
问题是如果更新的共享库只是增加一些接口,并没有修改已有的接口,也就是向前兼容。但是这时候它的主版本号却增加1. 如果你的应用程序想调用新的共享库,该怎么办? 简单,只要手工把soname 文件修改,使其指向新的版本就可以。(这时候ldconfig 文件不会帮你做这样的事,因为这时候soname 和real name 的版本号主板本号不一致,只能手动修改)。
比如: ln -s libhello.so.0 ---> libhello.so.1.0.0
但是有时候,主版本号增加,接口发生变化,可能向前不兼容。这时候再这样子修改,就会报错,“xx”方法找不到之类的错误。
总结一下,Linux 系统是通过共享库的三个不同名字,来管理共享库的多个版本。 real name 就是共享库的实际文件名字,soname 就是共享库加载时的用的文件名。在生成共享库的时候,编译器将soname 绑定到共享库的文件头里,二者关联起来。 在应用程序引用共享库时,其通过link name 来完成,link时将按照系统指定的目录去搜索link名字找到共享库,并将共享库的soname写在应用程序的头文件里。当应用程序加载共享库时,就会通过soname在系统指定的目录(path or LD_LIBRARY)去寻找共享库。
当共享库升级时,分为两种。一种是主板本不变,升级小版本和build 号。在这种情况下,系统会通过更新soname( ldconfig 来维护),来使用新的版本号。这中情况下,旧版本就没有用,可以删掉。
另外一种是主版本升级,其意味着库的接口发生变化,当然,这时候不能覆盖已有的soname。系统通过增加一个soname(ldconfig -p 里面增加一项),使得新旧版本同时存在。原有的应用程序在加载时,还是根据自己头文件的旧soname 去寻找老的库文件。
5.如果编译的时候没有指定,共享库的soname,会怎么样?
这是一个trick 的地方。第一系统将会在生成库的时候,就没有soname放到库的头里面。从而应用程序连接时候,就把linkname 放到应用程序依赖库里面。或者换句话说就是,soname这时候不带版本号。 有时候有人直接利用这点来升级应用程序,比如,新版本的库,直接拷贝到系统目录下,就会覆盖掉已经存在的旧的库文件,直接升级。 这个给程序员很大程度的便利性,如果一步小心,就会调到类似windows的Dll hell 陷阱里面。建议不要这样做。
=================================================================================================
Makefile有一句:
DYLIB_MAKE_CMD=$(CC) -shared -Wl,-soname,$(DYLIB_MINOR_NAME) -o $(DYLIBNAME) $(LDFLAGS)
编译下看看输出:
[hiredis]# make clean
rm -rf libhiredis.so libhiredis.a hiredis-test examples/hiredis-example* *.o *.gcda *.gcno *.gcov
[hiredis]# make
cc -std=c99 -pedantic -c -O3 -fPIC -Wall -W -Wstrict-prototypes -Wwrite-strings -g -ggdb net.c
cc -std=c99 -pedantic -c -O3 -fPIC -Wall -W -Wstrict-prototypes -Wwrite-strings -g -ggdb hiredis.c
cc -std=c99 -pedantic -c -O3 -fPIC -Wall -W -Wstrict-prototypes -Wwrite-strings -g -ggdb sds.c
cc -std=c99 -pedantic -c -O3 -fPIC -Wall -W -Wstrict-prototypes -Wwrite-strings -g -ggdb async.c
cc -shared -Wl,-soname,libhiredis.so.0.11 -o libhiredis.so net.o hiredis.o sds.o async.o
静态编译:
ar rcs libhiredis.a net.o hiredis.o sds.o async.o
一)这个/home/test/rpmbuild/BUILD/ngx_http_monitor_module-2.2.0/hiredis 目录下面也并没有:
ls libhiredis.so.0.11
ls: cannot access libhiredis.so.0.11: No such file or directory
readelf -d libhiredis.so
静态就是纯静态的:
ldd libhiredis.a
ldd: warning: you do not have execution permission for `./libhiredis.a'
not a dynamic executable
Dynamic section at offset 0xa7f0 contains 21 entries:
Tag Type Name/Value
0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
0x000000000000000e (SONAME) Library soname: [libhiredis.so.0.11] <===就是这个就是-soname,libhiredis.so.0.11编译进去的。
二)Nginx启动后,并找不到这个:
ldd /usr/local/nginx/sbin/nginx
libhiredis.so.0.11 => not found
Starting nginx: /usr/local/nginx/sbin/nginx: error while loading shared libraries: libhiredis.so.0.11: cannot open shared object file: No such file or directory
=============================================================================================
怎么样实现把共享库的soname 提取出来,写在自己的共享库的头文件里面?
第二个是动态库的soname( Short for shared object name),依赖该shared library的应用程序中记录了它的soname,所以应用程序加载该shared library的时候,其寻找共享库用的soname文件名。其格式为
lib + math+.so + ( major version number)
它是一个指向名字为real name的shared library的软链接。
当升级主版本时,共享库的soname 就会加1.比如libhello.so.0.0.0 变为 libhello.so.1.0.0. 这时候再运行ldconfig 文件,就会发现生成两个连接 文件。
ln -s libhello.so.0---->libhello.so.0.0.0
ln -s libhello.so.1----->libhello.so.1.0.0
尽管共享库升级,但是你的程序依旧用的是旧的共享库,并且两个之间不会相互影响。
问题是如果更新的共享库只是增加一些接口,并没有修改已有的接口,也就是向前兼容。但是这时候它的主版本号却增加1. 如果你的应用程序想调用新的共享库,该怎么办? 简单,只要手工把soname 文件修改,使其指向新的版本就可以。(这时候ldconfig 文件不会帮你做这样的事,因为这时候soname 和real name 的版本号主板本号不一致,只能手动修改)。
比如: ln -s libhello.so.0 ---> libhello.so.1.0.0
但是有时候,主版本号增加,接口发生变化,可能向前不兼容。这时候再这样子修改,就会报错,“xx”方法找不到之类的错误。
这就是接下来要介绍的第三个共享库的名字,link name,顾名思义,就是在编译过程,link 阶段用的文件名。 其将soname 和real name 关联起来。
第三个名字,共享库的连接名(link name),是专门为build 阶段连接而用的名字。这个名字就是lib + math +.so ,比如libmath.so。其是不带任何版本信息的。在共享库编译过程中,连接(link) 阶段,编译器将生成一个共享库及real name,同时将共享库的soname,写在共享库文件里的文件头里面。可以用命令 readelf -d sharelibrary 去查看。
在应用程序引用共享库时,其会用到共享库的link name。在应用程序的link阶段,其通过link名字找到动态库,并且把共享库的soname 提取出来,写在自己的共享库的头文件里面。当应用程序加载时就会通过soname 去给定的路径下寻找该共享库。
参考:
http://blog.163.com/kefeng_1984/blog/static/166615272014714114141296/
http://blog.chinaunix.net/uid-23592843-id-223539.html
代码文件如下结构:
1.生成共享库,关联real name 和soname 。
[root@test libhelo]#gcc -shared -W,soname,-libhello.so.0 -o libhello.so.0.0.0 hello.o #这句不行因为写错了,在soname前面得有一个小横杠,在libhello.so.0前面没有小横杠,得用下一句能正常产生带SONAME的动态链接库,互联网害人,这篇文章也是这么写的@ https://www.xuebuyuan.com/501443.html ,正确来自@http://emb.hqyj.com/Column/Column277.htm ,gcc -shared -Wl,-soname,libmylib.so.1 -o libmylib.so.1 *.o ,参考后如下:
将会生成共享库libhello.so.0.0.0,可以用系统提供的工具查看共享库的头:
tree -L 1 /usr/local/src/dll/libhello
/usr/local/src/dll/libhello
├── libhello.c
├── libhello.h
├── libhello.o
├── libhello.so.0.0.0
└── main.c
查看共享库的头:
[root@test libhelo]# readelf -d libhello.so.0.0.0 |grep Shared
0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
2.应用程序,引用共享库。
ln -s libhello.so.0.0.0 libhello.so.0
/usr/local/src/dll/libhello/libhello.so.0 -> libhello.so.0.0.0
ls /usr/local/src/dll/libhello
libhello.c libhello.h libhello.o libhello.so.0 libhello.so.0.0.0 main.c
新加动态链接库的Path,文件内容如下:
cat /etc/ld.so.conf.d/libhello.conf
/usr/local/hello/lib
由于动态链接库的编译能过,但是运行不行,找不到SO文件也就是缺SO,过一下ldconfig,ldconfig
ldconfig: /usr/local/hello/lib/libhello.so.0 is not a symbolic link
因为libxerces-c-3.0.so正常情况下应该是一个符号链接,而不是实体文集件,修改其为符号链接即可
原因是全拷贝过/usr/local/hello/lib后,软链接变成实体文件了,重新拷贝后正常:
cp -rf libhello.so* /usr/local/hello/lib/.
总之不能是实体文件,解决方法就是软链接,形成如下软链接:
libhello.so.0.0.0
libhello.so.0 -> libhello.so.0.0.0
libhello.so -> libhello.so.0
这样就ok了,
ldconfig -p|grep hello
libhello.so.0 (libc6,x86-64) => /usr/local/hello/lib/libhello.so.0
libhello.so (libc6,x86-64) => /usr/local/hello/lib/libhello.so
./main
Hello, library world./n
ldd main
linux-vdso.so.1 => (0x00007ffc659a7000)
libhello.so.0 => /usr/local/hello/lib/libhello.so.0 (0x00007f740d43d000)
libc.so.6 => /lib64/libc.so.6 (0x00007f740d070000)
/lib64/ld-linux-x86-64.so.2 (0x00007f740d64a000)
汇总编译的步骤,如下:
cat make.sh
gcc -g -Wall -fPIC -c libhello.c -o libhello.o
gcc -shared -Wl,-soname,libhello.so.0 -o libhello.so.0.0.0 libhello.o
gcc -g -Wall -c main.c -o main.o -I.
gcc -o main main.o -lhello -L.
ln -sf libhello.so.0.0.0 libhello.so.0
ln -sf libhello.so.0 libhello.so
软链接是左边是存在待被指向的实体文件,右边是软链接的软链接文件(仅仅是一个链接文件)。
完成。想继续探讨引入了动态链接库在RSS,VSZ上的一个系统不一致研究并查看:
https://jackxiang.com/post/842/
查看编译出来的最终程序里面的soname:
readelf -d main | grep libhello
[root@test libhelo]# readelf -d main | grep libhello
0x0000000000000001 (NEEDED) Shared library: [libhello.so]
ldd libhello.so.0
linux-vdso.so.1 => (0x00007ffeb05dd000)
libc.so.6 => /lib64/libc.so.6 (0x00007fb1f5628000)
/lib64/ld-linux-x86-64.so.2 (0x0000003771000000)
运行程序:
[root@test libhelo]# ./hello
./hello: error while loading shared libraries: libmyhello.so: cannot open shared object file: No such file or directory
[root@test libhelo]# vi /etc/ld.so.conf
/tmp/libhelo
[root@test libhelo]# ldconfig
[root@test libhelo]# ./hello
Hello, library world./n
通常在软件编译时出现的usr/bin/ld: cannot find -lxxx的错误,主要的原因是库文件并没有导入的ld检索目录中。
gcc -o main main.o -lhello -L.
/usr/bin/ld: cannot find -lhello
collect2: ld returned 1 exit status
成功解决办法:
ln -sf libhello.so.0 libhello.so # 来自:https://www.cnblogs.com/timeisbiggestboss/articles/7044947.html
1。确认库文件是否存在,比如-l123, 在/usr/lib, /usr/local/lib,或者其他自定义的lib下有无lib123.so, 如果只是存在lib123.so.1,那么可以通过ln -sv lib123.so.1 lib123.so,建立一个连接重建lib123.so.
临时解决方案采用变量:
命令行:export LD_LIBRARY_PATH=./lib:$LD_LIBRARY_PATH
或者 export LD_LIBRARY_PATH=/home/other/test/lib:$LD_LIBRARY_PATH
配置之后最好运行一下:# ldconfig
临时设置变量 LD_LIBRARY_PATH ,下次开机,一切设置将不复存在;如何把这个值持续写到 LD_LIBRARY_PATH 里呢?
我们可以在 ~/.bashrc 或者 ~/.bash_profile 中加入 export 语句,前者在每次登陆和每次打开 shell 都读取一次,后者只在登陆时读取一次。
我的习惯是加到 ~/.bashrc 中,在该文件的未尾,可采用如下语句来使设置生效:
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib
修改完后,记得关掉当前终端并重新打开一个新的终端,从而使上面的配置生效。
来自:https://www.cnblogs.com/wainiwann/p/4210343.html
彻底解决方式:(cat /etc/ld.so.conf.d/libiconv.conf
/usr/local/libiconv/lib64 ldconfig -p|grep iconf)
1。确认库文件是否存在,比如-l123, 在/usr/lib, /usr/local/lib,或者其他自定义的lib下有无lib123.so, 如果只是存在lib123.so.1,
那么可以通过ln -sv lib123.so.1 lib123.so,建立一个连接重建lib123.so.
2。检查/etc/ld.so.conf中的库文件路径是否正确,如果库文件不是使用系统路径,/usr/lib, /usr/local/lib, 那么必须在文件中加入。
3。ldconfig 重建ld.so.cache文件,ld的库文件检索目录存放文件。尤其刚刚编译安装的软件,必须运行ldconfig,才能将新安装的
库文件导入ld.so.cache.
4。测试,gcc -l123 --verbose.
5.写入系统的动态链接库目录(依然不行,最后还得这行才生效: ln -sf libhello.so.0 libhello.so ):
cat /etc/ld.so.conf.d/hello.conf
/home/xiangdong/test/c
ldconfig
ldconfig -p|grep xiangdong
libhello.so.0 (libc6,x86-64) => /home/xiangdong/test/c/libhello.so.0
ldconfig -p|grep conf
libfontconfig.so.1 (libc6,x86-64) => /usr/lib64/libfontconfig.so.1
libfontconfig.so (libc6,x86-64) => /usr/lib64/libfontconfig.so
依然出现找不到动态链接,看来是编辑步骤有问题,反查一下:
gcc -o main main.o -lhello -L.
/usr/bin/ld: cannot find -lhello
collect2: ld returned 1 exit status
ln -sf libhello.so.0 libhello.so #就好了,来处:https://www.cnblogs.com/timeisbiggestboss/articles/7044947.html
readelf -d main | grep libhello
0x0000000000000001 (NEEDED) Shared library: [libhello.so.0]
./main
./main: error while loading shared libraries: libhello.so.0: cannot open shared object file: No such file or directory
运行该程序,需要指定共享库的路径。 有两种办法,第一种使用环境变量“LD_LIBRARY_PATH”. 两外一种办法就是将共享库拷贝到系统目录(path 环境变量指定的其中一个目录)。
暂停! 我们还没有解决一个问题是,程序只知道soname,怎么从soname 找到共享库,即real name 文件呢? 这需要我们定义一个link文件,连接到共享库本身。
ln -s libhello.so.0.0.0 libhello.so.0
当然这个路径需要放到LD_LIBRARY_PATH环境变量中。
核对前面的知识,也就是ldconfig会找soname进行链接,如下的libhello.so.0:
gcc -lmyhello --verbose
Using built-in specs.
Target: x86_64-redhat-linux
Configured with: ../configure --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --enable-shared --enable-threads=posix --enable-checking=release --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-libgcj-multifile --enable-languages=c,c++,objc,obj-c++,java,fortran,ada --enable-java-awt=gtk --disable-dssi --enable-plugin --with-java-home=/usr/lib/jvm/java-1.4.2-gcj-1.4.2.0/jre --with-cpu=generic --host=x86_64-redhat-linux
Thread model: posix
gcc version 4.1.2 20080704 (Red Hat 4.1.2-46)
/usr/libexec/gcc/x86_64-redhat-linux/4.1.2/collect2 --eh-frame-hdr -m elf_x86_64 --hash-style=gnu -dynamic-linker /lib64/ld-linux-x86-64.so.2 /usr/lib/gcc/x86_64-redhat-linux/4.1.2/../../../../lib64/crt1.o /usr/lib/gcc/x86_64-redhat-linux/4.1.2/../../../../lib64/crti.o /usr/lib/gcc/x86_64-redhat-linux/4.1.2/crtbegin.o -L/usr/lib/gcc/x86_64-redhat-linux/4.1.2 -L/usr/lib/gcc/x86_64-redhat-linux/4.1.2 -L/usr/lib/gcc/x86_64-redhat-linux/4.1.2/../../../../lib64 -L/lib/../lib64 -L/usr/lib/../lib64 -lmyhello -lgcc --as-needed -lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s --no-as-needed /usr/lib/gcc/x86_64-redhat-linux/4.1.2/crtend.o /usr/lib/gcc/x86_64-redhat-linux/4.1.2/../../../../lib64/crtn.o
/usr/bin/ld: cannot find -lmyhello
collect2: ld returned 1 exit status
命令行标记告诉GCC产生的代码不要包含对函数和变量具体内存位置的引用,这是因为现在还无法知道使用该消息代码的应用程序会将它连接到哪一段内存地址空间。这样编译出的hello.o可以被用于建立共享链接库。建立共享链接库只需要用GCC的”-shared”标记即可。
gcc -o hello main.c -L. -lmyhello
gcc -shared -fPIC -o libmyhello.so hello.o
gcc -o hello main.c -L. -lmyhello
反之,去掉这个参数会出现打不到这个libmyhello.so。
===========================最终实践成功代码备查===================================
动态链接的实现方法:
root@yum_rpmbuild7_yz_bj_yz_10_73_234_251 libhello]# tree -L 1
.
├── hello
├── hello.c
├── hello.o
├── libhello.c
├── libhello.h
├── libhello.o
├── libhello.so -> libhello.so.0
├── libhello.so.0 -> libhello.so.0.0.0
├── libhello.so.0.0.0
└── make.sh
cat hello.c
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include "libhello.h"
int main() {
int i,j;
pid_t pid;
pid_t pid_c;
int status;
int *p[300];
for(i=0;i<300;i++){
p[i]=(int *)malloc(262013*sizeof(int));
for(j=0;j<262013;j++){
*(p[i]+j)=1;
}
}
hello();
pid = fork();
if(pid == 0) //返回子进程
{
printf("child pid: %d\n", getpid());
for(i=0;i<200;i++){
for(j=0;j<262013;j++){
*(p[i]+j)=7;
}
}
for(i=0;i<2;i++){
for(j=0;j<20;j++){
printf("p=%d\n",*(p[i]+j));
}
}
for(i=200;i<202;i++){
for(j=0;j<20;j++){
printf("p=%d\n",*(p[i]+j));
}
}
sleep(30);
} else {
printf("pid: %d\n", pid);//父进程中返回子进程的pid
printf("father pid: %d\n", getpid());
do
{
pid_c=waitpid(pid,&status,WNOHANG);
if(pid_c==0)
{
printf("NO child process exit\n");
sleep(1);
}
}while(pid_c==0);
if(WIFEXITED(status))
{
printf("child process %d will exit normally\n",pid_c);
printf("return code is %d\n",WEXITSTATUS(status));
} else
{
printf("child process %d will exit normally\n",pid_c);
}
}
return 0;
}
cat libhello.c
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
void hello(void)
{
printf("libhello begin\n");
int i,j;
int *p[300];
for(i=0;i<300;i++){
p[i]=(int *)malloc(42816*sizeof(int));
for(j=0;j<42816;j++){
*(p[i]+j)=1;
}
}
printf("Hello, library world.\n");
printf("libhello end\n");
}
cat libhello.h
/* libhello.h - demonstrate library use. */
void hello(void);
cat make.sh
gcc -g -Wall -fPIC -c libhello.c -o libhello.o
gcc -shared -Wl,-soname,libhello.so.0 -o libhello.so.0.0.0 libhello.o
gcc -g -Wall -c hello.c -o hello.o -I.
ln -sf libhello.so.0 libhello.so #注意顺序,下面这行要用
gcc -o hello hello.o -lhello -L.
ln -sf libhello.so.0.0.0 libhello.so.0
cp -rf libhello.so* /usr/local/hello/lib/.
ldconfig
ldconfig -p|grep hello
cat /etc/ld.so.conf.d/hello.conf
/usr/local/hello/lib
cat makeclean.sh
rm -rf *.o
rm -rf *.so.*
rm -rf libhello.so
rm -rf hello
查看rss,vsz:
watch -n 1 ps -o"ppid,pid,rss,vsz,comm" -C fork
用Top查看共享内存RES就是RSS,SHR就是共享内存:
top
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
34920 root 20 0 365916 358072 376 S 0.0 9.2 0:00.48 hello
34922 root 20 0 365916 357812 116 S 0.0 9.2 0:00.30 hello
cat /proc/35753/maps
7fbfa3483000-7fbfa3484000 rw-p 00001000 fd:01 1855296 /usr/local/hello/lib/libhello.so.0.0.0
分析/proc/[pid]/maps中的各个内存区域的大小:
cat maps | sed -e "s/\([0-9a-f]\{8\}\)-\([0-9a-f]\{8\}\)/0x\1 0x\2/" | awk '{printf("\033[0;33m[%8d Page]\033[0m \033[0;35m[%8d KB]\033[0m %s\n", (strtonum($2) - strtonum($1))/4096, (strtonum($2) - strtonum($1))/1024, $0)}'
PHP的一个页情况:
来自:https://www.cnblogs.com/long123king/p/3770156.html
lsof -nPp 35781
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
hello 35781 root cwd DIR 253,1 4096 1855279 /usr/local/src/dll/libhello
hello 35781 root rtd DIR 253,1 4096 2 /
hello 35781 root txt REG 253,1 10408 1855288 /usr/local/src/dll/libhello/hello
hello 35781 root mem REG 253,1 2173512 1836244 /usr/lib64/libc-2.17.so
hello 35781 root mem REG 253,1 9240 1855296 /usr/local/hello/lib/libhello.so.0.0.0
hello 35781 root mem REG 253,1 164240 1843207 /usr/lib64/ld-2.17.so
RES:resident memory usage 常驻内存
1、进程当前使用的内存大小,但不包括swap out
2、包含其他进程的共享
3、如果申请100m的内存,实际使用10m,它只增长10m,与VIRT相反
4、关于库占用内存的情况,它只统计加载的库文件所占内存大小
SHR:shared memory 共享内存
1、除了自身进程的共享内存,也包括其他进程的共享内存
2、虽然进程只使用了几个共享库的函数,但它包含了整个共享库的大小
3、计算某个进程所占的物理内存大小公式:RES – SHR
4、swap out后,它将会降下来
使用cat /proc/进程ID/maps 查看其进程映射:PHP-FPM
cat /proc/59379/maps
f90571b0000-7f90571b5000 r-xp 00000000 08:02 652827 /lib64/libnss_dns-2.12.so
7f90573b5000-7f90573b6000 rw-p 00005000 08:02 652827 /lib64/libnss_dns-2.12.so
7f90575c3000-7f90575c4000 rw-p 0000d000 08:02 653188 /lib64/libnss_files-2.12.so
7f907382a000-7f907382f000 rw-p 00066000 08:03 671978 /usr/local/php/ext/redis.so
7f9073a44000-7f9073a45000 rw-p 00015000 08:02 652808 /lib64/libgcc_s-4.4.7-20120601.so.1
7f9073d34000-7f9073d36000 rw-p 000ef000 08:03 914203 /usr/lib64/libstdc++.so.6.0.13
7f907417f000-7f9074180000 rw-p 00030000 08:03 787148 /usr/local/libmemcached/lib/libmemcached.so.11.0.0
7f9074395000-7f9074398000 rw-p 00015000 08:03 671976 /usr/local/php/ext/memcached.so
7f90745f0000-7f90745f7000 rw-p 00058000 08:03 671973 /usr/local/php/ext/opcache.so
556217d91000-556217dcd000 :虚拟地址空间的起始地址和结束地址
r-xp :此段地址空间的属性,r表示可读,x表示可执行,p表示私有段(s表示共享段)
00000000 :针对有名映射,指本段映射地址在文件中的偏移;对匿名映射,为vm_area_struct->vm_start
fd:00 :所映射的文件所属设备的设备号,其中fd为主设备号,00为次设备号
559540 :文件的索引节点号
/usr/sbin/chronyd :所映射的文件名。有有名映射而言,是映射的文件名。对匿名映射来说,是此段内存在进程中的作用
原文链接:https://blog.csdn.net/ma2595162349/article/details/88540326
lsof -nPp 59379 #https://jackxiang.com/post/9701/
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
php-fpm 59379 www mem REG 8,3 1322021 787148 /usr/local/libmemcached/lib/libmemcached.so.11.0.0
php-fpm 59379 www mem REG 8,3 1563318 671978 /usr/local/php/ext/redis.so
pid的值为什么在父子进程中不同。“其实就相当于链表,进程形成了链表,父进程的pid(p 意味point)指向子进程的进程id, 因为子进程没有子进程,所以其fpid为0.
作者:jackxiang@向东博客 专注WEB应用 构架之美 --- 构架之美,在于尽态极妍 | 应用之美,在于药到病除
地址:https://jackxiang.com/post/7717/
版权所有。转载时必须以链接形式注明作者和原始出处及本声明!
最后编辑: jackxiang 编辑于2022-7-13 15:29
评论列表