工作,学习,生活,这里将会有一些记录. 备用域名:http://meisw.wdlinux.cn 注册 | 登陆
浏览模式: 标准 | 列表2011年08月的文章

Android虚拟平台的编译和整合

概要

 

Android从2008年开始到本文写的2011年,短短三年的时间里成为手机行业首屈一指的操作系统,在平板电脑,GPS PND甚至工业控制等领域也迅速流行起来。

越来越多的CPU厂商提供完整的Android解决方案使得Android的编译方法千差万别。

本文从Google原生态的Android系统入手,试图跳过所有的CPU厂商从Android自带的ARM QEMU入手一步步的编译出定制的Android系统。

 

(本文原创:欢迎转载请注明出处和作者 Ricky.Wu rickleaf.wu@gmail.com)

资源

 

Android 2.1 r2
Android Goldfish kernel 2.6.29
ubuntu 10.04 LTS AMD64

 

下载源代码

 

安装工具包

 

安装必要的工具包

 

$sudo apt-get install git-core gnupg flex bison gperf build-essential zip curl zlib1g-dev libc6-dev  lib32ncurses5-dev ia32-libs x11proto-core-dev libx11-dev lib32readline5-dev lib32z-dev

 

安装64bit Linux编译32位系统需要的工具包

 

$ sudo apt-get install gcc-multilib g++-multilib libc6-i386 libc6-dev-i386

 

看需要安装下面工具包

 

$ sudo apt-get install make gcc g++ libc6-dev  patch texinfo
 Zlib1g-dev valgrind python

 

下载源代码

 

下载repo工具,更改为可执行权限,并确保安装路径包含在PATH中
$ curl http://android.git.kernel.org/repo > ~/bin/repo
$ chmod a+x ~/bin/repo
建立Android工作目录
$cd ~
$mkdir android_platform
下载Android 2.1 r2源代码
$repo init -u git://android.git.kernel.org/platform/manifest.git /
-b android-2.1_r2
$repo sync

 

定制化Vendor

 

Android系统的vendor

 

记下Android系统的代码仓库网页
http://android.git.kernel.org/
网页浏览platform/vendor目录

 

 

vendor目录的作用

 

vendor的英文单词有“提供商”的意思,我们结合刚才看到的Android源码仓库中的vendor子目录就可以看出,在Google的Android原始代码里vendor目录包含了CPU硬件提供商的一些信息。
Android系统作为开放的手持设备操作系统,在发布之初必须要提供配置接口给CPU提供商,这样Android的系统才能在不同的CPU上运行。
实际上vendor目录就是Android系统预留给我们加入定制化信息的目录。在这个目录中可以完成对系统默认应用程序的添加移除、及其背光,GPS和OpenGL硬件适应层的vendor库文件或者代码。

 

加入自己的vendor目录

 

参考sample和htc dream建立rickleaf作为一个新的cpu厂商,在rickleaf建立一个heaven的目录作为厂商的一个特别设备

 

Android.mk

 

在android的platform代码中,如果你的代码需要编译或者一些二进制文件和库需要复制到目标板,就必须有个Android.mk文件来管理。
Vendor里面的Android.mk文件主要负责编译CPU厂商的特定代码和复制文件到目标半
Heaven中的Android.mk文件内容如下:

BoardConfig.mk

 

