2009-05-07

pkg-config使用

1 pkg-config介绍

pkg-config用来检索系统中安装库文件的信息。典型的是用作库的编译和连接。如在Makefile中:

program: program.c

cc program.c `pkg-config --cflags --libs gnomeui`


pkg-config --list-all 可以列出所有可使用的包。

pkg-config要求库提供一个.pc元数据文件,从这些文件中检索库的各种必要信息,包括版本信息,编译和连接需要的参数等。这些信息可以通过pkg-config提供的参数(如--cflags, --libs)单独提取出来直接供编译器和连接器使用。

默认情况下,每个支持pkg-config的库对应的.pc文件,在安装后,都位于目录/usr/lib/pkgconfig目录下。

环境变量PKG_CONFIG_PATH是用来设置.pc文件的搜索路径的,pkg-config按照设置路径的先后顺序进行搜索。这样,库的头文件的搜索路径的设置实际上就变成了对.pc文件搜索路径的设置。

2 pkg-config功能

一般来说,即使是使用同一个库,不同的用户在安装时也可能会安装在不同的目录下。这样在编译时使用-I参数指定include路径,在连接时使用-L参数指定lib库的路径,可能造成了编译,连接的不一致,同一份程序从一台机器copy到另一台机器时就可能会出现问题。

pkg-config就是用来解决编译连接界面不统一问题的一个工具。

它 的基本思想:事先把库的各种必要信息保存在.pc文件中,需要的时候可以使用参数(--cflags, --libs),将所需信息提取出来供编译和连接使用。这样,不管库文件安装在哪,通过库对应的.pc文件就可以准确定位,可以使用相同的编译和连接命 令,使得编译和连接界面统一。

它提供的主要功能有:
<1> 检查库的版本号。如果所需库的版本不满足要求,打印出错误信息,避免连接错误版本的库文件。
<2> 获得编译预处理参数,如宏定义,头文件的路径。
<3> 获得编译参数,如库及其依赖的其他库的位置,文件名及其他一些连接参数。
<4> 自动加入所依赖的其他库的设置。

3 gtk+-2.0的.pc文件内容举例

prefix=/usr

exec_prefix=/usr

libdir=/usr/lib

includedir=/usr/include

target=x11



gtk_binary_version=2.4.0

gtk_host=i386-redhat-linux-gnu



Name: GTK+

Description: GIMP Tool Kit (${target} target)

Version: 2.6.7

Requires: gdk-${target}-2.0 atk

Libs: -L${libdir} -lgtk-${target}-2.0

Cflags: -I${includedir}/gtk-2.0


如果要使用gtk+的库编译一个程序,可以如下:

$gcc -c program.c -o program `pkg-config "gtk+-2.0>2.0.0" --cflags --libs`


其中,--cflags参数提取出编译所需的选项,--libs参数提取出连接时的选项。上面一步也可以分成两步如下:

$gcc -c program.c `pkg-config --cflags "gtk+-2.0>2.0.0"`

$gcc -o program program.o `pkg-config --libs "gtk+-2.0>2.0.0"`


4 环境变量PKG_CONFIG_PATH

在安装完一个需要使用的库后,比如Glib,一是将相应的.pc文件,如glib-2.0.pc拷贝到/usr/lib/pkgconfig目录下,二是通过设置环境变量PKG_CONFIG_PATH添加glib-2.0.pc文件的搜索路径。

尤其是不完全使用新安装的库取代旧库的时候,使用环境变量可能是更好的选择,因为环境窗口的设置只对当前终端窗口有效。

使用环境变量的方法,只需修改PKG_CONFIG_PATH和LD_LIBRARY_PATH变量。LD_LIBRARY_PATH变量主要是添加新安装库的搜索路径。

如新的Glib库安装在/usr/local/glib-2.0/lib下,可以编辑文件set_glib-2.0:

export PKG_CONFIG_PATH=/usr/local/lib/glib-2.0/lib/pkgconfig:$PKG_CONFIG_PATH

export LD_LIBRARY_PATH=/usr/local/lib/glib-2.0/lib:$LD_LIBRARY_PATH


每次使用时,source下就行了:

$source set_glib-2.0


5 其他的config

