我的linux笔记--SO模块
- 格式:doc
- 大小:56.50 KB
- 文档页数:6
linux下查看so或可执⾏程序的依赖库
在linux下查看so或可执⾏程序的依赖库
Linux下可执⾏程序包括可执⾏程序exe和so, 两者⽂件都是ELF打头的。
objdump -x libxxxxx.so | grep NEEDED
objdump -x 可执⾏程序名 | grep NEEDED
或
arm-hisiv300-linux-objdump -x 可执⾏程序 | grep NEEDED
arm-hisiv300-linux-readelf -a 可执⾏程序 | grep NEEDED
linux之如何查看哪些进程在使⽤某⼀个so
在我们服务端,我们怎么查看哪些进程在使⽤某⼀个so
解决办法
lsof **.so
很明显,我们的apache的httpd⼏个进程在使⽤这个so
总结
以上就是这篇⽂章的全部内容了,希望本⽂的内容对⼤家的学习或者⼯作具有⼀定的参考学习价值,谢谢⼤家对的⽀持。
如果你想了解更多相关内容请查看下⾯相关链接。
linux找不到动态链接库.so⽂件的解决⽅法
如果使⽤⾃⼰⼿动⽣成的动态链接库.so⽂件,但是这个.so⽂件,没有加⼊库⽂件搜索路劲中,程序运⾏时可能会出现找不到动态链接库的情形。
可以通过ldd命名来查看可执⾏⽂件依赖的动态链接库,如下(其中D为可执⾏程序):
其中的libjson_linux-gcc-4.6_libmt.so cannot found。
解决这个问题:
(1)在系统中查找这个⽂件(当然要保证系统中已经有这个.so⽂件,只是查找路径没有设置正确⽽已):
sudo find / -name libjson_linux-gcc-4.6_libmt.so
结果:/home/liu/Desktop/jsoncpp-src-0.5.0/libs/linux-gcc-4.6/libjson_linux-gcc-4.6_libmt.so
(2)将.so⽂件路径的⽬录添加到/etc/ld.so.conf
sudo vim /etc/ld.so.conf
⽂件末尾新添加⼀⾏,/home/liu/Desktop/jsoncpp-src-0.5.0/libs/linux-gcc-4.6
(3)使得修改⽣效
sudo /sbin/ldconfig
这样就不会有那个找不对.so⽂件的错误啦。
linux中.so用法
.so(Shared Object) 是Linux中的动态库文件,用于实现共享库的加载和运行,主要用于动态加载程序的运行环境和资源,减少程序的重复代码量。
主要用法有:
- 链接时加载:程序在链接时将需要的共享库文件.so通过程序链接器命令ld时指定加载,运行时可以直接使用。
- 惰性加载:程序运行时,当调用惰性加载库时,程序就会查找库文件,如有就加载库文件,没有就报错。
该方式可以动态加载不同的共享库,以满足需求。
- dlopen()函数:使用该函数可以指定路径加载指定库,可以将dlopen()和她的几个相关函数使用起来,实现更复杂的加载功能。
- 共享语义:.so文件可以实现函数共享,多个程序可以调用同一函数库,当程序修改函数库时,多个程序都可以同时使用,这可以减少程序的重复代码量。
linux操作系统课程学习笔记,我的Linux学习笔记·Linux操作系统基础今天的笔记主要是关于Linux操作系统根底的相关学问。
那就从我⾯前的电脑开端讲起。
计算机和操作系统计算机主要包括五个部分:运算器,控制器,存储器,输⼊设备和输出设备。
通常,运算器,控制器再加上其他⼀些部件如寄存器等构成了我们通常所说的CPU(central processing unit),存储器则主要是内存。
运算器,控制器和存储器可以实现数据的处理.但是数据从何⽽来,运算之后的结果去往哪⾥?这就需要输⼊设备和输出设备(I/O设备)。
我们通常⽤到的输⼊设备包括键盘⿏标等,输出设备为屏幕,打印机等。
值得⼀提的是,计算机中有个叫做硬盘的东西,它并不是存储器,⽽是⼀个I/O设备。
在将数据读取到内存时,它是⼀个输⼊设备;⽽将结果保存到磁盘时,它就变成了⼀个输出设备。
这么多设备整合在⼀起,就成了⼀台计算机。
它可以接收我们的指令(键盘⿏标),通过运算(CPU),把结果展⽰给我们(屏幕,硬盘等)。
但是这么多硬件是如何协调作⽤,共同完成⼀个任务⽽不会我⾏我素地乱来呢?我们需要⼀个东西,它可以控制硬件有序地⼯作,各⾃执⾏⾃⼰的任务,这个东西就是操作系统(Operating System)。
操作系统是⼀个特殊的软件,它的任务就是硬件管理—控制CPU的运算,控制内存的分配,控制计算机的⼏乎⼀切。
假如⼀台电脑没有操作系统,它可能只是⼀个艺术品,或者⼀堆废铁。
⼀个完整的操作系统包括内核和⼀些辅助软件。
内核的主要任务就是进⾏硬件管理,它是⼀个操作系统最基础最底层的东西。
内核若想很好地控制硬件并使其发挥相应的功能,需要和硬件相识相知相爱,他俩可以成为完美的⼀对,全都仰仗于驱动的帮忙。
驱动是硬件的灵魂,它向操作系统提供了访问和使⽤硬件的接⼝,以便在某项任务中最⾼效地调⽤硬件。
什么是LinuxLinux就是⼀个操作系统,它可以管理整个计算机硬件,并且可以接收我们的指令,来指挥硬件完成相应的任务,并把结果反馈给我们。
动态链接库*.so的编译与使用- -动态库*.so在linux下用c和c++编程时经常会碰到,最近在网站找了几篇文章介绍动态库的编译和链接,总算搞懂了这个之前一直不太了解得东东,这里做个笔记,也为其它正为动态库链接库而苦恼的兄弟们提供一点帮助。
1、动态库的编译下面通过一个例子来介绍如何生成一个动态库。
这里有一个头文件:so_test.h,三个.c文件:test_a.c、t est_b.c、test_c.c,我们将这几个文件编译成一个动态库:libtest.so。
so_test.h:#include <stdio.h>#include <stdlib.h>void test_a();void test_b();void test_c();test_a.c:#include "so_test.h"void test_a(){printf("this is in test_a...\n");}test_b.c:#include "so_test.h"void test_b(){printf("this is in test_b...\n");}test_c.c:#include "so_test.h"void test_c(){printf("this is in test_c...\n");}将这几个文件编译成一个动态库:libtest.so$ gcc test_a.c test_b.c test_c.c -fPIC -shared -o libtest.so2、动态库的链接在1、中,我们已经成功生成了一个自己的动态链接库libtest.so,下面我们通过一个程序来调用这个库里的函数。
程序的源文件为:test.c。
test.c:#include "so_test.h"int main(){test_a();test_b();test_c();return 0;}l 将test.c与动态库libtest.so链接生成执行文件test:$ gcc test.c -L. -ltest -o testl 测试是否动态连接,如果列出libtest.so,那么应该是连接正常了$ ldd test./test 执行test,可以看到它是如何调用动态库中的函数的。
查看LinuxSO符号定义nm -s libhello_test.so |grep test_run使⽤nm [option(s)] [file(s)]有⽤的options:-A 在每个符号信息的前⾯打印所在对象⽂件名称;-C 输出demangle过了的符号名称;-D 打印动态符号;-l 使⽤对象⽂件中的调试信息打印出所在源⽂件及⾏号;-n 按照地址/符号值来排序;-u 打印出那些未定义的符号;常见的符号类型:A 该符号的值在今后的链接中将不再改变;B 该符号放在BSS段中,通常是那些未初始化的全局变量;D 该符号放在普通的数据段中,通常是那些已经初始化的全局变量;T 该符号放在代码段中,通常是那些全局⾮静态函数;U 该符号未定义过,需要⾃其他对象⽂件中链接进来;W 未明确指定的弱链接符号;同链接的其他对象⽂件中有它的定义就⽤上,否则就⽤⼀个系统特别指定的默认值。
注意⼏点:-C 总是适⽤于c++编译出来的对象⽂件。
还记得c++中有重载么?为了区分重载函数,c++编译器会将函数返回值/参数等信息附加到函数名称中去形成⼀个mangle过的符号,那⽤这个选项列出符号的时候,做⼀个逆操作,输出那些原始的、我们可理解的符号名称。
使⽤ -l 时,必须保证你的对象⽂件中带有符号调式信息,这⼀般要求你在编译的时候指定⼀个 -g 选项,见 Linux:Gcc。
使⽤nm前,最好先⽤Linux:File查看对象⽂件所属处理器架构,然后再⽤相应交叉版本的nm⼯具。
举例更详细的内容见man page。
这⾥举例说明:nm -u hello.o显⽰hello.o 中的未定义符号,需要和其他对象⽂件进⾏链接.nm -A /usr/lib/* 2>/dev/null | grep "T memset"在 /usr/lib/ ⽬录下找出哪个库⽂件定义了memset函数.。
linux so路径在Linux操作系统中,.so文件是共享库文件,用于保存可共享代码和数据等,供多个程序调用。
在使用Linux系统时,经常需要查找和管理.so文件的路径,下面是一些常见的.so文件路径指南:1. 系统默认.so文件路径在Linux系统中,通常会将.so文件存放在/usr/lib或/lib目录下。
这些目录中的.so文件是系统默认的共享库文件,供系统内置的程序和其他程序调用使用。
在命令行中可以使用以下命令查看默认.so文件路径:ls /usr/libls /lib2. 应用程序指定.so文件路径有些应用程序需要调用自己特定的.so文件,此时需要指定.so 文件路径。
在命令行中可以使用以下命令指定.so文件路径:export LD_LIBRARY_PATH=/path/to/lib其中/path/to/lib为.so文件所在的路径。
该命令会将.so文件路径添加到环境变量LD_LIBRARY_PATH中,使应用程序能够找到所需的.so文件。
3. 系统环境变量指定.so文件路径有时候,系统管理员需要更改默认的.so文件路径或添加新的.so 文件路径,以确保应用程序能够访问所需的共享库文件。
此时,可以使用以下系统环境变量指定.so文件路径:LD_LIBRARY_PATH:该变量用于指定.so文件所在路径,会覆盖系统默认的.so文件路径。
例如,exportLD_LIBRARY_PATH=/path/to/lib。
LD_PRELOAD:该变量用于指定在应用程序启动时需要预加载的共享库文件。
例如,export LD_PRELOAD=/path/to/lib/preload.so。
预加载的.so文件将优先于其他.so文件被加载。
4. 其他.so文件路径除了系统默认的.so文件路径和应用程序指定的.so文件路径外,还可能存在其他.so文件路径。
这些.so文件路径可能是系统管理员自行设置的,也可能是安装其他软件包时自动生成的。
linux中so文件生效
在Linux中,so文件是共享对象(shared object)文件的扩展名。
要使so文件生效,可以按照以下步骤进行操作:
1. 将so文件移动到系统库目录(例如,/usr/lib或
/usr/local/lib)。
可以使用以下命令将so文件移动到系统库目录:
```
sudo mv <so文件路径> /usr/lib/
```
2. 更新共享库缓存。
执行以下命令来更新共享库缓存:
```
sudo ldconfig
```
这将重新加载系统库目录中的共享库。
如果所在的目录已经包含在`/etc/ld.so.conf`文件中,那么在使用`ldconfig`命令时将自动进行更新。
3. 如果so文件位于非系统库目录,可以使用以下命令告诉操作系统访问该目录:
```
export LD_LIBRARY_PATH=<so文件所在目录路
径>:$LD_LIBRARY_PATH
```
或者将上述命令添加到适当的配置文件中(例如`.bashrc`),以便每次启动时都会设置该环境变量。
完成以上步骤后,so文件应该生效并可以在程序中正确使用。
确保在使用so文件的程序中正确链接和引用它们。
Linux动态链接库.so文件的创建与使用2011年4月19日10:57Linux动态链接库.so文件的创建与使用大家都知道,在WINDOWS系统中有很多的动态链接库(以.DLL为后缀的文件,DLL即Dynamic Link Library)。
这种动态链接库,和静态函数库不同,它里面的函数并不是执行程序本身的一部分,而是根据执行程序需要按需装入,同时其执行代码可在多个 执行程序间共享,节省了空间,提高了效率,具备很高的灵活性,得到越来越多程序员和用户的青睐。
那么,在LINUX系统中有无这样的函数库呢?答案是肯定的,LINUX的动态链接库不仅有,而且为数不少。
在/lib目录下,就有许多以.so 作后缀的文件,这就是LINUX系统应用的动态链接库,只不过与WINDOWS叫法不同,它叫so,即Shared Object,共享对象。
(在LINUX下,静态函数库是以.a作后缀的),X-WINDOW作为LINUX下的标准图形窗口界面,它本身就采用了很多的动态链接库(在/usr/X11R6/lib目录下),以方便程序间的共享, 节省占用空间。
着名的APACHE网页服务器,也采用了动态链接库,以便扩充程序功能。
你只需将PHP动态链接库拷到其共享目录,修改一下配置, APACHE就可以支持PHP网页了。
如果你愿意,可以自己编写动态链接库,让APACHE支持你自己定义的网页格式。
这就是动态链接的好处。
1、LINUX下动态链接库的创建在LINUX系统下,创建动态链接库是件再简单不过的事情。
只要在编译函数库源程序时加上-shared选项即可,这样所生成的执行程序即为动态链接库。
从某种意义上来说,动态链接库也是一种执行程序。
按一般规则,程序名应带.so后缀。
下面举个例子说说。
我准备编写两个函数,一个用于查询当前日期getdate,一个用于查询当前时间gettime,并将这两个函数存于动态链接库my.so中。
为此,需要做以下几项工作。
Linux动态链接库(.so)的使⽤1. 背景库:就是已经编写好的,后续可以直接使⽤的代码。
c++静态库:会合⼊到最终⽣成的程序,使得结果⽂件⽐较⼤。
优点是不再有任何依赖。
c++动态库:动态库,⼀个⽂件可以多个代码同时使⽤内存中只有⼀份,节省内存,可以随主代码⼀起编译。
缺点是需要头⽂件。
⽹友说:库就是除了main函数之外的其他代码,都可以组成库。
2. 只介绍动态库(⼯作中主要⽤动态库)C++使⽤动态库⽐C语⾔使⽤动态库稍微⿇烦点。
因为C++⽀持函数重载(参数变量个数不同、参数类型不同、类型修饰符不同const/not const等),都会使得C++对函数名进⾏重写,不⽅便根据函数名查找对应函数。
C++中可以使⽤extern关键字修饰对应的函数,表⽰函数名按照C⾔语分隔编译,不进⾏改写。
(extern关键字另⼀个关键字修饰变量,表⽰变量在其他⽂件中已经定义。
通常见于修饰全局变量)3. 使⽤so⽂件需要的api头⽂件 #include <dlfcn.h>dlopen以指定的模式打开共享链接库。
使⽤可以参考: /linux/man-pages/man3/dlopen.3.html4. C++使⽤动态链接库实例4.1 test.h1class Test{2public:3virtual int get();4virtual void set(const int num);5 };4.2 test.cpp1 #include <iostream>2 #include "test.h"34int g_num = 0; ///全局变量56int Test::get() { return g_num; }7void Test::set(const int num){ g_num = num; }89 #ifdef __cplusplus10extern "C" {11#endif1213 Test* create(){ return new Test; }1415 #ifdef __cplusplus16 }17#endif4.3 main.cpp1 #include <iostream>2 #include <dlfcn.h>3 #include "test.h"4using namespace std;56//声明函数指针7 typedef Test* (*so_init)();89//定义插件类来封装,句柄⽤完后需要释放10struct Plugin{11void *handle;12 Test *t;1314 Plugin():handle(NULL), t(NULL) { }15 ~Plugin(){16if(t) { delete t; }17if (handle) { dlclose(handle); }18 }19 };2021int create_instance(const char *so_file, Plugin &p){22//根据特定的模式打开so⽂件, 获取so⽂件句柄23//RTLD_NOW:需要在dlopen返回前,解析出所有未定义符号24//RTLD_DEEPBIND:在搜索全局符号前先搜索库内的符号,避免同名符号的冲突25 p.handle = dlopen(so_file, RTLD_NOW | RTLD_DEEPBIND);26if (!p.handle) {27 cout << "Cannot open library: " << dlerror() << endl;28return -1;29 }3031//根据字符串"create"读取库中对应到函数, 并返回函数地址,可以理解为⼀种间接的“反射机制”32 so_init create_fun = (so_init) dlsym(p.handle, "create");33if (!create_fun) {34 cout << "Cannot load symbol" << endl;35 dlclose(p.handle);36return -1;37 }3839//调⽤⽅法, 获取类实例40 p.t = create_fun();4142return0;43 }4445int main(){46 Plugin p1;47 Plugin p2;4849if (0 != create_instance("./libtest_1.so", p1)50 || 0 != create_instance("./libtest_2.so", p2)){51 cout << "create_instance failed" << endl;52return0;53 }5455 p1.t->set(1); //对库1中到全局变量进⾏设置56 p2.t->set(2); //对库2中到全局变量进⾏设置5758//输出两个库中的全局变量59 cout << "t1 g_num is " << p1.t->get() << endl;60 cout << "t2 g_num is " << p2.t->get() << endl;61return0;62 }执⾏:g++ -fPIC -shared test.cpp -o libtest_1.sog++ -fPIC -shared test.cpp -o libtest_2.sog++ -g -Wl,--no-as-needed -ldl main.cpp -rdynamic。
Linux动态库(.so)搜索路径(⽬录)设置⽅法⽅法⼀:在配置⽂件/etc/ld.so.conf中指定动态库搜索路径。
vi /etc/ld.so.conf添加 lib⽬录ldconfig⽅法⼆:通过环境变量LD_LIBRARY_PATH指定动态库搜索路径。
export LD_LIBRARY_PATH=”LD_LIBRARY_PATH:/opt/”⽅法三:在编译⽬标代码时指定该程序的动态库搜索路径。
还可以在编译⽬标代码时指定程序的动态库搜索路径。
通过gcc 的参数”-Wl,-rpath,”指定其中⽅法三可以避免安装部署的⿇烦⽅法三⽰例假设main.cpp,hello.h,hello.cpp,其中main.cpp调⽤了hello类中的⽅法1 ⽣成hello.sog++ -shared hello.cpp -o libhello.so2 编译main.cpp,并链接,并指定运⾏时libhello.so的位置 g++ main.cpp -lhello -L./ -Wl,-rpath=./ -o main值得⼀提的是,如果采⽤带版本号的库,例如libhello.so.2链接命令可使⽤g++ main.cpp libhello.so.2 -L./ -Wl,-rpath=./ -o main2)加⼊第⼆个so库g++ main.cpp -L./second/ -Wl,-rpath=./second/ -lsecond -L./hello/ -Wl,-rpath=./hello/ -lhello -o mainps,遇到过⼀个奇怪的问题,就是假设libhello.so还⽤到了libother.so,由于在/etc/ld.so.conf⾥配置错误了libother.so的⽬录路径,导致⼀直产⽣undefined reference to错误,但是在⼯程⾥对libother⽬录路径配置是正确的,有可能于查找路径顺序有关。
linux 提取so库函数的参数类型
要提取Linux 上的共享库(.so 文件)中函数的参数类型,通常需要使用工具来进行符号查看(symbol lookup)或者反汇编(disassembly)。
以下是一些常用的工具和方法:
1. 使用nm 命令:nm 命令可以列出目标文件或共享库中的符号信息,包括函数名、变量名以及它们的类型。
您可以使用nm 命令来查看共享库中函数的参数类型。
例如:```bash
nm -D /path/to/your/library.so
```
2. 使用objdump 命令:objdump 命令可以对目标文件或共享库进行反汇编,您可以使用objdump 来查看函数的汇编代码,从中推断参数类型。
例如:
```bash
objdump -T /path/to/your/library.so
```
3. 使用readelf 命令:readelf 命令可以用来查看ELF 格式的目标文件和共享库的信息,包括符号表、段表等。
您可以使用readelf 来查看函数参数的类型信息。
例如:```bash
readelf -sW /path/to/your/library.so
```
请注意,提取函数参数类型需要一定的反汇编和符号查看技能,以及对ELF 文件格式的了解。
如果您只是需要获取某个库函数的参数类型,通常最简单的方法是查阅官方文档或者相关的API 参考资料。
在Linux下,我们可以使用GCC编译器来编译动态链接库(.so文件)。
以下是一个简单的示例,演示了如何创建一个动态链接库。
假设我们有一个简单的C程序,它定义了一个函数add,该函数接受两个整数并返回它们的和。
c// add.c#include<stdio.h>int add(int a, int b) {return a + b;}接下来,我们需要编写一个头文件add.h,以供其他程序使用。
c// add.h#ifndef ADD_H#define ADD_Hint add(int a, int b);#endif// ADD_H现在,我们编写一个动态链接库程序libadd.c。
该程序包含了上面定义的头文件和函数。
c// libadd.c#include"add.h"int add(int a, int b) {return a + b;}编译动态链接库的步骤如下:1.使用gcc编译器将C源文件编译为共享对象文件。
使用-shared选项指定输出为共享对象文件。
例如:2.shellgcc -shared -o libadd.so add.c libadd.c这将生成一个名为libadd.so的共享对象文件。
2. 将生成的共享对象文件复制到适当的位置。
例如,您可以将文件复制到系统的库目录中:shellsudo cp libadd.so /usr/local/lib/这将将共享对象文件复制到/usr/local/lib/目录下,这样其他程序就可以找到并链接它。
请注意,可能需要使用超级用户权限来执行此操作。
【linux】程序找不到动态库.so的解决办法查看.so动态库信息.so动态库加载顺序⽬录找不到.so解决⽅法Linux 动态库的默认搜索路径是 /lib 和 /usr/lib,除了默认的搜索路径外,还可以通过以下⼏种⽅法来指定。
⽅法⼀:添加环境变量添加环境变量三种⽅式1. 添加当前⽤户当前终端的环境变量(临时)export LD_LIBRARY_PATH=/home/czd/... #.so file path2. 添加当前⽤户的环境变量修改~/.bashrc⽂件,在其末尾,添加环境变量vim ~/.bashrcexport LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/czd/... #.so file path使其⽣效,source ~/.bashrc如不能⽣效,请重启3. 添加所有⽤户的环境变量修改profile⽂件,在其末尾添加环境变量vim /etc/profileexport LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/czd/... #.so file path使其⽣效source /etc/profile如不能⽣效,请重启⽅法⼆:复制so⽂件到lib路径linux系统的so库⼀般存储与“/usr/lib/”路径中,可将动态库复制到该路径中。
sudo cp liblibtest.so /usr/lib/即时⽣效⽅法三:(推荐)添加ldconfig寻找路径步骤1. 编辑链接配置⽂件vim /etc/ld.so.conf,确认内容是否为如下,不是则修改为如下:include /etc/ld.so.conf.d/*.conf步骤2. 进⼊/etc/ld.so.conf.d⽬录内,创建*.conf⽂件,⽂件名随意,扩展名必须为.confcd /etc/ld.so.conf/vim libmy.conf步骤4. 在⽂件内部,添加so的路径,保存并退出/home/czd/eclipse-workspacee/calllib/Debug步骤5. 执⾏命令时期⽣效sudo ldconfig程序在运⾏时寻找so库就会到添加的⽬录中寻找。
linux 提取so库函数的参数类型(原创实用版)目录1.介绍 Linux 系统中的 so 库2.探讨如何在 Linux 系统中提取 so 库函数的参数类型3.总结提取参数类型的方法及应用场景正文一、Linux 系统中的 so 库在 Linux 系统中,so 库(共享对象库)是一种可重用的动态链接库,包含了许多可执行文件和共享库所需的函数和数据。
so 库文件的后缀名为.so,它们可以在不同的程序之间共享,从而减少了内存占用和提高了程序的执行效率。
当一个程序需要调用某个函数时,操作系统会加载相应的 so 库,并将其中的函数映射到程序的地址空间。
二、如何在 Linux 系统中提取 so 库函数的参数类型要提取 so 库函数的参数类型,我们可以使用一些工具和命令来完成。
下面介绍两种常用的方法:1.使用 nm 工具m(name mangling)是一个用于解析符号和函数名称的工具,它可以帮助我们查看 so 库中的函数名称及其参数类型。
使用 nm 命令的基本格式如下:```m -D -f so 库文件名```例如,要查看名为 libexample.so 中的函数参数类型,可以使用以下命令:```m -D -f libexample.so```在输出结果中,我们可以看到函数名称以及括号内的参数类型。
需要注意的是,nm 命令输出的结果可能会较为繁琐,需要仔细阅读和分析。
2.使用 cdecl 工具cdecl是一个用于解析C/C++函数原型和参数类型的工具,它可以帮助我们以更直观的方式查看so库函数的参数类型。
使用cdecl命令的基本格式如下:```cdecl -I -M so 库文件名```例如,要查看名为 libexample.so 中的函数参数类型,可以使用以下命令:```cdecl -I -M libexample.so```在输出结果中,我们可以看到以“prototype:”开头的函数原型,其后的括号内就是函数的参数类型。
Linux下编译⽣成SO并进⾏调⽤执⾏Linux下编译⽣成SO并进⾏调⽤执⾏参考博客的博客:1 代码结构(1)include中是⽤于⽣成SO的头⽂件,当前只有⼀个test.h⽂件,内容如下:void print_func(void);(2)src中是⽤于⽣成SO的源⽂件,当前只有⼀个test.c⽂件,内容如下:#include <stdio.h>void print_func(void){int i = 0;for (; i < 10; i++)printf("i = %d\n", i);return;}(3)Makefile⽂件是⽤于⽣成SO的,内容如下:PROJECT=libprint_func.soCC?=gccSOURCES=$(wildcard src/*.c)OBJECTS=$(patsubst %.c,%.o,$(SOURCES)).PHONY:cleanCFLAG = -Iinclude -fPIC -sharedLD_FLAG = -fPIC -s -Wl,-z,relro,-z,now,-z,noexecstack -fstack-protector-all$(PROJECT): $(OBJECTS)mkdir -p lib$(CC) -shared -o lib/$@ $(patsubst %.o,obj/%.o,$(notdir $(OBJECTS))) $(LD_FLAG)@echo "finish $(PROJECT)".c.o:@mkdir -p obj$(CC) -c $< $(CFLAG) -o obj/$(patsubst %.c,%.o,$(notdir $<))clean:-rm -rf obj lib@echo "clean up"⽣成的SO的名字为libprint_func.so。
Linux的so⽂件到底是⼲嘛的?浅析Linux的动态链接库我们分析了Hello World是如何编译的,即使⼀个⾮常简单的程序,也需要依赖C标准库和系统库,链接其实就是把其他第三⽅库和⾃⼰源代码⽣成的⼆进制⽬标⽂件融合在⼀起的过程。
经过链接之后,那些第三⽅库中定义的函数就能被调⽤执⾏了。
早期的⼀些操作系统⼀般使⽤静态链接的⽅式,现在基本上都在使⽤动态链接的⽅式。
静态链接和动态链接虽然静态链接和动态链接都能⽣成可执⾏⽂件,但两者的代价差异很⼤。
下⾯这张图可以很形象地演⽰了动态链接和静态链接的区别:左侧的⼈就像是⼀个动态链接的可执⾏⽂件,右侧的海象是⼀个静态链接的可执⾏⽂件。
⽐起⼈,海象臃肿得多,那是因为静态链接在链接的时候,就把所依赖的第三⽅库函数都打包到了⼀起,导致最终的可执⾏⽂件⾮常⼤。
⽽动态链接在链接的时候并不将那些库⽂件直接拿过来,⽽是在运⾏时,发现⽤到某些库中的某些函数时,再从这些第三⽅库中读取⾃⼰所需的⽅法。
我们把编译后但是还未链接的⼆进制机器码⽂件称为⽬标⽂件(Object File),那些第三⽅库是其他⼈编译打包好的⽬标⽂件,这些库⾥⾯包含了⼀些函数,我们可以直接调⽤⽽不⽤⾃⼰动⼿写⼀遍。
在编译构建⾃⼰的可执⾏⽂件时,使⽤静态链接的⽅式,其实就是将所需的静态库与⽬标⽂件打包到⼀起。
最终的可执⾏⽂件除了有⾃⼰的程序外,还包含了这些第三⽅的静态库,可执⾏⽂件⽐较臃肿。
相⽐⽽⾔,动态链接不将所有的第三⽅库都打包到最终的可执⾏⽂件上,⽽是只记录⽤到了哪些动态链接库,在运⾏时才将那些第三⽅库装载(Load)进来。
装载是指将磁盘上的程序和数据加载到内存上。
例如下图中的Program 1,系统⾸先加载Program 1,发现它依赖libx.so后才去加载libx.so。
所以,静态链接就像GIF图中的海象,把所需的东西都带在了⾝上。
动态链接只把精简后的内容带在⾃⼰⾝上,需要什么,运⾏的时候再去拿。
Linux动态链接库.so⽂件的命名及⽤途总结我们在linux下开发项⽬,有时会对外提供动态库,像***.so.1.0.0这样⼦的⽂件,另外提供相应的头⽂件。
⽤户拿到动态库和头⽂件说明,就可以使⽤动态库⾥的function。
那随之⽽来的⼀个问题是,动态库的升级问题,我们的动态库更改了⼀个bug,升级了⼀个版本,那使⽤我们动态库的应⽤程序需要重新编译吗?运⾏时会产⽣异常吗?linux下是怎么规范这些内容的呐?⼤家⼀定听说过windows下的dll hell。
Linux中的.so⽂件是动态链接的产物共享库理解为提供各种功能函数的集合,对外提供标准的接⼝Linux中命名系统中共享库的规则主版本号:不同的版本号之间不兼容次版本号:增量升级向后兼容发⾏版本号:对应次版本的错误修正和性能提升,不影响兼容性下⾯说说linux下动态库的命名规范。
为⽅便管理依赖关系,创建或部署共享库时,必须遵循统⼀约定的规则才⾏,其中包括动态库的命名规则及其部署⽅式。
共享库命名约定1) 每个动态库有⼀个包含了真正的库代码的⽂件名,通常被称为库的 realname ,命名格式通常为libxxx.so.x.y.z,其中so后缀中的x为主版本号,y为副版本号,z为发⾏版本号。
例如,我的linux系统机器上zlib共享库的realname为libz.so.1.2.8,这个⽂件是含有可执⾏的⼆进制代码的。
2) 每个动态库都有⼀个以"lib"为前缀且以".so.x"为结尾的被称为 soname的特定名称,其中x为主版本号,soname命名格式通常为libxxx.so.x。
例如,我的linux系统机器上zlib共享库的soname为libz.so.1。
这个soname包含了动态库的主版本号,这个doname⼀般会包含在库代码的头⽂件中,这个可以使⽤ readelf -d 读取出来,使⽤这个动态库的程序的⼆进制ELF的头⽂件中包含有这个动态库的soname。
Linux动态链接库.so⽂件的创建与使⽤1. 介绍使⽤GNU的⼯具我们如何在Linux下创建⾃⼰的程序函数库?⼀个“程序函数库”简单的说就是⼀个⽂件包含了⼀些编译好的代码和数据,这些编译好的代码和数据可以在事后供其他的程序使⽤。
程序函数库可以使整个程序更加模块化,更容易重新编译,⽽且更⽅便升级。
程序函数库可分为3种类型:静态函数库(static libraries)、共享函数库(shared libraries)、动态加载函数库(dynamically loaded libraries):1、静态函数库,是在程序执⾏前就加⼊到⽬标程序中去了;2、共享函数库,则是在程序启动的时候加载到程序中,它可以被不同的程序共享;动态加载函数库则可以在程序运⾏的任何时候动态的加载。
3、动态函数库,并⾮另外⼀种库函数格式,区别是动态加载函数库是如何被程序员使⽤的。
2. 静态函数库静态函数库实际上就是简单的⼀个普通的⽬标⽂件的集合,⼀般来说习惯⽤“.a”作为⽂件的后缀。
可以⽤ar这个程序来产⽣静态函数库⽂件。
Ar是archiver的缩写。
静态函数库现在已经不在像以前⽤得那么多了,主要是共享函数库与之相⽐较有很多的优势的原因。
慢慢地,⼤家都喜欢使⽤共享函数库了。
不过,在⼀些场所静态函数库仍然在使⽤,⼀来是保持⼀些与以前某些程序的兼容,⼆来它描述起来也⽐较简单。
静态库函数允许程序员把程序link起来⽽不⽤重新编译代码,节省了重新编译代码的时间。
不过,在今天这么快速的计算机⾯前,⼀般的程序的重新编译也花费不了多少时间,所以这个优势已经不是像它以前那么明显了。
静态函数库对开发者来说还是很有⽤的,例如你想把⾃⼰提供的函数给别⼈使⽤,但是⼜想对函数的源代码进⾏保密,你就可以给别⼈提供⼀个静态函数库⽂件。
理论上说,使⽤ELF格式的静态库函数⽣成的代码可以⽐使⽤共享函数库(或者动态函数库)的程序运⾏速度上快⼀些,⼤概1-5%。
创建⼀个静态函数库⽂件,或者往⼀个已经存在地静态函数库⽂件添加新的⽬标代码,可以⽤下⾯的命令:ar rcs my_library.a file1.o file2.o这个例⼦中是把⽬标代码file1.o和file2.o加⼊到my_library.a这个函数库⽂件中,如果my_library.a不存在则创建⼀个新的⽂件。
自己动手用C扩展PHP(一)首先,我们应该选择如何去构建我们的扩展模块,有三种方式:1、External Modules:外部模块,也就是编译成共享库,用dl()函数动态加载。
好处:(1)不需要重新编译PHP(2)PHP体积小,因为不需要编译进PHP缺点:(1)每次*.php脚本执行都需要用dl()去加载,效率较低(2)每次都要调用dl()2、Built-in Modules:编译进PHP好处:(1)不需要动态加载,模块在php脚本里面可以直接使用。
(2)不需要将模块编译成.so共享库,因为直接编译进PHP。
缺点:(1)对模块的改变都需要重新编译PHP(2)因为编译进PHP,所以PHP二进制文件较大,而且多占点内存3、The Zend Engine:Zend核心里实现(略……有兴趣的话可以看Zend API)Note:在下面示例里,我们编译成外部模块,因为,外部模块不需要重新编译PHP,所以在测试阶段先编译成共享库,然后用dl()加载(不过记得在php.ini里将安全模式设为Off)一、首先下载PHP源代码,解压。
然后我们开始构建代码结构,可以利用PHP源代码包ext目录里提供的ext_skel程序去生成我们需要的结构。
[ext]#./ext_skel --extname=haosoft_php_module这样会在ext下生成一个haosoft_php_module目录。
目录里有config.m4、haosoft_php_module.h和haosoft_php_module.c等几个文件。
二、haosoft_php_module.h为模块的头文件(熟悉C语言的应该了解),用VI打开,删除其中的一行:PHP_FUNCTION(confirm_MyExt_compiled);改为:PHP_FUNCTION(haosoft_test);说明:PHP_FUNCTION()是一个Zend Macro,作用是声名一个C 函数,使他在PHP脚本里可用。
参数是函数的名字,我们这里的示例函数为haosoft_test。
三、haosoft_php_module.c为主文件,里面包含了实现,VI打开,我们删除掉PHP_FUNCTION(confirm_MyExt_compiled){char *arg = NULL;int arg_len, len;char string[256];if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &arg, &arg_len) == FAILURE){return;}len = sprintf(string, "Congratulations! You have successfully modifiedext/%.78s/config.m4. Module %.78s is now compiled into PHP.", "haosoft_php_module", arg);RETURN_STRINGL(string, len, 1);}增加:PHP_FUNCTION(haosoft_test){zend_printf("This is my php module !");}四、现在,我们开始编译我们的模块,因为要编译成so,我们选择手工编译。
进入ext目录:开始编译,编译成*.o对象文件,注意这个"-DCOMPILE_DL_HAOSOFT_PHP_MODULE",在haosoft_php_module.c里你可以找到下面的语句:#ifdef COMPILE_DL_HAOSOFT_PHP_MODULEZEND_GET_MODULE(haosoft_php_module)#endifHAOSOFT_PHP_MODULE是大写模块名,你可以换成其他名,但是必须保证这个预处理常量与上面的语句里一致,至于上面语句的ZEND_GET_MODULE(haosoft_php_module)我们将在下一篇文章讲解它的意思。
[ext]# cc -fpic -DCOMPILE_DL_HAOSOFT_PHP_MODULE=1 -I/usr/local/include -I.-I../main -I.. -I../TSRM -I../Zend -c -o haosoft_php_module/haosoft_php_module.o haosoft_php_module/haosoft_php_module.c执行完之后会在目录下生成一个haosoft_php_module.o文件,接下来,连接:[ext]# cc -shared -L/usr/local/lib -rdynamic -ohaosoft_php_module/haosoft_php_module.so haosoft_php_module/haosoft_php_mod ule.o 这样会在目录下生成了我们需要的haosoft_php_module.so文件。
然后我们要建立一个目录:[ext]# mkdir -p /usr/local/php/lib/php/extensions/no-debug-non-zts-20041030上面的20041030的号码是Zend内部模块API号码,如果你不知道的话,你先建立一个php文件test.php:<?phpdl("abc.so");?>在浏览器里运行该文件会出现错误信息,可以在错误信息里得到这个号码,请根据自己情况根据这个号码与你的PHP安装目录更改路径。
建立完目录后接下来,我们把haosoft_php_module.so移动到刚刚的目录里去:[ext]# mv haosoft_php_module/haosoft_php_module.so/usr/local/php/lib/php/extensions/no-debug-non-zts-20041030OK!我们可以测试刚建立的模块了,目前模块里只有一个haosoft_test()的无参数方法。
建立PHP文件test.php:<?php// 加载刚建立的模块dl("haosoft_php_module.so");// 调用函数haosoft_test();?>执行。
看到显示“This is my module !”了吗?五、好了,模块编译并测试无误后,我们可以将他编译进PHP了,回到PHP源代码包根目录,执行:[php-5.0.6]#./buildconf --force[php-5.0.6]#./configure --enable-haosoft_php_module ... 也许你还有其他选项[php-5.0.6]#make[php-5.0.6]#make install[php-5.0.6]#sh /etc/init.d/httpd restart ... 这里是重启 apache本示例在我PHP 5.0.6上编译通过。
下一章我们再详细分析haosoft_php_module.h和haosoft_php_module.c这两个文件里的内容,以及有参数、有返回值函数的定义及使用。
自己动手用C扩展PHP(二)在上一章里我们介绍了如何编写自己的函数,接下来,我们开始编写一个有参数有返回值的扩展函数。
因为Zend没有正式的函数调用语法检查支持,所以我们在编写扩展函数的时候必须得“小心”,也就是我们必须得严格地进行调用合法性检查。
首先,我们写一个函数:#include "string.h"PHP_FUNCTION(haosoft_strcat){char* pc_arg_one = NULL;char* pc_arg_tow = NULL;long arg_one_len;long arg_tow_len;if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &pc_arg_one,&arg_one_len, &pc_arg_tow, &arg_tow_len) == FAILURE){return;}RETURN_STRINGL(strcat(pc_arg_one, pc_arg_tow), (arg_one_len + arg_tow_len), 1);}我们来分析这个函数:1:if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &pc_arg_one,&arg_one_len, &pc_arg_tow, &arg_tow_len) == FAILURE){return;}这句就是进行参数检查与取得参数的语句,zend_parse_parameters()是Zend提供的API函数,第一个参数是参数的数量,可以通过Zend提供的ZEND_NUM_ARGS()来取得,TSRMLS_CC是必须的,第三个参数“ss”指明参数的类型(s - 字符串),这个函数有2个字符串参数,所以为“ss”,之后的参数就是你要取得的参数值了。
注意:如果参数类型为“s”的话,参数值后面还要传一个long来取得字符串的长度。
zend_parse_parameters函数成功的话会返回SUCCESS,失败则返回FAILURE,并且输出错误信息。
2:RETURN_STRINGL(strcat(pc_arg_one, pc_arg_tow), (arg_one_len + arg_tow_len), 1);这句的作用是返回一个值,RETURN_STRINGL()为返回字符串,详细信息可以参阅Zend API参考手册。
这句作用是连接两个字符串参数并返回。
编译好扩展模块后,现在我们可以在 php 里调用这个函数<?php$s_result = haosoft_strcat("a", "b");echo $s_result;?>输出:ab说明:函数参数类型指定字符表,和一些返回函数返回值的函数可以参阅Zend文档,这里就不列出了。
因为Zend API说明文档不完全,许多Macro及大多数Zend API 函数都没有详细说明。
呵呵,所以如有错误地方,欢迎指正。
今天查阅了些Zend的资料,用C++写了一下PHP的扩展,觉得真的非常麻烦,一个只有一个MyClass::GetString()方法的类,要实现在php里:<?php$obj = new MyClass();$string = $obj->GetString();?>写了好几百行代码,对于习惯面向对象形式编程的人简直是噩梦,希望Zend能在这方面改善一下了,等有时间我会再写写如何用C++编写PHP的扩展。