这个文件负责对Android系统的一些配置,包括如下等
ARM CPU的版本
Audio的架构类型
GPS的适应层名字
是否vendor有自己的init.rc文件
Android的image类型(inand需要yaffs2的image,mmc需要ext3或者ext4(android2.3)的image
如果在platform中加入了自己的模块,也可以加入配置机制,并且在这个文件中决定是否加入到系统中来

Heaven下面BoardConfig.mk的内容

AndroidProducts.mk

 

这个文件为PRODUCT_MAKEFILES指定一个mk文件
在指定的mk文件中完成对Android系统中APP层面的一些配置,下面简单列举:
添加默认的Android应用程序
采用哪种dpi
采用哪种图资
为android添加vendor的信息
加入默认铃声

 

PRODUCT_PROPERTY_OVERRIDES
修改一些默认的property设置,关于什么是property请大家去浏览google的文档,可以粗略的理解为类似注册表的东西驻留在内存中,可以供应用程序读写

 

PRODUCT_PACKAGES
PRODUCT_COPY_FILES
Android通过以上两个变量来对android packages和一些系统文件进行添加和移除

 

PRODUCT_LOCALES
这个变有两个作用一个是加入系统支持的语言类型;另一个是决定默认用哪一种图资,比如ldpi,mdpi还是hdpi

 

PRODUCT_BRAND
厂商名字
PRODUCT_NAME
产品名字
PRODUCT_DEVICE
设备名字

 

Linux kernel集成化

 

获得内核源代码

 

克隆kernel到android_platform根目录
$ git clone git://android.git.kernel.org/kernel/common.git kernel
切换kernel分支到2.6.29
$ cd kernel
$ git checkout -b android-goldfish-2.6.29 origin/android-goldfish-2.6.29

 

增加kernel编译到系统

 

修改platform下面的build

------------------------------- core/Makefile --------------------------------

index 2f316ca..7a92961 100644

@@ -288,6 +288,9 @@ endif

 

 INSTALLED_BOOTIMAGE_TARGET := $(PRODUCT_OUT)/boot.img

 

+kernel: $(INSTALLED_BOOTIMAGE_TARGET)

+.PHONY:kernel

+

 ifeq ($(TARGET_BOOTIMAGE_USE_EXT2),true)

 tmp_dir_for_image := $(call intermediates-dir-for,EXECUTABLES,boot_img)/bootimg

 INTERNAL_BOOTIMAGE_ARGS += --tmpdir $(tmp_dir_for_image)

 

AndroidBoard.mk

 

 

kernel配置

 

make kernelconfig

 

 

make kernelgconfig

 

 

Android系统的编译命令集

 

Android编译命令

 

执行环境配置命令
$ . build/envsetup.sh (.后面有空格)
当前终端会多出一些命令:
- croot:   Changes directory to the top of the tree.
- m:       Makes from the top of the tree.
- mm:      Builds all of the modules in the current directory.
- mmm:     Builds all of the modules in the supplied directories.
- cgrep:   Greps on all local C/C++ files.
- jgrep:   Greps on all local Java files.
- resgrep: Greps on all local res/*.xml files.
- godir:   Go to the directory containing a file.

 

—help查看帮助

 

编译模块

 

使用mmm来编译指定目录的模块,如编译Launcher:
$ mmm packages/apps/Launcher/
编完之后生成两个文件:
out/target/product/heaven/data/app/LauncherTests.apk
out/target/product/heaven/system/app/Launcher.apk
可以使用
$ make snod
重新生成system.img,再运行模拟器

 

选择product编译

 

按照如下图的方式,可以选择heaven这个Product

 

用OpenJDK骗过Android的

既然我们在虚拟系统编译,就不用非要去安装官方推荐的java sdk 1.5了。

只要我们在out目录建立这个文件就可以了

 

编译整个系统

 

make -j2 (2表示我有的两个cpu)

 

 

 

 

模拟器加载Android系统

 

创建Heaven模拟环境

创建标准Android 2.1模拟器命名为heaven
更改~/.android/avd/heaven/config.ini文件
hw.lcd.density=240
sdcard.size=64M
skin.name=WVGA800-L
skin.path=platforms/android-2.1/skins/WVGA800-L
vm.heapSize=24
image.sysdir.1=platforms/android-2.1/heaven/
更改以后,我们只要我们把我们编译好的image放到SDK的
platforms/android-2.1/heaven/目录就可以用emualtor了

把out目录中编译好的zimage改名成qemu-kernel放到Heaven仿真目录中
重新冷启动emulator
emulator -avd heaven -wipe-data
这时候heaven的emualtor的所以image都是我们自己生成的了,当然也可以调试apk到这个emulator

 

加入kernel logo

 

手机的about信息

http://blog.csdn.net/rickleaf/article/details/6369065

android中修改默认语言

本文是主要对android定制多语言的问题进行深入研究后,总结了其定制的 机制和其具体实现方法。如果想深入了解其定制的机制,可阅读本文第一部分,如 果只想了解如何定制,请参考第二部分。 


第一部分 多语言定制的机制 


1、ICU4C简介 

ICU4C(ICU for C, http://site.icu-project.org/ ) 是ICU在C/C++平台下的版本, ICU(International Component for Unicode)是基于"IBM公共许可证"的,与开源组织合作研究的, 用于支持软件国际化的开源项目。ICU4C提供了C/C++平台强大的国际化开发能力,软件开发者几乎可以使用ICU4C解决任何国际化的问题,根据各地 的风俗和语言习惯,实现对数字、货币、时间、日期、和消息的格式化、解析,对字符串进行大小写转换、整理、搜索和排序等功能,必须一提的是,ICU4C提 供了强大的BIDI算法,对阿拉伯语等BIDI语言提供了完善的支持。 

ICU首先是由Taligent公司开发的,Taligent公司现在被合并为IBM?公司全球化认证中心的Unicode研究组,然后ICU由IBM和 开源组织合作继续开发,开源组织给与了ICU极大的帮助。 
开始ICU只有Java平台的版本,后来这个平台下的ICU类被吸纳入SUN公司开发的JDK1.1,并在JDK以后的版本中不断改进。C++和C平台下 的ICU是由JAVA平台下的ICU移植过来的,移植过的版本被称为ICU4C,来支持这C/C++两个平台下的国际化应用。 ICU4J和ICU4C区别不大,但由于ICU4C是开源的,并且紧密跟进Unicode标准,ICU4C支持的Unicode标准总是最新的;同时,因 为JAVA平台的ICU4J的发布需要和JDK绑定,ICU4C支持Unicode标准改变的速度要比ICU4J快的多。 

2、 ANDROID语言包 

Android 使用的语言包就是ICU4C,位置:external/icu4c。Android支持的语言有: Locale CANADA 
Locale constant for en_CA. 
Locale CANADA_FRENCH 
Locale constant for fr_CA. 
Locale CHINA 
Locale constant for zh_CN. 
Locale CHINESE 
Locale constant for zh. 
Locale ENGLISH 
Locale constant for en. 
Locale FRANCE 
Locale constant for fr_FR. 
Locale FRENCH 
Locale constant for fr. 
Locale GERMAN 
Locale constant for de. 
Locale GERMANY 
Locale constant for de_DE. 
Locale ITALIAN 
Locale constant for it. 
Locale ITALY 
Locale constant for it_IT. 
Locale JAPAN 
Locale constant for ja_JP. 
Locale JAPANESE 
Locale constant for ja. 
Locale KOREA 
Locale constant for ko_KR. 
Locale KOREAN 
Locale constant for ko. 
Locale PRC 
Locale constant for zh_CN. 
Locale SIMPLIFIED_CHINESE 
Locale constant for zh_CN. 
Locale TAIWAN 
Locale constant for zh_TW. 
Locale TRADITIONAL_CHINESE 
Locale constant for zh_TW. 
Locale UK 
Locale constant for en_GB. 
Locale US 
Locale constant for en_US. 

3、定制语言 

定制语言在PRODUCT_LOCALES字段里添加需要语言,如: PRODUCT_LOCALES := en_US zh_CN,则系统里只有英语和汉语两种语言。然后语言的选择处理是在external/icu4c/stubdata/Android.mk里进行的, 如下: 
config := $(word 1, / $(if $(findstring ar,$(PRODUCT_LOCALES)),large) / 
$(if $(findstring da,$(PRODUCT_LOCALES)),large) / 
$(if $(findstring el,$(PRODUCT_LOCALES)),large) / 
$(if $(findstring fi,$(PRODUCT_LOCALES)),large) / 
$(if $(findstring he,$(PRODUCT_LOCALES)),large) / 
$(if $(findstring hr,$(PRODUCT_LOCALES)),large) / 
$(if $(findstring hu,$(PRODUCT_LOCALES)),large) / 
$(if $(findstring id,$(PRODUCT_LOCALES)),large) / 
$(if $(findstring ko,$(PRODUCT_LOCALES)),large) / 
$(if $(findstring nb,$(PRODUCT_LOCALES)),large) / 
$(if $(findstring pt,$(PRODUCT_LOCALES)),large) / 
$(if $(findstring ro,$(PRODUCT_LOCALES)),large) / 
$(if $(findstring ru,$(PRODUCT_LOCALES)),large) / 
$(if $(findstring sk,$(PRODUCT_LOCALES)),large) / 
$(if $(findstring sr,$(PRODUCT_LOCALES)),large) / 
$(if $(findstring sv,$(PRODUCT_LOCALES)),large) / 
$(if $(findstring th,$(PRODUCT_LOCALES)),large) / 
$(if $(findstring tr,$(PRODUCT_LOCALES)),large) / 
$(if $(findstring uk,$(PRODUCT_LOCALES)),large) / 
$(if $(findstring zh,$(PRODUCT_LOCALES)),large) / 
$(if $(findstring ja,$(PRODUCT_LOCALES)),us-japan) / 
$(if $(findstring it,$(PRODUCT_LOCALES)),us-euro) / 
$(if $(findstring pl,$(PRODUCT_LOCALES)),us-euro) / 
$(if $(findstring cs,$(PRODUCT_LOCALES)),default) / 
$(if $(findstring de,$(PRODUCT_LOCALES)),default) / 
$(if $(findstring fr,$(PRODUCT_LOCALES)),default) / 
$(if $(findstring nl,$(PRODUCT_LOCALES)),default) / 
us) 

4、默认语言 
默认语言的选择实现是在build/core/Makefile里,从PRODUCT_LOCALES里选择第一个语言作为默认语言,如下: 
define default-locale $(subst _, , $(firstword $(1))) 
endef 
# Selects the first locale in the list given as the argument 
# and returns the language (or the region) 
define default-locale-language $(word 2, 2, $(call default-locale, $(1))) 
endef 
define default-locale-region $(word 3, 3, $(call default-locale, $(1))) 
Endef ... PRODUCT_DEFAULT_LANGUAGE="$(call default-locale-language,$(PRODUCT_LOCALES))" / 
PRODUCT_DEFAULT_REGION="$(call default-locale-region,$(PRODUCT_LOCALES))" 
然后通过build/tool/buildinfo.sh文件将如下段写到文件build.prop,如下: 
echo "ro.product.locale.language=$PRODUCT_DEFAULT_LANGUAGE" 
echo "ro.product.locale.region=$PRODUCT_DEFAULT_REGION" 。 

因此,要改变默认语言用下面两种方法中的一种就行了: 

4.1、在PRODUCT_LOCALES字段里,将要选择的语言放在第一位,

如: PRODUCT_LOCALES := en_US zh_CN 默认语言是英语; 


4.2、在persist.sys.language 和persist.sys.country 里指定语言,如下:

PRODUCT_PROPERTY_OVERRIDES := / 
persist.sys.language=zh / 
persist.sys.country=CN /

 

rickleaf我测试后发现需要再添加两个值,算是对原作者的补充吧

 

ro.product.locale.language=zh /

ro.product.locale.region=CN

(在编译的时候一定要删除system下面的build.prop,在编译才有效,否则这个文件不会被更新)

 

build.prop文件的处理是在system/core/init/property_service.c。 


第二部分 多语言定制的方法 


1、多语言定制的实现步骤 

1)进入build/target/product目录,在languages_full.mk或languages_small.mk文件中, 修改PRODUCT_LOCALES的值,来定制语言,比如PRODUCT_LOCALES := en_US zh_CN zh_TW en_GB fr_FR it_IT de_DE es_ES; 

2)相同目录下,修改full.mk文件的 
$(call inherit-product, build/target/product/languages_small|full.mk)语句来切换所使用的文件; 

3)重新编译即可。 

2、设置默认语言的实现步骤 
1)进入build/target/product目录,修改文件core.mk的PRODUCT_PROPERTY_OVERRIDES 值,例如,欲修改为默认中文,则增加 
“/ persist.sys.language=zh / persist.sys.country=CN”,增加后的语句如PRODUCT_PROPERTY_OVERRIDES := / 

ro.config.notification_sound=OnTheHunt.ogg / 

ro.config.alarm_alert=Alarm_Classic.ogg / persist.sys.language=zh / persist.sys.country=CN 
2)重新编译即可。 

3、与多语言定制相关的字段及其所在的文件 

PREVIOUS_BUILD_CONFIG out/target/product/dream/previous_build_config.mk 

NO_FALLBACK_FONT的定义 device/htc/dream-sapphire/BoardConfigCommon.mk 

NO_FALLBACK_FONT的调用 frameworks/base/data/fonts/Android.mk 

extra_locales CUSTOM_LOCALES nodpi mdpi hdpi build/core/product_config.mk 

PRODUCT_PROPERTY_OVERRIDES build/target/product 
build.prop out/target/product/generic/system

Ubuntu DNS配置与使用命令设置Ubuntu的ip地址

经过长时间学习Ubuntu DNS,你可能会遇到Ubuntu DNS中网络问题,这里将介绍Ubuntu DNS解决网络问题的方法,新装了Ubuntu,虽然使用图形界面明明已经设置好了ip,网关等等,但是不能上网。

一个朋友提醒说,也许是设置的问题,试试命令行的设置。于是搜索了一下怎么设置,结果还真的成功了。具体如下:
1. 检验是否可以连通,就使用ping命令ping 网关开始的时候总是现实unreachable
2. 设置IP sudo ifconfig eth0 133.133.133.190 netmask 255.255.255.0这样就算设置好了网卡eth0的IP地址和子网掩码
3. 设置网关 sudo route add default gw 133.133.133.40
4. ping 网关就可以ping通了
5. 设置Ubuntu DNS 修改/etc/resolv.conf,在其中加入
nameserver DNS的地址1
nameserver DNS的地址2完成。这样设置之后,下次开机时候似乎IP又会发生变化。

一、使用命令设置Ubuntu的ip地址

1. 设置IP sudo ifconfig eth0 203.171.239.155 netmask 255.255.255.224 这样就算设置好了网卡eth0的IP地址和子网掩码
2. 设置网关 sudo route add default gw 203.171.239.129
3. 设置DNS 修改/etc/resolv.conf,在其中加入 nameserver DNS的地址1 nameserver DNS的地址2 完成。不过,这样设置之后,下次开机时候似乎IP又不存在了。

二、直接修改系统配置文件

Ubuntu的网络配置文件是:/etc/network/interfaces
Ubuntu命令行修改网络配置方法/etc/network/interfaces打开后里面可设置DHCP或手动设置静态ip。前面auto eth0,让网卡开机自动挂载.

1. 以DHCP方式配置网卡
编辑文件/etc/network/interfaces:sudo vi /etc/network/interfaces.并用下面的行来替换有关eth0的行:

  1. # The primary network interface - use DHCP to find our address  
  2. auto eth0  
  3. iface eth0 inet dhcp 

用下面的命令使网络设置生效:sudo /etc/init.d/networking restart也可以在命令行下直接输入下面的命令来获取地址sudo dhclient eth0

2. 为网卡配置静态IP地址
编辑文件/etc/network/interfaces:sudo vi /etc/network/interfaces.并用下面的行来替换有关eth0的行:# The primary network interface

  1. auto eth0  
  2. iface eth0 inet static  
  3. address 192.168.3.90  
  4. gateway 192.168.3.1  
  5. netmask 255.255.255.0  
  6. #network 192.168.3.0  
  7. #broadcast 192.168.3.255 

将上面的ip地址等信息换成你自己就可以了.用下面的命令使网络设置生效:sudo /etc/init.d/networking restart

3. 设定第二个IP地址(虚拟IP地址)
编辑文件/etc/network/interfaces:sudo vi /etc/network/interfaces.在该文件中添加如下的行:

  1. auto eth0:1  
  2. iface eth0:1 inet static  
  3. address 192.168.1.60  
  4. netmask 255.255.255.0  
  5. network x.x.x.x  
  6. broadcast x.x.x.x  
  7. gateway x.x.x.x 

根据你的情况填上所有诸如address,netmask,network,broadcast和gateways等信息.用下面的命令使网络设置生效:sudo /etc/init.d/networking restart

4. 设置主机名称(hostname)
使用下面的命令来查看当前主机的主机名称:sudo /bin/hostname
使用下面的命令来设置当前主机的主机名称:sudo /bin/hostname newname
系统启动时,它会从/etc/hostname来读取主机的名称.
关于设置主机名称的更多信息,请访问这里

5. 配置Ubuntu DNS
首先,你可以在/etc/hosts中加入一些主机名称和这些主机名称对应的IP地址,这是简单使用本机的静态查询.要访问Ubuntu DNS 服务器来进行查询,需要设置/etc/resolv.conf文件.假设Ubuntu DNS服务器的IP地址是192.168.3.2, 那么/etc/resolv.conf文件的内容应为:
search test.com
nameserver 192.168.3.2
/etc/network/interfaces
打开后里面可设置DHCP或手动设置静态ip。前面auto eth0,让网卡开机自动挂载.

1. 以DHCP方式配置网卡

编辑文件/etc/network/interfaces:sudo vi /etc/network/interfaces并用下面的行来替换有关eth0的行:

  1. # The primary network interface - use DHCP to find our address  
  2. auto eth0  
  3. iface eth0 inet dhcp 

用下面的命令使网络设置生效:sudo /etc/init.d/networking restart也可以在命令行下直接输入下面的命令来获取地址sudo dhclient eth0

2. 为网卡配置静态IP地址
编辑文件/etc/network/interfaces:sudo vi /etc/network/interfaces
并用下面的行来替换有关eth0的行:# The primary network interface

  1. auto eth0  
  2. iface eth0 inet static  
  3. address 192.168.3.90  
  4. gateway 192.168.3.1  
  5. netmask 255.255.255.0  
  6. #network 192.168.3.0  
  7. #broadcast 192.168.3.255 

将上面的ip地址等信息换成你自己就可以了.用下面的命令使网络设置生效:sudo /etc/init.d/networking restart

3. 设定第二个IP地址(虚拟IP地址)
编辑文件/etc/network/interfaces:sudo vi /etc/network/interfaces在该文件中添加如下的行:

  1. auto eth0:1  
  2. iface eth0:1 inet static  
  3. address 192.168.1.60  
  4. netmask 255.255.255.0  
  5. network x.x.x.x  
  6. broadcast x.x.x.x  
  7. gateway x.x.x.x 

根据你的情况填上所有诸如address,netmask,network,broadcast和gateways等信息.用下面的命令使网络设置生效:sudo /etc/init.d/networking restart

4. 设置主机名称(hostname)
使用下面的命令来查看当前主机的主机名称:sudo /bin/hostname
使用下面的命令来设置当前主机的主机名称:sudo /bin/hostname newname
系统启动时,它会从/etc/hostname来读取主机的名称.关于设置主机名称的更多信息,请访问这里

5. 配置Ubuntu DNS
首先,你可以在/etc/hosts中加入一些主机名称和这些主机名称对应的IP地址,这是简单使用本机的静态查询.要访问Ubuntu DNS 服务器来进行查询,需要设置/etc/resolv.conf文件.假设Ubuntu DNS服务器的IP地址是192.168.3.2, 那么/etc/resolv.conf文件的内容应为:

  1. search test.com  
  2. nameserver 192.168.3.2 

解决Ubuntu重启Ubuntu DNS重新设置的问题作者: wynlchae 发布日期: 2008-4-09 查看数: 256 出自: http://www.linuxdiyf.com在Ubuntu下设置静态IP,“主菜单”,“系统”,“系统管理”,“网络”,在“Ubuntu DNS”里删掉原来的Ubuntu DNS,填上静态的Ubuntu DNS的服务器,不过这样重启后就恢复,下次开机开得重新设置一次。

先备份:sudo cp /etc/resolv.conf /etc/resolv.conf.bak然后编辑sudo gvim /etc/dhcp3/dhclient.conf将以下行添加到末尾,就是Ubuntu DNS的2个服务器啦prepend domain-name-servers 208.67.222.222,208.67.220.220;#这两个服务器根据自己的Ubuntu DNS填上。

不同编译方式下php扩展的调用区别

php的一些组件,既可以静态编译进php本身,也可以用so的方式来动态加载。静态编译的模式不说,如果是共享库so的方式,肯定是用dlopen的方式,那就需要这个so有一个方法名已知的函数暴露出来,使用dlsym来调用。仔细看扩展预先生成的源码,以hanks这个扩展为例:

 

#ifdef COMPILE_DL_HANKS

ZEND_GET_MODULE(hanks)

#endif

 

COMPILE_DL_HANKS 决定了要不要以dll(.so)的方式来编译hanks这个扩展,如果dll方式,则ZEND_GET_MODULE这个宏就会发生作用。


ZEND_GET_MODULE定义如下:

 

#define ZEND_GET_MODULE(name) \

    BEGIN_EXTERN_C()\

ZEND_DLEXPORT zend_module_entry *get_module(void) { return &name##_module_entry; }\

    END_EXTERN_C()

 

BEGIN_EXTERN_C 就是extern "C" {,其实这里暴露出来的就是 get_module 方法,dlsym(dl,'get_module')就会返回这个函数指针,调用后就返回 hanks_module_entry 这个zend_module_entry结构体,包含这个模块的相关信息和方法,这样php就能完全掌握这个模块,融入内部的体系中。

php调用so库和a库方法

  • (一)调用so方法

(引自:http://tech.idv2.com/2007/07/06/use-local-so-in-php/)

某个功能被编译到so文件中,那么如何通过php来调用它?一个方法是写一个php模块(php extension),在php中调用该模块内的函数,再通过该模块来调用so中的函数。下面做一个简单的例子,使用的操作系统是Fedora Core 6。

首先做一个简单的so文件:

/**
 * hello.c
 * To compile, use following commands:
 *   gcc -O -c -fPIC -o hello.o hello.c 
 *   gcc -shared -o libhello.so hello.o
 */

int hello_add(int a, int b)
{
    return a + b;
}

然后将它编译成.so文件并放到系统中:

$ gcc -O -c -fPIC -o hello.o hello.c
$ gcc -shared -o libhello.so hello.o
$ su
# echo /usr/local/lib > /etc/ld.so.conf.d/local.conf
# cp libhello.so /usr/local/lib
# /sbin/ldconfig

写段小程序来验证其正确性:

/**
 * hellotest.c
 * To compile, use following commands:
 *   gcc -o hellotest -lhello hellotest.c
 */
#include <stdio.h>
int main()
{
    int a = 3, b = 4;
    printf("%d + %d = %d\n", a, b, hello_add(a,b));
    return 0;
}

编译并执行:

$ gcc -o hellotest -lhello hellotest.c
$ ./hellotest
3 + 4 = 7

OK,下面我们来制作PHP模块。首先确保你安装了 php-devel 包,没有的话请自行从安装光盘上找。然后下载php源代码。我使用的是php-5.2.3.tar.gz,解压缩。

$ tar xzvf php-5.2.3.tar.gz
$ cd php-5.2.3/ext

然后通过下面的命令建立一个名为 hello 的模块。

$ ./ext_skel --extname=hello

执行该命令之后它会提示你应当用什么命令来编译模块,可惜那是将模块集成到php内部的编译方法。如果要编译成可动态加载的 php_hello.so,方法要更为简单。

$ cd hello

首先编辑 config.m4 文件,去掉第16行和第18行的注释(注释符号为 dnl 。)

16:  PHP_ARG_ENABLE(hello, whether to enable hello support,
17:  dnl Make sure that the comment is aligned:
18:  [  --enable-hello           Enable hello support])

然后执行 phpize 程序,生成configure脚本:

$ phpize

然后打开 php_hello.h,在 PHP_FUNCTION(confirm_hello_compiled); 之下加入函数声明:

PHP_FUNCTION(confirm_hello_compiled);   /* For testing, remove later. */
PHP_FUNCTION(hello_add);

打开 hello.c,在 PHP_FE(confirm_hello_compiled, NULL) 下方加入以下内容。

zend_function_entry hello_functions[] = {
    PHP_FE(confirm_hello_compiled,  NULL)       /* For testing, remove later. */
    PHP_FE(hello_add,   NULL)       /* For testing, remove later. */
    {NULL, NULL, NULL}  /* Must be the last line in hello_functions[] */
};

然后在 hello.c 的最末尾书写hello_add函数的内容:

PHP_FUNCTION(hello_add)
{
    long int a, b;
    long int result;

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ll", &a, &b) == FAILURE) {
        return;
    }

    result = hello_add(a, b);

    RETURN_LONG(result);
}

