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的出现只是让这一切变得整合性更好而已。