$ls /usr/bin/*config


参考:
1. ld.so.conf 文件与PKG_CONFIG_PATH变量,http://www.91linux.com/html/article/program/cpp/20071207/8934.html
2. Makefile好助手:pkgconfig, http://blog.mcuol.com/User/AT91RM9200/Article/7457_1.htm
3. pkg-config使用,http://blog.chinaunix.net/u/12467/showart_246860.html

原文: http://www.diybl.com/course/6_system/linux/Linuxjs/20090311/160653.html

ld.so.conf 文件与PKG_CONFIG_PATH变量

一、编译和连接

一般来说,如果库的头文件不在 /usr/include 目录中,那么在编译的时候需要用 -I 参数指定其路径。由于同一个库在不同系统上可能位于不同的目录下,用户安装库的时候也可以将库安装在不同的目录下,所以即使使用同一个库,由于库的路径的 不同,造成了用 -I 参数指定的头文件的路径也可能不同,其结果就是造成了编译命令界面的不统一。如果使用 -L 参数,也会造成连接界面的不统一。编译和连接界面不统一会为库的使用带来麻烦。

为了解决编译和连接界面不统一的问题,人们找到了一些解决办法。其基本思想就是:事先把库的位置信息等保存起来,需要的时候再通过特定的工具将其中有用的 信息提取出来供编译和连接使用。这样,就可以做到编译和连接界面的一致性。其中,目前最为常用的库信息提取工具就是下面介绍的 pkg-config。

pkg-config 是通过库提供的一个 .pc 文件获得库的各种必要信息的,包括版本信息、编译和连接需要的参数等。这些信息可以通过 pkg-config 提供的参数单独提取出来直接供编译器和连接器使用。

The pkgconfig package contains tools for passing the include path and/or library paths to build tools during the make file execution.

pkg-config is a function that returns meta information for the specified library.

The default setting for PKG_CONFIG_PATH is /usr/lib/pkgconfig because of the prefix we use to install pkgconfig. You may add to PKG_CONFIG_PATH by exporting additional paths on your system where pkgconfig files are installed. Note that PKG_CONFIG_PATH is only needed when compiling packages, not during run-time.

在默认情况下,每个支持 pkg-config 的库对应的 .pc 文件在安装后都位于安装目录中的 lib/pkgconfig 目录下。例如,我们在上面已经将 Glib 安装在 /opt/gtk 目录下了,那么这个 Glib 库对应的 .pc 文件是 /opt/gtk/lib/pkgconfig 目录下一个叫 glib-2.0.pc 的文件:

prefix=/opt/gtk/
exec_prefix=${prefix}
libdir=${exec_prefix}/lib
includedir=${prefix}/include

glib_genmarshal=glib-genmarshal
gobject_query=gobject-query
glib_mkenums=glib-mkenums

Name: GLib
Description: C Utility Library
Version: 2.12.13
Libs: -L${libdir} -lglib-2.0
Cflags: -I${includedir}/glib-2.0 -I${libdir}/glib-2.0/include

使用 pkg-config 的 --cflags 参数可以给出在编译时所需要的选项,而 --libs 参数可以给出连接时的选项。例如,假设一个 sample.c 的程序用到了 Glib 库,就可以这样编译:

$ gcc -c `pkg-config --cflags glib-2.0` sample.c

然后这样连接:

$ gcc sample.o -o sample `pkg-config --libs glib-2.0`

或者上面两步也可以合并为以下一步:

$ gcc sample.c -o sample `pkg-config --cflags --libs glib-2.0`

可以看到:由于使用了 pkg-config 工具来获得库的选项,所以不论库安装在什么目录下,都可以使用相同的编译和连接命令,带来了编译和连接界面的统一。

使用 pkg-config 工具提取库的编译和连接参数有两个基本的前提:

  1. 库本身在安装的时候必须提供一个相应的 .pc 文件。不这样做的库说明不支持 pkg-config 工具的使用。
  2. pkg-config 必须知道要到哪里去寻找此 .pc 文件。

GTK+ 及其依赖库支持使用 pkg-config 工具,所以剩下的问题就是如何告诉 pkg-config 到哪里去寻找库对应的 .pc 文件,这也是通过设置搜索路径来解决的。

对于支持 pkg-config 工具的 GTK+ 及其依赖库来说,库的头文件的搜索路径的设置变成了对 .pc 文件搜索路径的设置。.pc 文件的搜索路径是通过环境变量 PKG_CONFIG_PATH 来设置的,pkg-config 将按照设置路径的先后顺序进行搜索,直到找到指定的 .pc 文件为止。

安装完 Glib 后,在 bash 中应该进行如下设置:

$ export PKG_CONFIG_PATH=/opt/gtk/lib/pkgconfig:$PKG_CONFIG_PATH

可以执行下面的命令检查是否 /opt/gtk/lib/pkgconfig 路径已经设置在 PKG_CONFIG_PATH 环境变量中:

$ echo $PKG_CONFIG_PATH

这样设置之后,使用 Glib 库的其它程序或库在编译的时候 pkg-config 就知道首先要到 /opt/gtk/lib/pkgconfig 这个目录中去寻找 glib-2.0.pc 了(GTK+ 和其它的依赖库的 .pc 文件也将拷贝到这里,也会首先到这里搜索它们对应的 .pc 文件)。之后,通过 pkg-config 就可以把其中库的编译和连接参数提取出来供程序在编译和连接时使用。

另外还需要注意的是:环境变量的设置只对当前的终端窗口有效。如果到了没有进行上述设置的终端窗口中,pkg-config 将找不到新安装的 glib-2.0.pc 文件、从而可能使后面进行的安装(如 Glib 之后的 Atk 的安装)无法进行。


在我们采用的安装方案中,由于是使用环境变量对 GTK+ 及其依赖库进行的设置,所以当系统重新启动、或者新开一个终端窗口之后,如果想使用新安装的 GTK+ 库,需要如上面那样重新设置 PKG_CONFIG_PATH 和 LD_LIBRARY_PATH 环境变量。

这种使用 GTK+ 的方法,在使用之前多了一个对库进行设置的过程。虽然显得稍微繁琐了一些,但却是一种最安全的使用 GTK+ 库的方式,不会对系统上已经存在的使用了 GTK+ 库的程序(比如 GNOME 桌面)带来任何冲击。

为了使库的设置变得简单一些,可以把下面的这两句设置保存到一个文件中(比如 set_gtk-2.10 文件):

export PKG_CONFIG_PATH=/opt/gtk/lib/pkgconfig:$PKG_CONFIG_PATH
export LD_LIBRARY_PATH=/opt/gtk/lib:$LD_LIBRARY_PATH

之后,就可以用下面的方法进行库的设置了(其中的 source 命令也可以用 . 代替):

$ source set_gtk-2.10

只有在用新版的 GTK+ 库开发应用程序、或者运行使用了新版 GTK+ 库的程序的时候,才有必要进行上述设置。

如果想避免使用 GTK+ 库之前上述设置的麻烦,可以把上面两个环境变量的设置在系统的配置文件中(如 /etc/profile)或者自己的用户配置文件中(如 ~/.bash_profile) ;库的搜索路径也可以设置在 /etc/ld.so.conf 文件中,等等。这种设置在系统启动时会生效,从而会导致使用 GTK+ 的程序使用新版的 GTK+ 运行库,这有可能会带来一些问题。当然,如果你发现用新版的 GTK+ 代替旧版没有什么问题的话,使用这种设置方式是比较方便的。加入到~/.bashrc中,例如:
PKG_CONFIG_PATH=/opt/gtk/lib/pkgconfig
重启之后:
[root@localhost ~]# echo $PKG_CONFIG_PATH
/opt/gtk/lib/pkgconfig


二、运行时

库文件在连接(静态库和共享库)和运行(仅限于使用共享库的程序)时被使用,其搜索路径是在系统中进行设置的。一般 Linux 系统把 /lib 和 /usr/lib 两个目录作为默认的库搜索路径,所以使用这两个目录中的库时不需要进行设置搜索路径即可直接使用。对于处于默认库搜索路径之外的库,需要将库的位置添加到 库的搜索路径之中。设置库文件的搜索路径有下列两种方式,可任选其一使用:

  1. 在环境变量 LD_LIBRARY_PATH 中指明库的搜索路径。
  2. 在 /etc/ld.so.conf 文件中添加库的搜索路径。
将自己可能存放库文件的路径都加入到/etc/ld.so.conf中是明智的选择 ^_^
添加方法也极其简单,将库文件的绝对路径直接写进去就OK了,一行一个。例如:
/usr/X11R6/lib
/usr/local/lib
/opt/lib
需要注意的是:第二种搜索路径的设置方式对于程序连接时的库(包括共享库和静态库)的定位已经足够了,但是对于使用了共享库的程序的执行还是不够的。这是 因为为了加快程序执行时对共享库的定位速度,避免使用搜索路径查找共享库的低效率,所以是直接读取库列表文件 /etc/ld.so.cache 从中进行搜索的。/etc/ld.so.cache 是一个非文本的数据文件,不能直接编辑,它是根据 /etc/ld.so.conf 中设置的搜索路径由 /sbin/ldconfig 命令将这些搜索路径下的共享库文件集中在一起而生成的(ldconfig 命令要以 root 权限执行)。因此,为了保证程序执行时对库的定位,在 /etc/ld.so.conf 中进行了库搜索路径的设置之后,还必须要运行 /sbin/ldconfig 命令更新 /etc/ld.so.cache 文件之后才可以。ldconfig ,简单的说,它的作用就是将/etc/ld.so.conf列出的路径下的库文件 缓存到/etc/ld.so.cache 以供使用。因此当安装完一些库文件,(例如刚安装好glib),或者修改ld.so.conf增加新的库路径后,需要运行一下/sbin/ldconfig使所有的库文件都被缓存到ld.so.cache中,如果没做,即使库文件明明就在/usr/lib下的,也是不会被使用的,结果编译过程中抱错,缺少xxx库,去查看发现明明就在那放着,搞的想大骂computer蠢猪一个。 ^_^
在程序连接时,对于库文件(静态库和共享库)的搜索路径,除了上面的设置方式之外,还可以通过 -L 参数显式指定。因为用 -L 设置的路径将被优先搜索,所以在连接的时候通常都会以这种方式直接指定要连接的库的路径。

前面已经说明过了,库搜索路径的设置有两种方式:在环境变量 LD_LIBRARY_PATH 中设置以及在 /etc/ld.so.conf 文件中设置。其中,第二种设置方式需要 root 权限,以改变 /etc/ld.so.conf 文件并执行 /sbin/ldconfig 命令。而且,当系统重新启动后,所有的基于 GTK2 的程序在运行时都将使用新安装的 GTK+ 库。不幸的是,由于 GTK+ 版本的改变,这有时会给应用程序带来兼容性的问题,造成某些程序运行不正常。为了避免出现上面的这些情况,在 GTK+ 及其依赖库的安装过程中对于库的搜索路径的设置将采用第一种方式进行。这种设置方式不需要 root 权限,设置也简单:

$ export LD_LIBRARY_PATH=/opt/gtk/lib:$LD_LIBRARY_PATH

可以用下面的命令查看 LD_LIBRAY_PATH 的设置内容:

$ echo $LD_LIBRARY_PATH

至此,库的两种设置就完成了。

原文: http://hi.baidu.com/dexinmeng/blog/item/5512cf018b8941d5277fb571.html

2009-04-12

RPM 的介绍和应用

作者:北南南北
来自:LinuxSir.Org
提要:RPM 是 Red Hat Package Manager 的缩写,原意是Red Hat 软件包管理;本文介绍RPM,并结合实例来解说RPM手工安装、查询等应用;
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
正文:
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
RPM 是 Red Hat Package Manager 的缩写,本意是Red Hat 软件包管理,顾名思义是Red Hat 贡献出来的软件包管理;在Fedora 、Redhat、Mandriva、SuSE、YellowDog等主流发行版本,以及在这些版本基础上二次开发出来的发行版采用; RPM包里面都包含什么?里面包含可执行的二进制程序,这个程序和Windows的软件包中的.exe文件类似是可执行的;RPM包中还包括程序运行时所需要的文件,这也和Windows的软件包类似,Windows的程序的运行,除了.exe文件以外,也有其它的文件; 一个RPM 包中的应用程序,有时除了自身所带的附加文件保证其正常以外,还需要其它特定版本文件,这就是软件包的依赖关系;依赖关系并不是Linux特有的,Windows操作系统中也是同样存在的;比如我们在Windows系统中运行3D游戏,在安装的时候,他可能会提示,要安装Direct 9 ;Linux和Windows原理是差不多的; 软件安装流程图:
本文使用范围:

1、本文是对RPM管理的软件的说明,对通过file.tar.gz 或file.tar.bz2源码包用 make ;make install 安装的软件无效;
2、安装软件时,最好用各自发行版所提供的系统软件包管理工具,对于Fedora/Redhat 您可以参考如下文章; 1)Fedora 系统管理软件包工具 system-config-packages,方便的添加和移除系统安装盘提供的软件包,详情请看 《Fedora 软件包管理器system-config-packages》 2)Redhat 系统管理软件包工具,新一点的系统应该是 redhat-config-packages ,用法和 《Fedora 软件包管理器system-config-packages》 一样; 3)apt + synaptic 软件包在线安装、移除、升级工具; 用法:《用apt+synaptic 在线安装或升级Fedora core 4.0 软件包》
4)yum 软件包在线安装、升级、移除工具;用法:《Fedora/Redhat 在线安装更新软件包,yum 篇》 5)所有的yum和apt 教程 《apt and yum》 目前 apt和yum 已经极为成熟了,建议我们安装软件时,采用 apt或者yum ;如果安装系统盘提供的软件包,可以用 system-config-packages 或redhat-config-packages ;
一、RPM包管理的用途;
1、可以安装、删除、升级和管理软件;当然也支持在线安装和升级软件;
2、通过RPM包管理能知道软件包包含哪些文件,也能知道系统中的某个文件属于哪个软件包;
3、可以在查询系统中的软件包是否安装以及其版本;
4、作为开发者可以把自己的程序打包为RPM 包发布;
5、软件包签名GPG和MD5的导入、验证和签名发布
6、依赖性的检查,查看是否有软件包由于不兼容而扰乱了系统;
二、RPM 的使用权限;

RPM软件的安装、删除、更新只有root权限才能使用;对于查询功能任何用户都可以操作;如果普通用户拥有安装目录的权限,也可以进行安装;
三、rpm 的一点简单用法;

我们除了软件包管理器以外,还能通过rpm 命令来安装;是不是所有的软件包都能通过rpm 命令来安装呢?不是的,文件以.rpm 后缀结尾的才行;有时我们在一些网站上找到file.rpm ,都要用 rpm 来安装;

一)初始化rpm 数据库;

通过rpm 命令查询一个rpm 包是否安装了,也是要通过rpm 数据库来完成的;所以我们要经常用下面的两个命令来初始化rpm 数据库;
[root@localhost beinan]# rpm --initdb
[root@localhost beinan]# rpm --rebuilddb 注:这个要花好长时间;
注:这两个参数是极为有用,有时rpm 系统出了问题,不能安装和查询,大多是这里出了问题;

二)RPM软件包管理的查询功能:

命令格式
rpm {-q|--query} [select-options] [query-options]
RPM的查询功能是极为强大,是极为重要的功能之一;举几个常用的例子,更为详细的具体的,请参考#man rpm

1、对系统中已安装软件的查询;
1)查询系统已安装的软件;
语法:rpm -q 软件名
举例:
[root@localhost beinan]# rpm -q gaim
gaim-1.3.0-1.fc4
-q就是 --query ,中文意思是“问”,此命令表示的是,是不是系统安装了gaim ;如果已安装会有信息输出;如果没有安装,会输出gaim 没有安装的信息; 查看系统中所有已经安装的包,要加 -a 参数 ;
[root@localhost RPMS]# rpm -qa

如果分页查看,再加一个管道 |和more命令;
[root@localhost RPMS]# rpm -qa |more
在所有已经安装的软件包中查找某个软件,比如说 gaim ;可以用 grep 抽取出来;
[root@localhost RPMS]# rpm -qa |grep gaim
上面这条的功能和 rpm -q gaim 输出的结果是一样的; 2)查询一个已经安装的文件属于哪个软件包;
语法 rpm -qf 文件名

注:文件名所在的绝对路径要指出 举例:
[root@localhost RPMS]# rpm -qf /usr/lib/libacl.la
libacl-devel-2.2.23-8
3)查询已安装软件包都安装到何处;
语法:rpm -ql 软件名 或 rpm rpmquery -ql 软件名
举例:
[root@localhost RPMS]# rpm -ql lynx
[root@localhost RPMS]# rpmquery -ql lynx
4)查询一个已安装软件包的信息
语法格式: rpm -qi 软件名
举例:
[root@localhost RPMS]# rpm -qi lynx
5)查看一下已安装软件的配置文件;
语法格式:rpm -qc 软件名
举例:
[root@localhost RPMS]# rpm -qc lynx
6)查看一个已经安装软件的文档安装位置:
语法格式: rpm -qd 软件名
举例:
[root@localhost RPMS]# rpm -qd lynx
7)查看一下已安装软件所依赖的软件包及文件;
语法格式: rpm -qR 软件名
举例:
[root@localhost beinan]# rpm -qR rpm-python
查询已安装软件的总结:对于一个软件包已经安装,我们可以把一系列的参数组合起来用;比如 rpm -qil ;比如:
[root@localhost RPMS]# rpm -qil lynx

2、对于未安装的软件包的查看:

查看的前提是您有一个.rpm 的文件,也就是说对既有软件file.rpm的查看等; 1)查看一个软件包的用途、版本等信息;
语法: rpm -qpi file.rpm
举例:
[root@localhost RPMS]# rpm -qpi lynx-2.8.5-23.i386.rpm
2)查看一件软件包所包含的文件;
语法: rpm -qpl file.rpm
举例:
[root@localhost RPMS]# rpm -qpl lynx-2.8.5-23.i386.rpm
3)查看软件包的文档所在的位置;
语法: rpm -qpd file.rpm
举例:
[root@localhost RPMS]# rpm -qpd lynx-2.8.5-23.i386.rpm
5)查看一个软件包的配置文件;
语法: rpm -qpc file.rpm
举例:
[root@localhost RPMS]# rpm -qpc lynx-2.8.5-23.i386.rpm
4)查看一个软件包的依赖关系
语法: rpm -qpR file.rpm
举例:
[root@localhost archives]# rpm -qpR yumex_0.42-3.0.fc4_noarch.rpm
/bin/bash
/usr/bin/python
config(yumex) = 0.42-3.0.fc4
pygtk2
pygtk2-libglade
rpmlib(CompressedFileNames) <= 3.0.4-1
rpmlib(PayloadFilesHavePrefix) <= 4.0-1
usermode
yum >= 2.3.2

三)软件包的安装、升级、删除等;

1、安装和升级一个rpm 包;
[root@localhost beinan]#rpm -vih file.rpm 注:这个是用来安装一个新的rpm 包;
[root@localhost beinan]#rpm -Uvh file.rpm 注:这是用来升级一个rpm 包;
如果有依赖关系的,请解决依赖关系,其实软件包管理器能很好的解决依赖关系,请看前面的软件包管理器的介绍;如果您在软件包管理器中也找不到依赖关系的包;那只能通过编译他所依赖的包来解决依赖关系,或者强制安装; 语法结构:
[root@localhost beinan]# rpm -ivh file.rpm --nodeps --force
[root@localhost beinan]# rpm -Uvh file.rpm --nodeps --force
更多的参数,请查看 man rpm 举例应用:
[root@localhost RPMS]# rpm -ivh lynx-2.8.5-23.i386.rpm
Preparing... ########################################### [100%]
1:lynx ########################################### [100%]


[root@localhost RPMS]# rpm -ivh --replacepkgs lynx-2.8.5-23.i386.rpm
Preparing... ########################################### [100%]
1:lynx ########################################### [100%]
注: --replacepkgs 参数是以已安装的软件再安装一次;有时没有太大的必要; 测试安装参数 --test ,用来检查依赖关系;并不是真正的安装;
[root@localhost RPMS]# rpm -ivh --test gaim-1.3.0-1.fc4.i386.rpm
Preparing... ########################################### [100%]
由新版本降级为旧版本,要加 --oldpackage 参数;
[root@localhost RPMS]# rpm -qa gaim
gaim-1.5.0-1.fc4

[root@localhost RPMS]# rpm -Uvh --oldpackage gaim-1.3.0-1.fc4.i386.rpm
Preparing... ########################################### [100%]
1:gaim ########################################### [100%]

[root@localhost RPMS]# rpm -qa gaim
gaim-1.3.0-1.fc4
为软件包指定安装目录:要加 -relocate 参数;下面的举例是把gaim-1.3.0-1.fc4.i386.rpm指定安装在 /opt/gaim 目录中;
[root@localhost RPMS]# rpm -ivh --relocate /=/opt/gaim gaim-1.3.0-1.fc4.i386.rpm
Preparing... ########################################### [100%]
1:gaim ########################################### [100%]
[root@localhost RPMS]# ls /opt/
gaim
为软件包指定安装目录:要加 -relocate 参数;下面的举例是把lynx-2.8.5-23.i386.rpm 指定安装在 /opt/lynx 目录中;
[root@localhost RPMS]# rpm -ivh --relocate /=/opt/lynx --badreloc lynx-2.8.5-23.i386.rpm
Preparing... ########################################### [100%]
1:lynx ########################################### [100%]
我们安装在指定目录中的程序如何调用呢?一般执行程序,都放在安装目录的bin或者sbin目录中;看下面的例子;如果有错误输出,就做相应的链接,用 ln -s ;
[root@localhost RPMS]# /opt/lynx/usr/bin/lynx

Configuration file /etc/lynx.cfg is not available.

[root@localhost RPMS]# ln -s /opt/lynx/etc/lynx.cfg /etc/lynx.cfg
[root@localhost RPMS]# /opt/lynx/usr/bin/lynx www.linuxsir.org

2、删除一个rpm 包;
首先您要学会查询rpm 包 ;请看前面的说明; [root@localhost beinan]#rpm -e 软件包名 举例:我想移除lynx 包,完整的操作应该是:
[root@localhost RPMS]# rpm -e lynx
如果有依赖关系,您也可以用--nodeps 忽略依赖的检查来删除。但尽可能不要这么做,最好用软件包管理器 systerm-config-packages 来删除或者添加软件;
[root@localhost beinan]# rpm -e lynx --nodeps

四、导入签名:

[root@localhost RPMS]# rpm --import 签名文件 举例:
[root@localhost fc40]# rpm --import RPM-GPG-KEY
[root@localhost fc40]# rpm --import RPM-GPG-KEY-fedora
关于RPM的签名功能,详情请参见 man rpm

五、RPM管理包管理器支持网络安装和查询;

比如我们想通过 Fedora Core 4.0 的一个镜像查询、安装软件包; 地址:
http://mirrors.kernel.org/fedora/core/4/i386/os/Fedora/RPMS/ 举例: 命令格式:
rpm 参数 rpm包文件的http或者ftp的地址
# rpm -qpi http://mirrors.kernel.org/fedora/core/4/i386/os/ Fedora/RPMS/gaim-1.3.0-1.fc4.i386.rpm
# rpm -ivh http://mirrors.kernel.org/fedora/core/4/i386/os/ Fedora/RPMS/gaim-1.3.0-1.fc4.i386.rpm
举一反三吧;
六、对已安装软件包查询的一点补充;

[root@localhost RPMS]# updatedb
[root@localhost RPMS]# locate 软件名或文件名
通过updatedb,我们可以用 locate 来查询一些软件安装到哪里了;系统初次安装时要执行updatedb ,每隔一段时间也要执行一次;以保持已安装软件库最新;updatedb 是slocate软件包所有;如果您没有这个命令,就得安装slocate ; 举例:
[root@localhost RPMS]# locate gaim


七、从rpm软件包抽取文件;

命令格式: rpm2cpio file.rpm |cpio -div

举例:
[root@localhost RPMS]# rpm2cpio gaim-1.3.0-1.fc4.i386.rpm |cpio -div
抽取出来的文件就在当用操作目录中的 usr 和etc中; 其实这样抽到文件不如指定安装目录来安装软件来的方便;也一样可以抽出文件; 为软件包指定安装目录:要加 -relocate 参数;下面的举例是把gaim-1.3.0-1.fc4.i386.rpm指定安装在 /opt/gaim 目录中;
[root@localhost RPMS]# rpm -ivh --relocate /=/opt/gaim gaim-1.3.0-1.fc4.i386.rpm
Preparing... ########################################### [100%]
1:gaim ########################################### [100%]
[root@localhost RPMS]# ls /opt/
gaim
这样也能一目了然;gaim的所有文件都是安装在 /opt/gaim 中,我们只是把gaim 目录备份一下,然后卸掉gaim;这样其实也算提取文件的一点用法;
八、RPM的配置文件;

RPM包管理,的配置文件是 rpmrc ,我们可以在自己的系统中找到;比如Fedora Core 4.0中的rpmrc 文件位于;
[root@localhost RPMS]# locate rpmrc
/usr/lib/rpm/rpmrc
/usr/lib/rpm/redhat/rpmrc
我们可以通过 rpm --showrc 查看;具体的还得我们自己来学习。呵。。。不要问我,我也不懂;只要您看了这篇文章,认为对您有用,您的水平就和我差不多;咱们水平是一样的,所以我不能帮助您了;请理解;

九、src.rpm的用法:

《file.src.rpm 使用方法的简介》
后记:Fedora/Redhat 入门教程中的软件包管理篇,我已经写了很多了;目前还缺少通过源码包安装软件我方法以及一篇总结性的文档;我想在最近两天补齐,这两篇我以前写过;重新整理一下贴出来就行了; 以我的水平来看,写Fedora 入门教程是极为费力气的,只能一点一点的完善和补充;我所写的教程是面对的是对Linux一无所知新手;教程中实例应用占大部份;我发现没有实例的情况下,新手不如看man ;能看man了,当然也不是什么新手; 经常在论坛上看一些弟兄的提问,虽然一问话解说过去也能应付;但想让大家更方便一点,不如写系统入门教程;虽然所花的时间要长一点;
附录:
《Fedora / Redhat 软件包管理指南》

2009-04-08

浅析iniit.rc脚本中的service程序不写disabled字段why会自动执行

浅析iniit.rc脚本中的service程序不写disabled字段why会自动执行

1.ramdisk的/init.rc中,
...
on boot//在boot阶段执行
ifup lo
hostname localhost
...
class_start default //对应KEYWORD(class_start, COMMAND, 1, do_class_start)
...
service console /system/bin/sh //这个sh就是busybox或者toolbox等开源工具的一个ln -s连接
console
...

==>do_class_start
==>service_for_each_class
==>service_start_if_not_disabled
==>service_start
KEYWORD(class_start, COMMAND, 1, do_class_start)
int do_class_start(int nargs, char **args)
{
/* Starting a class does not start services
* which are explicitly disabled. They must
* be started individually.
*/
service_for_each_class(args[1], service_start_if_not_disabled);
return 0;
}