保存退出,编译并安装:

$ ./configure
$ make LDFLAGS=-lhello
$ su
# cp modules/hello.so /usr/lib/php/modules

然后在 /var/www/html 下建立一个 hello.php 文件,内容如下:

<?php
    dl("hello.so");
    echo hello_add(3, 4);
?>

然后在浏览器中打开hello.php文件,如果显示7,则说明函数调用成功了。

参考URL

 

  • 调用a库方法:

(引自:http://group.qqread.com/thread-22305-1-1.html)

把静态库加入PHP:
       把要编译的静态库链接存入环境变量。假设静态库的文件名叫libnpc.a,放在/home目录下。在PHP的安装目录下输入如下命令:export LDFLAGS=”–L/home –lnpc”
       这个环境变量的作用就是让PHP在编译时知道要把这个库也一起编译进去。

编译PHP:
       和普通编译PHP没什么大区别,就是要在./configure的时候加上你的扩展,具体见编写PHP扩展部分。
       当编译好之后可以通过unset LDFLAGS来删除之前的环境变量。

总结:

动态库和静态库相同的地方就是上面橘色的地方,不同的地方so库是紫色的方法,而a库

s#export LDFLAGS=”–L/home –lnpc”

s#./configure

s#make

s#unset LDFLAGS

 

举例分析Linux动态库和静态库

函数库分为静态库和动态库两种。创建Linux静态库和Linux动态库和使用它们在这里将以举例的形式详述一下。静态库在程序编译时会被连接到目标代码中,程序运行时将不再需要该静态库。动态库在程序编译时并不会被连接到目标代码中,而是在程序运行是才被载入,因此在程序运行时还需要动态库存在。

第1步:编辑得到举例的程序--hello.h、hello.c和main.c;

hello.h(见程序1)为该函数库的头文件。
hello.c(见程序2)是函数库的源程序,其中包含公用函数hello,该函数将在屏幕上输出"Hello XXX!"。
main.c(见程序3)为测试库文件的主程序,在主程序中调用了公用函数hello。

  1. --------------------------------------------------------------------------------  
  2.  程序1: hello.h  
  3. #ifndef HELLO_H  
  4. #define HELLO_H  
  5. void hello(const char *name);  
  6. #endif //HELLO_H  
  7. --------------------------------------------------------------------------------  
  8.  程序2: hello.c  
  9.  
  10. #include <stdio.h> 
  11. void hello(const char *name)  
  12. {  
  13.     printf("Hello %s!\n", name);  
  14. }  
  15. --------------------------------------------------------------------------------  
  16.  程序3: main.c  
  17. #include "hello.h"  
  18. int main()  
  19. {  
  20.     hello("everyone");  
  21.     return 0;  
  22. }  
  23. -------------------------------------------------------------------------------- 

第2步:将hello.c编译成.o文件

无论静态库,还是动态库,都是由.o文件创建的。因此,我们必须将源程序hello.c通过gcc先编译成.o文件。在系统提示符下键入以下命令得到hello.o文件。

# gcc -c hello.c

第3步:由.o文件创建静态库

静态库文件名的命名规范是以lib为前缀,紧接着跟静态库名,扩展名为.a。例如:我们将创建的静态库名为myhello,则静态库文件名就是libmyhello.a。在创建和使用静态库时,需要注意这点。创建静态库用ar命令。在系统提示符下键入以下命令将创建静态库文件libmyhello.a。

# ar cr libmyhello.a hello.o

第4步:在程序中使用静态库

静态库制作完了,如何使用它内部的函数呢?只需要在使用到这些公用函数的源程序中包含这些公用函数的原型声明,然后在用gcc命令生成目标文件时指明静态库名,gcc将会从静态库中将公用函数连接到目标文件中。注意,gcc会在静态库名前加上前缀lib,然后追加扩展名.a得到的静态库文件名来查找静态库文件。在程序3:main.c中,我们包含了静态库的头文件hello.h,然后在主程序main中直接调用公用函数hello。下面先生成目标程序hello,然后运行hello程序看看结果如何。
# gcc -o hello main.c -L. -lmyhello
# ./hello
Hello everyone!
#
我们删除静态库文件试试公用函数hello是否真的连接到目标文件 hello中了。
# rm libmyhello.a
rm: remove regular file `libmyhello.a'? y
# ./hello
Hello everyone!
#
程序照常运行,静态库中的公用函数已经连接到目标文件中了。我们继续看看如何在Linux中创建动态库。我们还是从.o文件开始。

第5步:由.o文件创建动态库文件

动态库文件名命名规范和静态库文件名命名规范类似,也是在动态库名增加前缀lib,但其文件扩展名为.so。例如:我们将创建的动态库名为myhello,则动态库文件名就是libmyhello.so。用gcc来创建动态库。在系统提示符下键入以下命令得到动态库文件libmyhello.so。

# gcc -shared -fPCI -o libmyhello.so hello.o

第6步:在程序中使用动态库

在程序中使用动态库和使用静态库完全一样,也是在使用到这些公用函数的源程序中包含这些公用函数的原型声明,然后在用gcc命令生成目标文件时指明动态库名进行编译。我们先运行gcc命令生成目标文件,再运行它看看结果。
# gcc -o hello main.c -L. -lmyhello
# ./hello
./hello: error while loading shared libraries: libmyhello.so: cannot open shared object file: No such file or directory
#
哦!出错了。快看看错误提示,原来是找不到动态库文件libmyhello.so。程序在运行时,会在/usr/lib和/lib等目录中查找需要的动态库文件。若找到,则载入动态库,否则将提示类似上述错误而终止程序运行。我们将文件libmyhello.so复制到目录/usr/lib中,再试试。
# mv libmyhello.so /usr/lib
# ./hello
Hello everyone!
#
成功了。这也进一步说明了动态库在程序运行时是需要的。
我们回过头看看,发现使用静态库和使用动态库编译成目标程序使用的gcc命令完全一样,那当静态库和动态库同名时,gcc命令会使用哪个库文件呢?抱着对问题必究到底的心情,来试试看。先删除 除.c和.h外的 所有文件,恢复成我们刚刚编辑完举例程序状态。
# rm -f hello hello.o /usr/lib/libmyhello.so
# ls
hello.c hello.h main.c
#
在来创建静态库文件libmyhello.a和动态库文件libmyhello.so。
# gcc -c hello.c
# ar cr libmyhello.a hello.o
# gcc -shared -fPCI -o libmyhello.so hello.o
# ls
hello.c hello.h hello.o libmyhello.a libmyhello.so main.c
#
通过上述最后一条ls命令,可以发现静态库文件libmyhello.a和动态库文件libmyhello.so都已经生成,并都在当前目录中。然后,我们运行gcc命令来使用函数库myhello生成目标文件hello,并运行程序 hello。
# gcc -o hello main.c -L. -lmyhello
# ./hello
./hello: error while loading shared libraries: libmyhello.so: cannot open shared object file: No such file or directory
#
从程序hello运行的结果中很容易知道,当Linux静态库和Linux动态库同名时, gcc命令将优先使用动态库。

如何查看静态库内容 Unix/Linux

如何查看静态库内容 Unix/Linux


以下从最好情况->最坏情况:

1. 最好知道静态库的原文件(.c),要是知道声明文件(.h)也比较好。

2. ar -t YourFile 看其结构,找其中的原文件。

3. 可以将库文件下到本地用UE打开,然后你就找吧。

另外,看动态库用 nm -D lib*.so


附:

1.ar基本用法
2.nm基本用法命令

 当我们的程序中有经常使用的模块,而且这种模块在其他程序中也会用到,这时按照软件重用的思想,我们应该将它们生成库,使得以后编程可以减少开发代码量。这里介绍两个命令ar和nm,用来对库操作。

1.ar基本用法

  ar命令可以用来创建、修改库,也可以从库中提出单个模块。库是一单独的文件,里面包含了按照特定的结构组织起来的其它的一些文件(称做此库文件的member)。原始文件的内容、模式、时间戳、属主、组等属性都保留在库文件中。

  下面是ar命令的格式:

  ar [-]{dmpqrtx}[abcfilNoPsSuvV] [membername] [count] archive files...


  例如我们可以用ar rv libtest.a hello.o hello1.o来生成一个库,库名字是test,链接时可以用-ltest链接。该库中存放了两个模块hello.o和hello1.o。选项前可以有‘-‘字符,也可以没有。下面我们来看看命令的操作选项和任选项。现在我们把{dmpqrtx}部分称为操作选项,而[abcfilNoPsSuvV]部分称为任选项。

  {dmpqrtx}中的操作选项在命令中只能并且必须使用其中一个,它们的含义如下:

d:从库中删除模块。按模块原来的文件名指定要删除的模块。如果使用了任选项v则列出被删除的每个模块。
m:该操作是在一个库中移动成员。当库中如果有若干模块有相同的符号定义(如函数定义),则成员的位置顺序很重要。如果没有指定任选项,任何指定的成员将移到库的最后。也可以使用'a','b',或'I'任选项移动到指定的位置。
p:显示库中指定的成员到标准输出。如果指定任选项v,则在输出成员的内容前,将显示成员的名字。如果没有指定成员的名字,所有库中的文件将显示出来。
q:快速追加。增加新模块到库的结尾处。并不检查是否需要替换。'a','b',或'I'任选项对此操作没有影响,模块总是追加的库的结尾处。如果使用了任选项v则列出每个模块。 这时,库的符号表没有更新,可以用'ar s'或ranlib来更新库的符号表索引。
r:在库中插入模块(替换)。当插入的模块名已经在库中存在,则替换同名的模块。如果若干模块中有一个模块在库中不存在,ar显示一个错误消息,并不替换其他同名模块。默认的情况下,新的成员增加在库的结尾处,可以使用其他任选项来改变增加的位置。
t:显示库的模块表清单。一般只显示模块名。
x:从库中提取一个成员。如果不指定要提取的模块,则提取库中所有的模块。
  下面在看看可与操作选项结合使用的任选项:

a:在库的一个已经存在的成员后面增加一个新的文件。如果使用任选项a,则应该为命令行中membername参数指定一个已经存在的成员名。
b:在库的一个已经存在的成员前面增加一个新的文件。如果使用任选项b,则应该为命令行中membername参数指定一个已经存在的成员名。
c:创建一个库。不管库是否存在,都将创建。
f:在库中截短指定的名字。缺省情况下,文件名的长度是不受限制的,可以使用此参数将文件名截短,以保证与其它系统的兼容。
i:在库的一个已经存在的成员前面增加一个新的文件。如果使用任选项i,则应该为命令行中membername参数指定一个已经存在的成员名(类似任选项b)。
l:暂未使用
N:与count参数一起使用,在库中有多个相同的文件名时指定提取或输出的个数。
o:当提取成员时,保留成员的原始数据。如果不指定该任选项,则提取出的模块的时间将标为提取出的时间。
P:进行文件名匹配时使用全路径名。ar在创建库时不能使用全路径名(这样的库文件不符合POSIX标准),但是有些工具可以。
s:写入一个目标文件索引到库中,或者更新一个存在的目标文件索引。甚至对于没有任何变化的库也作该动作。对一个库做ar s等同于对该库做ranlib。
S:不创建目标文件索引,这在创建较大的库时能加快时间。
u:一般说来,命令ar r...插入所有列出的文件到库中,如果你只想插入列出文件中那些比库中同名文件新的文件,就可以使用该任选项。该任选项只用于r操作选项。
v:该选项用来显示执行操作选项的附加信息。
V:显示ar的版本。
2.nm基本用法命令

  nm用来列出目标文件的符号清单。下面是nm命令的格式:

  nm [-a|--debug-syms] [-g|--extern-only] [-B][-C|--demangle] [-D|--dynamic] [-s|--print-armap][-o|--print-file-name] [-n|--numeric-sort][-p|--no-sort] [-r|--reverse-sort] [--size-sort][-u|--undefined-only] [-l|--line-numbers] [--help][--version] [-t radix|--radix=radix][-P|--portability] [-f format|--format=format][--target=bfdname] [objfile...]


  如果没有为nm命令指出目标文件,则nm假定目标文件是a.out。下面列出该命令的任选项,大部分支持"-"开头的短格式和"—"开头的长格式。

-A、-o或--print-file-name:在找到的各个符号的名字前加上文件名,而不是在此文件的所有符号前只出现文件名一次。
例如nm libtest.a的输出如下:

CPThread.o:
00000068 T Main__8CPThreadPv
00000038 T Start__8CPThread
00000014 T _._8CPThread
00000000 T __8CPThread
00000000 ? __FRAME_BEGIN__
.......................................

则nm -A 的输出如下:

libtest.a:CPThread.o:00000068 T Main__8CPThreadPv
libtest.a:CPThread.o:00000038 T Start__8CPThread
libtest.a:CPThread.o:00000014 T _._8CPThread
libtest.a:CPThread.o:00000000 T __8CPThread
libtest.a:CPThread.o:00000000 ? __FRAME_BEGIN__
..................................................................

-a或--debug-syms:显示调试符号。
-B:等同于--format=bsd,用来兼容MIPS的nm。
-C或--demangle:将低级符号名解码(demangle)成用户级名字。这样可以使得C++函数名具有可读性。
-D或--dynamic:显示动态符号。该任选项仅对于动态目标(例如特定类型的共享库)有意义。
-f format:使用format格式输出。format可以选取bsd、sysv或posix,该选项在GNU的nm中有用。默认为bsd。
-g或--extern-only:仅显示外部符号。
-n、-v或--numeric-sort:按符号对应地址的顺序排序,而非按符号名的字符顺序。
-p或--no-sort:按目标文件中遇到的符号顺序显示,不排序。
-P或--portability:使用POSIX.2标准输出格式代替默认的输出格式。等同于使用任选项-f posix。
-s或--print-armap:当列出库中成员的符号时,包含索引。索引的内容包含:哪些模块包含哪些名字的映射。
-r或--reverse-sort:反转排序的顺序(例如,升序变为降序)。
--size-sort:按大小排列符号顺序。该大小是按照一个符号的值与它下一个符号的值进行计算的。
-t radix或--radix=radix:使用radix进制显示符号值。radix只能为"d"表示十进制、"o"表示八进制或"x"表示十六进制。
--target=bfdname:指定一个目标代码的格式,而非使用系统的默认格式。
-u或--undefined-only:仅显示没有定义的符号(那些外部符号)。
-l或--line-numbers:对每个符号,使用调试信息来试图找到文件名和行号。对于已定义的符号,查找符号地址的行号。对于未定义符号,查找指向符号重定位入口的行号。如果可以找到行号信息,显示在符号信息之后。
-V或--version:显示nm的版本号。
--help:显示nm的任选项。

Linux动态库、静态库加载基础

Linux动态库、静态库加载基础
静态库
       在linux环境中, 使用ar命令创建静态库文件.如下是命令的选项:
          d -----从指定的静态库文件中删除文件
          m -----把文件移动到指定的静态库文件中
          p -----把静态库文件中指定的文件输出到标准输出
          q -----快速地把文件追加到静态库文件中
          r -----把文件插入到静态库文件中
          t -----显示静态库文件中文件的列表
          x -----从静态库文件中提取文件
          还有多个修饰符修改以上基本选项,详细请man ar 以下列出三个:
          a -----把新的目标文件(*.o)添加到静态库文件中现有文件之后
          b -----***************************************之前
          v -----使用详细模式       ar 命令的命令行格式如下:       ar [-]{dmpqrtx}[abcfilNoPsSuvV] [membername] [count] archive files...      eg:         ar -crs hello.a hello.c
动态库
1.创建共享库
     gcc -c error.c               
     gcc -c errorlog.c
     gcc -shared -o libapue.so error.o errorlog.o
这样就创建了共享库!
2.编译共享库
    假设共享库位于当前目录(即跟程序文件相同的目录中)
gcc -o test -L. -lapue test.c
这样就编译出了不包含函数代码可执行文件了,但是但你运行时会发现linux动态加载器打不到libapue.so文件.
可以用ldd 命令查看可执行文件依赖什么共享库:
ldd test
如何才能让动态加载器发现库文件呢?有两种方法可以解决:
     LD_LIBRARY_PATH 环境变量  
     /etc/ld.so.conf文件
    1.环境变量
       export LD_LIBRARY_PATH="dir$LD_LIBRARY_PATH"
    2.修改/etc/ld.so.conf文件.位于/etc/ld.so.conf
一般应用程序的库文件不与系统库文件放在同一个目录下,一般把应用程序的共享库文件放在/usr/local/lib下,新建一个属于自己的目录apue,然后把刚才libapue.so复制过去就行了
同时在/etc/ld.so.conf中新增一行:
/usr/local/lib/apue
实例分析:
    test.c
         #include
         #include "print.h"
         
         int main(void){
             printf("Function : main/n");
             print();
             printf("out main/n");
   
             return 0;
         }
     
     print.c
         #include
         #include
         int print(void){
            
             printf("Function : print/n");
             printf("Hello world/n");
             printf("out print/n");
             return 0;
         }
       1 : 创建动态库:
          gcc -o libprint.so -fPIC -rdynamic -shared print.c
     2 : 创建可执行文件
          gcc -o test -I$(INC_PATH) -L$(LIB_PATH) -lprint test.c
  3: 如果出现下列错误
          ./test: error while loading shared libraries: libprint.so: cannot open   shared object file: No such file or directory
      解决方法:
       1: export LD_LIBRARY_PATH=DIR:$LD_LIBRARY_PATH
       2: 修改文件/etc/ld.so.config文件,在末行加上你的库文件目录,最后,ldconfig刷新ld.so.cache文件即可。可以用strings命令查看ld.so.cache是否刷新:strings /etc/ld.so.cache | grep print
     
4:   ldd命令查看动态连接库
            
            linux-gate.so.1 =>  (0xb7f0e000)
            libprint.so => not found
            libc.so.6 => /lib/tls/i686/cmov/libc.so.6 (0xb7d82000)
            /lib/ld-linux.so.2 (0xb7ef4000)
5 : nm查看程序中有那些符号
      
049f18 d _DYNAMIC
08049ff4 d _GLOBAL_OFFSET_TABLE_
080485cc R _IO_stdin_used
         w _Jv_RegisterClasses
08049f08 d __CTOR_END__
08049f04 d __CTOR_LIST__
08049f10 D __DTOR_END__
08049f0c d __DTOR_LIST__
080485ec r __FRAME_END__
08049f14 d __JCR_END__
08049f14 d __JCR_LIST__
0804a018 A __bss_start
0804a010 D __data_start
08048580 t __do_global_ctors_aux
08048450 t __do_global_dtors_aux
0804a014 D __dso_handle
         w __gmon_start__
0804857a T __i686.get_pc_thunk.bx
08049f04 d __init_array_end
08049f04 d __init_array_start
08048510 T __libc_csu_fini
08048520 T __libc_csu_init
         U __libc_start_main@@GLIBC_2.0
0804a018 A _edata
0804a020 A _end
080485ac T _fini
080485c8 R _fp_hw
0804839c T _init
08048420 T _start
0804a018 b completed.6625
0804a010 W data_start
0804a01c b dtor_idx.6627
080484b0 t frame_dummy
080484d4 T main
         U print
         U puts@@GLIBC_2.0
6: strip取出程序中符号
7: strings查看可执行文件中的文本信息
      
参考:http://www.kuqin.com/linux/20081103/25444.html
      http://hi.baidu.com/li_zhongnan/blog/item/1d9bf3c2e13a9f32e4dd3b4f.html

Records:271234