void service_for_each_class(const char *classname,
void (*func)(struct service *svc))
{
struct listnode *node;
struct service *svc;
list_for_each(node, &service_list) {
svc = node_to_item(node, struct service, slist);
if (!strcmp(svc->classname, classname)) {//执行所有classname为上面class_start default指定的default的svc程序
//所以这样service console /system/bin/sh我们的console交互程序shell -- /system/bin/sh处理来自console开发板的串口输入了,并且会将结果output输出到console开发板的串口上[luther.gliethttp]
func(svc);
}
}
}

static void service_start_if_not_disabled(struct service *svc)
{
if (!(svc->flags & SVC_DISABLED)) {
service_start(svc);
}
}

==>service_start
...
needs_console = (svc->flags & SVC_CONSOLE) ? 1 : 0;
...
pid = fork();
if (pid == 0) {
...
if (needs_console) {
setsid();
open_console();//打开console
} else {
zap_stdio();
}
...
execve(svc->args[0], (char**) svc->args, (char**) ENV);//执行函数
...
static void open_console()
{
int fd;
if ((fd = open(console_name, O_RDWR)) < 0) {
fd = open("/dev/null", O_RDWR);
}
dup2(fd, 0);
dup2(fd, 1);
dup2(fd, 2);
close(fd);
}
2.解析init.rc脚本的部分程序
parse_config_file("/init.rc");
snprintf(tmp, sizeof(tmp), "/init.%s.rc", hardware);
parse_config_file(tmp);

==>parse_config_file
==>parse_config
==>parse_new_section
...
switch(kw) {
case K_service:
state->context = parse_service(state, nargs, args);
if (state->context) {
state->parse_line = parse_line_service;
return;
}
break;
...
==>parse_service
...
svc->name = args[1];
svc->classname = "default";//默认的classname类名
...
list_add_tail(&service_list, &svc->slist);
return svc;
3.system/init/init.c
==>main //处理restart类型的svc程序
action_for_each_trigger("early-init", action_add_queue_tail);
drain_action_queue();
action_for_each_trigger("init", action_add_queue_tail);
drain_action_queue();
action_for_each_trigger("early-boot", action_add_queue_tail);
action_for_each_trigger("boot", action_add_queue_tail);//最后执行init.rc和init.borad.rc脚本的on boot字段中的命令,其中就包括上面的class_start default命令字段
drain_action_queue();
for(;;) {
...
drain_action_queue();
restart_processes();
...
nr = poll(ufds, 3, timeout);
...
==>restart_processes
#define SVC_RESTARTING 0x08 /* waiting to restart */
#define SVC_CONSOLE 0x10 /* requires console */
==>service_for_each_flags(SVC_RESTARTING, restart_service_if_needed);
void service_for_each_flags(unsigned matchflags,
void (*func)(struct service *svc))
{
struct listnode *node;
struct service *svc;
list_for_each(node, &service_list) {//遍历service链表
svc = node_to_item(node, struct service, slist);
if (svc->flags & matchflags) {
func(svc);
}
}
}

http://blog.chinaunix.net/u1/38994/showart_1775465.html

busybox的所有命令如何快速安装到/bin目录下或者自定义目录下

直接执行./busybox --install即可

/data/luther # ./busybox --install -s 将创建符号连接
/data/luther # ./busybox --install 将直接生成文件
然后在手机的测试脚本.sh的第一行写上:
#!/data/bin/sh
这样kernel在加载我们的测试脚本.sh时,就会使用/data/bin/sh程序来运行我们的.sh文件


static void install_links(const char *busybox, int use_symbolic_links)
{
/* directory table
* this should be consistent w/ the enum,
* busybox.h::bb_install_loc_t, or else... */
static const char usr_bin [] ALIGN1 = "/usr/bin";//如果想修改其他路径可以在这里修改
static const char usr_sbin[] ALIGN1 = "/usr/sbin";
static const char *const install_dir[] = {
&usr_bin [8], /* "", equivalent to "/" for concat_path_file() */
&usr_bin [4], /* "/bin" */
&usr_sbin[4], /* "/sbin" */
usr_bin,
usr_sbin
};

static int busybox_main(char **argv)

比如这样修改,当执行./busybox --insatll之后,将会把所有程序安装到/data/bin和/data/sbin目录下
static void install_links(const char *busybox, int use_symbolic_links)
{
/* directory table
* this should be consistent w/ the enum,
* busybox.h::bb_install_loc_t, or else... */
static const char usr_bin [] ALIGN1 = "/data/bin";
static const char usr_sbin[] ALIGN1 = "/data/sbin";
static const char *const install_dir[] = {
&usr_bin [9], /* "", equivalent to "/" for concat_path_file() */
&usr_bin [0], /* "/bin" */
&usr_sbin[0], /* "/sbin" */
usr_bin,
usr_sbin
};
http://blog.chinaunix.net/u1/38994/showart_1782372.html

linux下查看本地字符集和系统支持的所有字符集

luther@gliethttp:~$ locale -a 查看本地字符集
luther@gliethttp:~$ locale -m 查看所有支持的字符集

将文件从gb2312转为utf8
iconv -f gb2312 -t utf8 input.txt -o output.txt

http://blog.chinaunix.net/u1/38994/showart_1784883.html

sed网摘笔记

http://www.7880.com/info/2004/12/03/article-9952.html
http://blog.chinaunix.net/u1/38994/showart_1739302.html
http://blog.chinaunix.net/u1/38994/showart_1784759.html

基本sed编辑命令

代码:
sed编辑命令
p 打印匹配行
P 将模式空间中的第一行内容输出到标准输出
= 显示文件行号
a \ 在定位行号后附加新文本信息
i \ 在定位行号后插入新文本信息
d 删除定位行
D 删除模式空间内的第一行
c \ 用新文本替换定位文本
s 使用替换模式替换相应模式
r 从另一个文件中读文本
w 写文本到一个文件
q 第一个模式匹配完成后推出或立即推出
l 显示与八进制A S C I I代码等价的控制字符
{ } 在定位行执行的命令组
n 从另一个文件中读文本下一行,并附加在下一行
N 读入下一行,将其追加到模式空间尾,当前行号加1
y 转换字符,比如小写转大写
比如:luther@gliethttp:/vobs/sed$ sed -e 'y/abcdef/ABCDEF/' b.txt
这样b.txt中的所有abcdef都将转为相应的大写字母,y操作不支持[a-z]正则表达式
或者:luther@gliethttp:/vobs/sed$ sed -e 'y/abcdef/123456/' b.txt
这样b.txt中的所有abcdef都将转为相应的123456.
n 延续到下一输入行;允许跨行的模式匹配语句
h 用模式空间内容替换后备空间内容
H 将模式空键内容追加到后备空间尾
g/G 同h/H,但方向相反,后备空间->模式空间
x 交换模式空间和后备空间内容
l 输出模式空间内容到标准输出,以更人性话的方式输出
比如:luther@gliethttp:/vobs/sed$ sed -ne 'l' b.txt
! 对不符合条件的行执行后续命令操作,
比如:luther@gliethttp:/vobs/sed$ sed -e '/luther/! d' b.txt
将只输出含有luther的行
luther@gliethttp:/vobs/sed$ sed -n -e '/luther/!p' b.txt
加入-n表示强制不输出pattern space模式空间中的内容到stdout,如果不加,那么b.txt的每一行都将输出




luther@gliethttp:/vobs/sed$ sed 'n;n;d' b.txt
表示从第1行开始,n取出下一行,然后n再取出下一行,然后对第3行进行d删除操作,同时行号自动增加,
所以当执行完d操作之后,将取第4行,所以这样将只显示
1,2
4,5
7,8
对3的倍数行执行删除操作
如果
luther@gliethttp:/vobs/sed$ sed 'n;d' b.txt
那么就是对2的倍数行进行删除操作.

luther@gliethttp:/vobs/sed$ sed '/^$/d' b.txt
删除所有空行

luther@gliethttp:/vobs/sed$ sed '/^$/d;G' b.txt
删除所有空行,同时使用G在非空行后面添加一个空行
sed如果发现/^$/满足,那么执行d删除操作,因为模式空间中的内容已将清除,所以后续的对该行的所有操作命令将不会被执行,sed命令将继续取出下一行,所以如果/^$/d执行,那么sed将执行不到;G命令就提前退出了,进行下一行数据操作了.

luther@gliethttp:/vobs/sed$ sed 's/^/# /' b.txt
在每行的开头添加'# '

luther@gliethttp:/vobs/sed$ sed 's/^.\{7\}//' b.txt
将b.txt文件每行的前7个字节删除

luther@gliethttp:/vobs/sed$ sed 's/$/] /' b.txt
在每行的结尾添加'] '




在vim中
:%s/^..//
这将删除每行的前2个字节
:%s/..$//
这将删除每行结尾的2个字节
:%s/^.\{7\}//
这将删除每行的前7个字节
:%!sed '/status="/a \echo $status'
在含有status="的行后面追加echo $status字符串


:%!sed '/[ -z "$result" ] && echo/r a.c'
向[ -z "$result" ] && echo字符串所在行的下一行追加a.c文件中的内容
:%!sed '/[ -z "$result" ] && echo/d'
删除[ -z "$result" ] && echo字符串所在行
这样组合之后,就可以替换文件中的一行数据为多行a.c文本中的数据



正则表达式的*表示该*前面的字符重复任意次,即:0次或者无数次,所以如果只写*号那么将不起任何作用,
所以如果想删除每行开头的所有空行或者空格,那么需要按如下方式写:
luther@gliethttp:/vobs/sed$ sed 's/^[ \t]*//' b.txt
这样就表示[ \t]这两个字符将使用*操作,重复0次或者无数次,当然也可以使用如下语句指定至少出现1次
luther@gliethttp:/vobs/sed$ sed 's/^[ \t]\{1,\}//' b.txt




只打印第二行,用-n
sed -n '2p' quote.txt
打印1到3行
sed -n '1,3p' quote.txt
打印含有'The'字符的行
sed -n '/The/p' quote.txt
打印含有'$'符号的行
sed -n '/\$/p' quote.txt
这个模式查询以i n g结尾的任意单词
sed -n '/.*ing/p' quote.txt
打印输出行范围设为第一行到最后一行1 , $。$意为最后一行
sed -n '1,$p' quote.txt
要打印行号,使用等号=。打印模式匹配的行号
整个文件都打印出来,并且匹配行打印了行号。如果只关心实际行号,使用- e选项。
sed -e '/music/=' quote.txt
如果只打印行号及匹配行,必须使用两个s e d命令,并使用e选项。第一个命令打印模式匹配行,第二个使用=选项打印行号,格式为sed -n -e /pattern/p -e /pattern/=
sed -n -e '/music/p' -e '/music/=' quote.txt
插入命令类似于附加命令,只是在指定行前面插入
sed "/company/i\Utter confusion followed." quote.txt
修改文本修改命令将在匹配模式空间的指定行用新文本加以替代
sed "/honeysuck/c\The Office Dibble band played well." quote.txt
删除文本
删除第一行;1 d意为删除第一行
sed '1d' quote.txt 2
删除第一到第三行
sed '1,3d' quote.txt
删除最后一行
sed '$d' quote.txt
也可以使用正则表达式进行删除操作
sed '/Neave/d' quote.txt
s选项通知s e d这是一个替换操作
's/{old value}/{new value}/'
替换选项如下:
引用:
g 缺省情况下只替换第一次出现模式,使用g选项替换全局所有出现模式。
p 缺省s e d将所有被替换行写入标准输出,加p选项将使- n选项无效。- n选项不打印输出结果。
w 文件名使用此选项将输出定向到一个文件。
sed 's/night/NIGHT/' quote.txt
删除$ 符号
sed 's/\$//' quote.txt
全局替换
sed 's/The/Wow/g' quote.txt
将替换结果写入一个文件用w选项,下面的例子将splendid替换为SPLENDID的替换结果写入文件sed.out:
sed 's/splendid/SPLENDID/w sed.out' quote.txt
使用替换修改字符串
如果要附加或修改一个字符串,可以使用(&)命令,&命令保存发现模式以便重新调用它,然后把它放在替换字符串里面。
先给出一个被替换模式,然后是一个准备附加在第一个模式后的另一个模式,并且后面带有&,这样修改模式将放在匹配模式之前。
sed -n 's/nurse/"hello" &/p' quote.txt
sed -n 's/played/from Hockering &/p' quote.txt
将sed结果写入文件命令
sed '1,2 w filedt' quote.txt
从文件中读文本
address r filename
这里r通知sed将从另一个文件源中读文本
sed '/company./r sedex.txt' quote.txt
匹配后退出
下面的例子假定查询模式/ . a . * /,意为任意字符后跟字符a,再跟任意字符0次或任意多次。
查询首次出现模式,然后退出。需要将q放在s e d语句末尾。
sed '/.a.*/q' quote.txt
处理控制字符
使用s e d实现的一个重要功能是在另一个系统中下载的文件中剔除控制字符
假定希望用 "2" 来替换 "1",但仅在单词 "two" 之后才作替换,而不是每一行的所有位置
sed '/two/ s/1/2/' sample_one
sed '/two/ s/1/2/; /three/ s/1/3/; /^$/ d' sample_one
从第一行直到第一个空行:
sed '1,/^$/ d' {filename}
这将文本添加到第 3 行之后
sed '3i\This is where we stop the test' sample_one

保持和获取:h命令和G命令
$ sed -e '/test/h' -e '$G example-----在sed处理文件的时候,每一行都被保存在一个叫模式空间的临时缓冲区中,除非行被删除或者输出被取消,否则所有被处理的行都将 打印在屏幕上。接着模式空间被清空,并存入新的一行等待处理。在这个例子里,匹配test的行被找到后,将存入模式空间,h命令将其复制并存入一个称为保 持缓存区的特殊缓冲区内。第二条语句的意思是,当到达最后一行后,G命令取出保持缓冲区的行,然后把它放回模式空间中,且追加到现在已经存在于模式空间中 的行的末尾。在这个例子中就是追加到最后一行。简单来说,任何包含test的行都被复制并追加到该文件的末尾。

保持和互换:h命令和x命令
$ sed -e '/test/h' -e '/check/x' example -----互换模式空间和保持缓冲区的内容。也就是把包含test与check的行互换。

http://blog.chinaunix.net/u1/38994/showart_1784907.html

浅析setsid()函数的作用

pid_t pid = fork();
if (pid == 0) {
...
int result = execl(path, "adb", "fork-server", "server", NULL);
} else {
// run a program in a new session
setsid();//之前parent和child运行在同一个session里,而且parent是session头,所以,
//所以作为session头的parent如果exit结束执行的话,那么会话session组中的所有进程将都被杀死,
//所以执行setsid()之后,parent将重新获得一个新的会话session组id,child将仍持有原有的会话session组,
//这时parent退出之后,将不会影响到child了[luther.gliethttp].
}

http://blog.chinaunix.net/u1/38994/showart_1807234.html

安装arm-elf-gcc编译器

安装arm-elf-gcc编译器
luther@gliethttp:/vobs/tools/arm-tools$ wget http://www.uclinux.org/pub/uClinux/arm-elf-tools/arm-elf-tools-20030314.sh
作如下修改,否则报错:tail: cannot open `+43' for reading: No such file or directory
以下方法参考:http://hi.baidu.com/xxwl04108/blog/item/5e0706ecd77e92d22f2e21c3.html
luther@gliethttp:/vobs/tools/arm-tools$ sed 's/^tail /tail -n/' arm-elf-tools-20030314.sh > arm-elf-tools-20030314.sh.luther
luther@gliethttp:/vobs/tools/arm-tools$ chmod +x arm-elf-tools-20030314.sh.luther
luther@gliethttp:/vobs/tools/arm-tools$ sudo ./arm-elf-tools-20030314.sh.luther 这样就可以正常安装了

http://blog.chinaunix.net/u1/38994/showart_1844445.html

Android Input Event Dispatching

[First written by Steve Guo, please keep the mark if forwarding.]

Input Event Detect and Dispatch

The input event dispatch engine is in WindowManagerService.java. WindowManagerService.java creates a thread to read input event from KeyInputQueue.java and dispatches the event to the window which has current focus through binder.

// Retrieve next event, waiting only as long as the next

// repeat timeout. If the configuration has changed, then

// don't wait at all -- we'll report the change as soon as

// we have processed all events.

QueuedEvent ev = mQueue.getEvent(

(int)((!configChanged && curTime <>

? (nextKeyTime-curTime) : 0));

If an input event is read, it judges the input event type. Currently support three input event types: key, trackball and pointer. Then according to event type call corresponding dispatch function to the window which has current focus through binder. For example, for key event, it calls the following code.

focus.mClient.dispatchKey(event);

At the lowest level, Android reads the real input event (keyboard, mouse or touch) from Linux input device. The corresponding source code is EventHub.cpp. For key input event, Android maps scan code to key code according to a key layout map file. OEM needs customize the key layout map file to match the needs of his own device. It uses the following method to find out the key layout map file.

// a more descriptive name

ioctl(mFDs[mFDCount].fd, EVIOCGNAME(sizeof(devname)-1), devname);

devname[sizeof(devname)-1] = 0;

device->name = devname;

// replace all the spaces with underscores

strcpy(tmpfn, devname);

for (char *p = strchr(tmpfn, ' '); p && *p; p = strchr(tmpfn, ' '))

*p = '_';

// find the .kl file we need for this device

const char* root = getenv("ANDROID_ROOT");

snprintf(keylayoutFilename, sizeof(keylayoutFilename),

"%s/usr/keylayout/%s.kl", root, tmpfn);

bool defaultKeymap = false;

if (access(keylayoutFilename, R_OK)) {

snprintf(keylayoutFilename, sizeof(keylayoutFilename),

"%s/usr/keylayout/%s", root, "qwerty.kl");

defaultKeymap = true;

}

device->layoutMap->load(keylayoutFilename);

OEM can get the key layout map file name during Android booting, because Android will log the name. The JAVA layer wrapper is KeyInputQueue.java which is used by WindowManagerService.java. It calls EventHub.cpp through JNI. com_android_server_KeyInputQueue.cpp is the JNI implementation.

Input Event Processing

When an activity is to be launched, ActivityManagerService.java calls ActivityThread.java for creating the activity.

activity.attach(appContext, this, getInstrumentation(), r.token, app,

r.intent, r.activityInfo, title, r.parent, r.embeddedID,

r.lastNonConfigurationInstance, config);

Then Activity.java creates a PhoneWindow.java instance to represent the activity. Each PhoneWindow.java contains a DecorView.java instance as the root of any views in the activity.

mWindow = PolicyManager.makeNewWindow(this);

mWindow.setCallback(this);

After an activity is created, ActivityManagerService.java calls ActivityThread.java for resume the activity. At this time, ActivityThread.java calls WindowManagerImpl.java to add the DecorView.java instance.

r.window = r.activity.getWindow();

View decor = r.window.getDecorView();

decor.setVisibility(View.INVISIBLE);

ViewManager wm = a.getWindowManager();

WindowManager.LayoutParams l = r.window.getAttributes();

a.mDecor = decor;

l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;

wm.addView(decor, l);

WindowManagerImpl.java news a ViewRoot.java instance. ViewRoot.java has a static member which will be only initialized once for each process. It’s used to let WindowManagerService.java know that there is a process linked now.

if (!mInitialized) {

try {

sWindowSession = IWindowManager.Stub.asInterface(

ServiceManager.getService("window"))

.openSession(new Binder());

mInitialized = true;

} catch (RemoteException e) {

}

}

After ViewRoot.java instance is created, WindowManagerImpl.java calls its setView API to bind the ViewRoot.java for the DecorView.java.

// do this last because it fires off messages to start doing things

root.setView(view, wparams, panelParentView);

In setView API, ViewRoot.java finally draws the DecorView.java and registers an IWindow instance to WindowManagerService.java.

res = sWindowSession.add(mWindow, attrs,

getHostVisibility(), mCoveredInsets);

After that WindowManagerService.java directly communicates (including dispatching input event) with this IWindow instance in ViewRoot.java. Then ViewRoot.java calls View.java to process input event. For example, for key event, dispatchKeyEvent API in View.java will be called.

public boolean dispatchKeyEvent(KeyEvent event) {

// If any attached key listener a first crack at the event.

//noinspection SimplifiableIfStatement

if (mOnKeyListener != null && (mViewFlags & ENABLED_MASK) == ENABLED

&& mOnKeyListener.onKey(this, event.getKeyCode(), event)) {

return true;

}

return event.dispatch(this);

}

View.java detects if any key listener is registered for this view. If so, the key event will be handled by the key listener. Otherwise, it calls OnKeyDown/OnKeyUp as usual.

All the key listener implementations are under frameworks/base/core/java/android/text/method folder.

MultiTapKeyListener.java - if the keypad is NUMERIC keypad, this listener is used to transform digit inputs to characters.

QwertyKeyListener.java – if the keypad is QWERTY keypad, this listener is used.

原文地址 http://letsgoustc.spaces.live.com/blog/cns!89AD27DFB5E249BA!488.entry?_c=BlogPart

2009-04-05

X窗口系统原理粗解

原文地址:http://kongjian.baidu.com/wienerlee/blog/item/f49f570208d2e80a4bfb5196.html

图形界面一直是Linux用户头疼的问题,出了问题往往一片茫然,大多重装了事。本文试图从宏观的角度剖析一下X系统的简要原理,只有理解了原理才能在解决问题时有的放矢。好,废话少说,现在就开始。
首先,我们要搞清楚X系统的构成。我们平时称呼的X图形界面,实际上是由几个重要部分构成的。它们分别是:
X Server (Xorg)
Display Manager (gdm, kdm, xdm)
Window Manager (metacity, fluxbox, E17, fvwm etc.)
X Client (其他各种图形应用程序,如natulis文件管理器)
以及几个重要概念:
X session: X server启动到X server关闭之间的时间段
X库: GUI程序使用的库函数,如GTK+和QT
下面我们就这几个重要构成的作用、相互关系进行展开,向你展示X的运行原理。

1,X Server
X Server,译作X服务器,是X的核心。X Server是用来驱动硬件,提供基本的图形显示能力的。合理但并不十分科学地,你可以把它看作视频卡的驱动程序。只有运行了X Server的Linux系统,X Client才能利用X Server绘出相应的图像。
X Server早期使用的是XFree86,自4.4版本后,XFree86改用GPL发布,此后开始称为Xorg。X Server部分,主要关注两点。
(1) xorg.conf
xorg.conf是X Server的主要配置文件,它包含一个当前系统的硬件资源列表。X Server就是根据这些硬件资源“组织”出基本的图形能力。xorg.conf文件在/etc/X11/xorg.conf,主要包含几个字段:
Files: X系统使用的字体存放目录(字体的具体使用由FontConfig工具主持)
InputDevice: 输入设备,如键盘鼠标的信息
Monitor: 显示器的设置,如分辨率,刷新率等
Device: 显示卡信息
Screen: 由Monitor和Device组装成一个Screen,表示由它们向这个Screen提供输出能力
ServerLayout: 将一个Screen和InputDevice组装成一个ServerLayout
在具有多个显示设备的系统中,可能有多个Screen和多个ServerLayout,用以实现不同的硬件搭配。
在最近的xorg版本中,X Server已经开始自动侦测硬件,现在的xorg.conf已经都成了默认名称。具体细节还待查,但基本原理还是不变的。
(2) X session(X会话)
前面已经说过,X session是指X server启动后直到X server关闭之间的这段时间。这期间一切跟X相关的动作都属于X session的内容。管理X session的程序称为Display Manager,常听说的gdm或kdm就是gnome/kde所分别对应的Display Manager。
开启一个X session,也就是开始了图形界面的使用。在开启的过程中,Display Manager会对用户进行认证(也就是用户名密码的输入),运行事先设置好的程序(比如scim输入法就是这个时候启动的)等等。
这个开启过程要执行的一系列操作都可以在/etc/X11/Xseesion以及/etc/X11/Xsession.d/目录下看到,其他还有一些配置文件如Xsession.options, Xresource等,都是执行的X session的初始化过程。仔细阅读这些脚本或配置文件,可以帮助你更好地理解X。

2, Display Manager
上面说过,Display Manager(后简称DM)是管理X session的程序,常见的有gdm, kdm, xdm等。对于默认进入X界面的Linux系统,必须将DM程序在开机时执行,即:/etc/rc2.d/S13gdm。下面我们从手工启动X的过程,看一下DM为我们做了哪些工作。
如果没有设置DM在开机时运行的话,手动启动X使用startx命令。
man startx
可以知道,startx的作用可以看作是Display Manager的一种隐性实现。它使用xinit命令,分别根据/etc/X11/xinit/xinitrc和/etc/X11/xinit/xserverrc中所指定的设置唤起X。
其中,xserverrc执行X server的运行任务;xinitrc则运行Xsession命令。从/etc/X11/Xsession脚本的内容可以看出,它也就是进入/etc /X11/Xsession.d/目录轮询地执行所有脚本。很明显,这些也就是前面所说的Xsession初始化工作。
综合起来说,Display Manager完成三个任务:1, X Server的启动; 2, X session的初始化; 3, X session的管理。

3, Window Manager
X Server提供了基本的图形显示能力。然而具体怎么绘制应用程序的界面,却是要有应用程序自己解决的。而Window Manager(桌面管理器,后简称WM)就是用来提供统一的GUI组件的(窗口、外框、菜单、按钮等)。否则,应用程序们各自为政,既增加了程序开发的负担,不统一的桌面风格对视觉也是不小的挑战。
WM的启动由DM控制,在gdm的登录窗口,我们可以进行选择。常见的WM有:Metacity(Gnome默认的WM), fluxbox, fvwm, E17等。

4, X Clients
最后,就是X Client了。X客户端程序,顾名思义,就是使用X服务的程序。firefox,gedit等等都属于X Client程序。X Client部分值得考虑一下的就是DISPLAY环境变量。它主要用于远程X Client的使用。该变量表示输出目的地的位置,由三个要素组成:
[host]:display[.screen]
host指网络上远程主机的名称,可以是主机名、IP地址等。默认的host是本地系统,你可以在自己系统上echo $DISPLAY看一下。
display和screen分别代表输出画面的编号和屏幕的编号。具体细节由于硬件的缺乏,还有待进一步研究。

5, GNOME与KDE
光讲X却不提GNOME和KDE,对一些新手来说可能会更糊涂。所以简单解释一下,GNOME/KDE都是集成的工作环境,是完整X系统的一个组成部分。换句话说,它们提供的是一整套从DM到WM到一揽子X Client的程序集合。以GNOME为例:DM是gdm,WM是metacity,X Client有gnome-terminal, gedit, rhythmbox等等。
不过要注意到,即使没有这些组件,我们像前辈们一样用startx登录X,用fvwm做窗口管理器,用那些最最原始的应用程序,仍然可以实现一个高效的X工作环境。GNOME/KDE的出现只是让这一切变得整合性更好而已。

2009-03-24

Linux DISPLAY作用

在Linux/Unix类操作系统上, DISPLAY用来设置将图形显示到何处. 直接登陆图形界面或者登陆命令行界面后使用startx启动图形, DISPLAY环境变量将自动设置为:0:0, 此时可以打开终端, 输出图形程序的名称(比如xclock)来启动程序, 图形将显示在本地窗口上, 在终端上输入printenv查看当前环境变量, 输出结果中有如下内容:

DISPLAY=:0.0

使用xdpyinfo可以查看到当前显示的更详细的信息.

DISPLAY 环境变量格式如下hostname: displaynumber.screennumber,我们需要知道,在某些机器上,可能有多个显示设备共享使用同一套输入设备,例如在一台PC上连接 两台CRT显示器,但是它们只共享使用一个键盘和一个鼠标。这一组显示设备就拥有一个共同的displaynumber,而这组显示设备中的每个单独的设 备则拥有自己单独的 screennumber。displaynumber和screennumber都是从零开始的数字。这样,对于我们普通用户来说, displaynumber、screennumber就都是0。 hostname指Xserver所在的主机主机名或者ip地址, 图形将显示在这一机器上, 可以是启动了图形界面的Linux/Unix机器, 也可以是安装了Exceed, X-Deep/32等Windows平台运行的Xserver的Windows机器. 如果Host为空, 则表示Xserver运行于本机, 并且图形程序(Xclient)使用unix socket方式连接到Xserver, 而不是TCP方式. 使用TCP方式连接时, displaynumber为连接的端口减去6000的值, 如果displaynumber为0, 则表示连接到6000端口; 使用unix socket方式连接时则表示连接的unix socket的路径, 如果displaynumber为0, 则表示连接到/tmp/.X11-unix/X0 . screennumber则几乎总是0.

如果使用su username或者su - username切换到别的用户, 并且使用命令

export DISPLAY=:0.0

设置DISPLAY环境变量, 运行图形程序(如xclock)时会收到如下错误:

Xlib: connection to ":0.0" refused by server
Xlib: No protocol specified

Error: Can't open display: :0.0

这是因为Xserver默认情况下不允许别的用户的图形程序的图形显示在当前屏幕上. 如果需要别的用户的图形显示在当前屏幕上, 则应以当前登陆的用户, 也就是切换身份前的用户执行如下命令

xhost +

这个命令将允许别的用户启动的图形程序将图形显示在当前屏幕上.

在2台Linux机器之间, 如果设置服务器端配置文件/etc/ssh/sshd_config中包含

X11Forwarding no

客户端配置文件/etc/ssh/ssh_config包含

ForwardX11 yes

则从客户端ssh到服务器端后会自动设置DISPLAY环境变量, 允许在服务器端执行的图形程序将图形显示在客户端上. 在服务器上查看环境变量显示如下(这个结果不同的时候并不相同)

DISPLAY=localhost:10.0

在客户机上用netstat -lnp可以看到有程序监听了6010端口

tcp 0 0 127.0.0.1:6010 0.0.0.0:* LISTEN 4827/1

如 果希望允许远程机器上的图形程序将图形显示在本地机器的Xserver上, 除了要设置远端机器的DISPLAY环境变量以外, 还需要设置本地机器的Xserver监听相应的TCP端口. 而现在的Linux系统出于安全的考虑, 默认情况下不再监听TCP端口. 可通过修改/etc/X11/xinit/xserverrc文件, 将

exec /usr/bin/X11/X -dpi 100 -nolisten tcp

修改为

exec /usr/bin/X11/X -dpi 100

允许在直接使用startx启动图形时启动对TCP端口的监听.

修改/etc/kde3/kdm/kdmrc, 将

ServerArgsLocal=-nolisten tcp

修改为

ServerArgsLocal=

允许kdm作为显示管理器时, 启动会话时监听相应的TCP端口.

修改/etc/gdm/gdm.conf, 在[Security]一节增加

DisallowTCP=false

或 者在登陆窗口选择"Options" -> "Configure Login Manager..."的Security页面, 取消"Deny TCP connections to Xserver", 允许gdm作为显示管理器时, 启动会话时监听相应的TCP端口.

Reference:
http://ubuntuforums.org/archive/index.php/t-28502.html

2009-03-23

vbox相关问题解决方法

1,键盘无效: 安装 scim-bridge-client-qt ,重新登录.

2,无缝模式错位:vbox里设置成16位色就OK.
无缝模式的时候必须至少保留一个Windows的窗口不被关闭.

3,Windows无法和Linux通过网络文件夹共享
使用net use x: //svrbox/$your_share_dir 的时候提示找不到共享资源名, 解决办法, Windows下再次安装Virtualbox扩展增强工具包 .

4,Virtualbox独占声音,导致启动Vbox后Ubuntu没有声音
解决方法是安装Pulseaudio包,并且设置VBox声音使用Pulseaudio驱动.

5,USB设备有问题:
让GUEST识别USB设备
最简单的办法如下:
$ sudo gedit /etc/fstab

在末尾加上
# the USB group
# 对所有用户开放USB设备的读写权限
none /proc/bus/usb usbfs devmode=666 0 0


然后就可以使用的USB设备了。

但是把USB设备的读写权限开放给所有用户的方式在一些情况下不太安全,更为稳妥的做法是:

增加用户组usbfs
$ sudo groupadd usbfs

查看usbfs用户组的gid
$ cat /etc/group | grep usbfs

usbfs:x:1002:


把当前用户增加到usbfs组
$ sudo gedit /etc/group
把 usbfs:x:1002:
修改为 usbfs:x:1002:joson

为USB设备重新设置权限,编辑/etc/fstab文件,添加下面两行,注意你的gid可能不是1002
$ sudo gedit /etc/fstab
在末尾加上
# 1002 is the USB group IDI
none /proc/bus/usb usbfs devgid=1002,devmode=774 0 0

重新启动后,应该就可以在客户机中使用USB设备了。

方法:插入一个USB设备后,如U盘、鼠标,右键点击虚拟机里右下脚的USB图标,选择已经识别的U盘,就可以正常使用了。
注意: 在客户机里使用USB设备前要先在主机里卸载。

2009-03-17

Map of Linux Kernel



http://www.makelinux.net/kernel_map

Create USB MSDOS bootable disk in Linux

1. Insert USB stick, check dmesg for device, for example /dev/sga
2. Run fdisk and create FAT16 bootable partition
3. Downloa bootable MSDOS floppy image from http://www.bootdisk.com/, save as msdos.img
4. Run qemu -fda msdos.img -hda /dev/sga -boot a
5. When MSDOS booted, in the DOS prompt execute format /s c:
6. The bootable disk is created on USB flash