2009年9月29日 星期二

NFS

http://linux.vbird.org/linux_server/0330nfs.php

Android Building System 分析

Android Building System 分析
http://blog.chinaunix.net/u2/85805/showart_1944878.html

2009年9月14日 星期一

《看日记学git》之七

http://roclinux.cn/?p=241

2009年9月10日 星期四

手動mount一個 LVM的Patition

第一件事就是載入LVM所需要的module
$ modprobe dm-mod
接著就是找出partiton中的VG(Volume Group),以我的狀況為例,我將硬碟以usb連接到電腦後,由dmesg可以知道有 /dev/sda1, /dev/sda2兩個patitons,在以 fdisk -l /dev/sda 可以知道/dev/sda2是LVM格式。接著,我就執行:
$ pvscan
PV /dev/sda2 VG VolGroup00 lvm2 [18.50 GB / 32.00 MB free]
Total: 1 [18.50 GB] / in use: 1 [18.50 GB] / in no VG: 0 [0 ]
從以上結果,我可以知道我有一個VG,叫做VolGroup00,他的PV(Physical Volume)是 /dev/sda2
接著我就執行:
$ vgchange -ay VolGroup00
$ ls /dev/VolGroup00/
LogVol00 LogVol01
這時候vgchange會在/dev下產生一個VolGroup00的目錄,並且在這個目錄下產生該有的device(其實是link到 /dev/mapper下)。然後我利用 ls就可以知道VolGroup00這個VG下有兩個LV(Logical Volume)。分別是LogVol00 與 LogVol01。然後我只要mount我要的就可以了。
$ mkdir /mnt/usb
$ mount /dev/VolGroup00/LogVol00 /mnt/usb/

參考資料:
1. Logical Volume Manager HowTo
2. Fedora Forum
以上內容轉載自 Gator's Blog
引用通告地址: 點擊獲取引用地址
標籤: mount LVM

2009年9月7日 星期一

Android init 启动过程分析

Android init 启动过程分析
分析android的启动过程,从内核之上,我们首先应该从文件系统的init开始,因为 init 是内核进入文件系统后第一个运行的程序,通常我们可以在linux的命令行中指定内核第一个调用谁,如果没指定那么内核将会到/sbin/, /bin/ 等目录下查找默认的init,如果没有找到那么就报告出错。
下面是曾经用过的几种开发板的命令行参数:
S3C2410 启动参数:
noinitrd root=/dev/nfs nfsroot=192.168.2.56:/nfsroot/rootfs ip=192.168.2.188:192.168.2.56:192.168.2.56:255.255.255.0::eth0:on console=ttySAC0
S3C2440 启动参数:
setenv bootargs console=ttySAC0 root=/dev/nfs nfsroot=192.168.2.56:/nfsroot/rootfs ip=192.168.2.175:192.168.2.56:192.168.2.201:255.255.255.0::eth0:on mem=64M init=/init
marvell 310 启动参数:
boot root=/dev/nfs nfsroot=192.168.2.56:/nfsroot/rootfs,rsize=1024,wsize=1024 ip=192.168.2.176:192.168.2.201:192.168.2.201:255.255.255.0::eth0:-On console=ttyS2,115200 mem=64M init=/init

init的源代码在文件:./system/core/init/init.c 中,init会一步步完成下面的任务:
1.初始化log系统

2.解析/init.rc和/init.%hardware%.rc文件

3. 执行 early-init action in the two files parsed in step 2.

4. 设备初始化,例如:在 /dev 下面创建所有设备节点,下载 firmwares.

5. 初始化属性服务器,Actually the property system is working as a share memory. Logically it looks like a registry under Windows system.

6. 执行 init action in the two files parsed in step 2.

7. 开启 属性服务。

8. 执行 early-boot and boot actions in the two files parsed in step 2.

9. 执行 Execute property action in the two files parsed in step 2.

10. 进入一个无限循环 to wait for device/property set/child process exit events.例如, 如果SD卡被插入,init会收到一个设备插入事件,它会为这个设备创建节点。系统中比较重要的进程都是由init来fork的,所以如果他们他谁崩溃了,那么init 将会收到一个 SIGCHLD 信号,把这个信号转化为子进程退出事件, 所以在loop中,init 会操作进程退出事件并且执行 *.rc 文件中定义的命令。
例如,在init.rc中,因为有:
service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
socket zygote stream 666
onrestart write /sys/android_power/request_state wake
onrestart write /sys/power/state on
所以,如果zygote因为启动某些服务导致异常退出后,init将会重新去启动它。

int main(int argc, char **argv)
{
...
//需要在后面的程序中看打印信息的话,需要屏蔽open_devnull_stdio()函数
open_devnull_stdio();
...
//初始化log系统
log_init();
//解析/init.rc和/init.%hardware%.rc文件
parse_config_file("/init.rc");
...
snprintf(tmp, sizeof(tmp), "/init.%s.rc", hardware);
parse_config_file(tmp);
...
//执行 early-init action in the two files parsed in step 2.
action_for_each_trigger("early-init", action_add_queue_tail);
drain_action_queue();
...
/* execute all the boot actions to get us started */
/* 执行 init action in the two files parsed in step 2 */
action_for_each_trigger("init", action_add_queue_tail);
drain_action_queue();
...
/* 执行 early-boot and boot actions in the two files parsed in step 2 */
action_for_each_trigger("early-boot", action_add_queue_tail);
action_for_each_trigger("boot", action_add_queue_tail);
drain_action_queue();

/* run all property triggers based on current state of the properties */
queue_all_property_triggers();
drain_action_queue();

/* enable property triggers */
property_triggers_enabled = 1;
...
for(;;) {
int nr, timeout = -1;
...
drain_action_queue();
restart_processes();

if (process_needs_restart) {
timeout = (process_needs_restart - gettime()) * 1000;
if (timeout < 0)
timeout = 0;
}
...
nr = poll(ufds, 3, timeout);
if (nr <= 0)
continue;

if (ufds[2].revents == POLLIN) {
/* we got a SIGCHLD - reap and restart as needed */
read(signal_recv_fd, tmp, sizeof(tmp));
while (!wait_for_one_process(0))
;
continue;
}

if (ufds[0].revents == POLLIN)
handle_device_fd(device_fd);

if (ufds[1].revents == POLLIN)
{
handle_property_set_fd(property_set_fd);
}
}

return 0;
}


2.解析init.rc脚本
init.rc 脚本的具体语法可以参考下面文档
http://www.kandroid.org/android_pdk/bring_up.html
名词解释:
Android初始化語言由四大类声明组成:行为类(Actions),命令类(Commands),服务类(Services),选项类(Options).
初始化语言以行为单位,由以空格间隔的语言符号組成。C风格的反斜杠转义符可以用来插入空白到语言符号。双引号也可以用来防止文本被空格分成多个语言符号。当反斜杠在行末时,作为换行符。
* 以#开始(前面允许空格)的行为注释。
* Actions和Services隐含声明一个新的段落。所有该段落下Commands或Options的声明属于该段落。第一段落前的Commands或Options被忽略。
* Actions和Services拥有唯一的命名。在他们之后声明相同命名的类将被当作错误并忽略。
Actions是一系列命令的命名。Actions拥有一个触发器(trigger)用来決定action何時执行。当一个action在符合触发条件被执行时,如果它还没被加入到待执行队列中的话,則加入到队列最后。
队列中的action依次执行,action中的命令也依次执行。Init在执行命令的中间处理其他活动(设备创建/销毁,property 设置,进程重启)。

Actions的表现形式:
on



重要的数据结构
两个列表,一个队列。
static list_declare(service_list);
static list_declare(action_list);
static list_declare(action_queue);
*.rc 脚本中所有 service关键字定义的服务将会添加到 service_list 列表中。
*.rc 脚本中所有 on 关键开头的项将会被会添加到 action_list 列表中。
每个action列表项都有一个列表,此列表用来保存该段落下的 Commands

脚本解析过程:
parse_config_file("/init.rc")
int parse_config_file(const char *fn)
{
char *data;
data = read_file(fn, 0);
if (!data) return -1;

parse_config(fn, data);
DUMP();
return 0;
}
static void parse_config(const char *fn, char *s)
{
...
case T_NEWLINE:
if (nargs) {
int kw = lookup_keyword(args[0]);
if (kw_is(kw, SECTION)) {
state.parse_line(&state, 0, 0);
parse_new_section(&state, kw, nargs, args);
} else {
state.parse_line(&state, nargs, args);
}
nargs = 0;
}
...
}

parse_config会逐行对脚本进行解析,如果关键字类型为 SECTION ,那么将会执行 parse_new_section()
类型为 SECTION 的关键字有: on 和 sevice
关键字类型定义在 Parser.c (system\core\init) 文件中
Parser.c (system\core\init)
#define SECTION 0x01
#define COMMAND 0x02
#define OPTION 0x04
关键字 属性
capability, OPTION, 0, 0)
class, OPTION, 0, 0)
class_start, COMMAND, 1, do_class_start)
class_stop, COMMAND, 1, do_class_stop)
console, OPTION, 0, 0)
critical, OPTION, 0, 0)
disabled, OPTION, 0, 0)
domainname, COMMAND, 1, do_domainname)
exec, COMMAND, 1, do_exec)
export, COMMAND, 2, do_export)
group, OPTION, 0, 0)
hostname, COMMAND, 1, do_hostname)
ifup, COMMAND, 1, do_ifup)
insmod, COMMAND, 1, do_insmod)
import, COMMAND, 1, do_import)
keycodes, OPTION, 0, 0)
mkdir, COMMAND, 1, do_mkdir)
mount, COMMAND, 3, do_mount)
on, SECTION, 0, 0)
oneshot, OPTION, 0, 0)
onrestart, OPTION, 0, 0)
restart, COMMAND, 1, do_restart)
service, SECTION, 0, 0)
setenv, OPTION, 2, 0)
setkey, COMMAND, 0, do_setkey)
setprop, COMMAND, 2, do_setprop)
setrlimit, COMMAND, 3, do_setrlimit)
socket, OPTION, 0, 0)
start, COMMAND, 1, do_start)
stop, COMMAND, 1, do_stop)
trigger, COMMAND, 1, do_trigger)
symlink, COMMAND, 1, do_symlink)
sysclktz, COMMAND, 1, do_sysclktz)
user, OPTION, 0, 0)
write, COMMAND, 2, do_write)
chown, COMMAND, 2, do_chown)
chmod, COMMAND, 2, do_chmod)
loglevel, COMMAND, 1, do_loglevel)
device, COMMAND, 4, do_device)

parse_new_section()中再分别对 service 或者 on 关键字开头的内容进行解析。
...
case K_service:
state->context = parse_service(state, nargs, args);
if (state->context) {
state->parse_line = parse_line_service;
return;
}
break;
case K_on:
state->context = parse_action(state, nargs, args);
if (state->context) {
state->parse_line = parse_line_action;
return;
}
break;
}
...

对 on 关键字开头的内容进行解析
static void *parse_action(struct parse_state *state, int nargs, char **args)
{
...
act = calloc(1, sizeof(*act));
act->name = args[1];
list_init(&act->commands);
list_add_tail(&action_list, &act->alist);
...
}

对 service 关键字开头的内容进行解析
static void *parse_service(struct parse_state *state, int nargs, char **args)
{
struct service *svc;
if (nargs < 3) {
parse_error(state, "services must have a name and a program\n");
return 0;
}
if (!valid_name(args[1])) {
parse_error(state, "invalid service name '%s'\n", args[1]);
return 0;
}
//如果服务已经存在service_list列表中将会被忽略
svc = service_find_by_name(args[1]);
if (svc) {
parse_error(state, "ignored duplicate definition of service '%s'\n", args[1]);
return 0;
}

nargs -= 2;
svc = calloc(1, sizeof(*svc) + sizeof(char*) * nargs);
if (!svc) {
parse_error(state, "out of memory\n");
return 0;
}
svc->name = args[1];
svc->classname = "default";
memcpy(svc->args, args + 2, sizeof(char*) * nargs);
svc->args[nargs] = 0;
svc->nargs = nargs;
svc->onrestart.name = "onrestart";
list_init(&svc->onrestart.commands);
//添加该服务到 service_list 列表
list_add_tail(&service_list, &svc->slist);
return svc;
}
服务的表现形式:
service [ ]*

2009年8月17日 星期一

Android 技术专题系列之七 -- 输入法 框架

Android 技术专题系列之七 -- 输入法 框架(2009-03-02 14:25:25)
标签:android 中文 输入法 结构图 架构图 解析 手机操作系统 智能手机 it 分类:android
Android的输入法框架比价复杂。从进程的角度来讲,相关功能主要分布在下面三个位置:


客户端应用是一个包含有图形界面的应用,如地址本。图形界面上包含有能够接收输入的编辑框,如TextView。
输入法模块提供软键盘,将用户在软键盘上的按键输入根据某种算法(如Zi, T9, 国笔等)转换成单词,然后传递给客户端应用。目录development/samples/SoftKeyboard下提供了一个输入法模块实例。如果想要实现一个中文输入法,可参考这个实例。
平台部分实现一些管理功能,负责装载某个输入法模块,启动,终止该模块等。
相关代码主要位于下面几个位置。其中,位于3,5,6,7目录下的代码最值得关注。
1. frameworks/base/core/java/com/android/internal/view
这个目录下定义了几个重要的idl 接口。
IInputMethod.aidl 定义了IInputMethod idl 接口,用于客户端跨进程操作InputMethod接口。
IInputMethodSession.aidl 定义了IInputMethodSession接口,是IInputMethod的辅助接口。用于客户端跨进程操作InputMethodSession接口。

IInputMethodCallback.aidl定义了一个helper 接口,由客户端实现。IInputMethod.aidl和IInputMethodSession.aidl实例可以分别调用该接口中的不同方法

IInputMethodManager.aidl 定义了Input Method Manager的service接口。客户端通过InputMethodManager interface调用这个service。
InputMethodManagerService.java实现了IInputMethodManager.aidl接口

IInputMethodClient.aidl定义接口,标识一个Input Method Manager 的客户。这个service在客户端实现,提供给server端调用。

IInputContext.aidl定义了一个接口,由客户端提供InputMethod使用。InputMethod可以与客户端交互,调用客户端提供的callback。
IInputConnectionWrapper.java 实现了IInputContext接口。
IInputContextCallback.aidl定义了一个接口,定义了一组callback函数给IInputContext.aidl实例调用,从客户端返回信息给InputMethod。
InputConnectionWrapper.java实现了IInputContextCallback接口。

2. frameworks/base/services/java/com/android/server
InputMethodManagerService.java实现了IInputMethodManager.aidl接口

3. frameworks/base/core/java/android/view/inputmethod
这个目录下定义了几个重要的interface和类。
InputMethodManager.java实现了InputMethodManager 类。此类调用IInputMethodManager.aidl接口功能,而IInputMethodManager.aidl接口功能由InputMethodManagerService.java实现,并运行在不同于客户端进程的server进程中。

InputConnection.java定义了InputConnection interface。InputConnection 接口在输入法和客户端之间建立了一个连接,输入法可以使用该连接获取或发送信息给客户端。InputConnection实例由客户端创建之后传递给输入法使用。BaseInputConnection.java 实现了InputConnection接口的一个基类: BaseInputConnection。 EditableInputConnection.java实现了一个派生类

InputBinding.java 定义了类InputBinding,这个类实现了parcelable 接口。这个类的成员变量包含了客户端传向server的信息。

InputMethod.java定义了InputMethod interface。文件InputMethodService.java中类InputMethodImpl实现了这个接口。这个接口定义了一套操纵一个输入法的方法。如,createSession,startInput等。要编写一个具体输入法的话,就需要派生这个接口。

InputMethodSession.java定义了InputMethodSession接口。文件InputMethodService.java中类InputMethodSessionImpl实现了这个接口。InputMethodSession是InputMethod的辅助接口,用于具体和某个输入法客户端交互。

CompletionInfo.java 类描述一个text completion.
EditorInfo.java类描述一个接收输入的view的属性,如内容属性(text, digit, etc)。
ExtractedText.java类描述从view中提取的传递给输入法的文本属性。


4. frameworks/base/core/java/com/android/internal/widget
EditableInputConnection.java实现了BaseInputConnection的一个派生类。

5. frameworks/base/core/java/android/inputmethodservice
这个目录下的代码提供了实现一个具体输入法的框架类。从这些类派生,就可以定制一个输入法。
SoftInputWindow.java中的SoftInputWindow类是一个Dialog子类。它代表一个输入法的顶级窗口(由窗口管理器管理),这个窗口由上到下,包含extractArea, candidatesArea, 和 inputArea。

Keyboard.java 中的Keyboard类装载并解析一个描述虚拟键盘(Soft Keyboard)的xml文件(如development/samples/SoftKeyboard/res/xml),并存储该键盘的属性,如该虚拟键盘包含多上行,每行有哪些键等。
KeyboardView.java 中的KeyboardView类是一个View子类。它根据Keyboard数据结构真正的在screen上画出一个虚拟键盘。这个虚拟键盘就是SoftInputWindow中的inputArea。

AbstractInputMethodService是Service的派生类,并实现了KeyEvent.Callback 接口。实现了InputMethod 和 InputMethodSession的基类。dispatchKeyEvent 函数将收到的key event传给相应的key 处理函数(在派生类中实现)。当这个service被客户端绑定时,其onBind()函数给客户端返回了一个IInputMethodWrapper实例,这个实例实现了IInputMethod idl接口。客户端可以使用该接口的相关功能。

IInputMethodWrapper.java 实现了IInputMethod idl 接口。这个类收到客户端的跨进程命令后,调用InputMethod完成相应功能。
IInputMethodSessionWrapper.java 实现了IInputMethodSession idl接口。这个类收到客户端的跨进程命令后,调用InputMethodSession完成相应功能。


6. frameworks/base/core/res/res/layout
这个目录下存放着一些系统资源。其中,
input_method.xml描述了一个输入法的窗口(即SoftInputWindow)布局,从上往下,依次排列extractArea, candidatesArea 和 inputArea。
input_method_extract_view.xml。

7. development/samples/SoftKeyboard
这个目录下代码实现了一个的输入法实例--软键盘英文/数字输入法。这里面实现的类大都是从frameworks/base/core/java/android/inputmethodservice 中的类派生而来。
AndroidManifest.xml:描述这个.apk提供的service以及关于这个输入法的一些信息。
res/xml/目录下存储着几个描述不同虚拟键盘的xml文件。
LatinKeyboard.java中的LatinKeyboard类是Keyboard的子类。
LatinKeyboardView.java中的LatinKeyboardView类是KeyboardView的子类。

8. frameworks/base/core/java/android/widget
在这里TextView.java是使用Input Method Framework (IMF)的客户端。TextView创建了一个InputMethodManager的实例并调用其restartInput 函数。
InputMethodManager::restartInput函数创建了一个InputConnection 实例并调用IInputMethodManager::startInput。
IInputMethodManager::startInput 函数使用mContext.bindService启动一个InputMethod service, 如 Sample Soft Keyboard。

9. frameworks/base/core/java/com/android/internal/widget

Android 技术专题系列之十一 -- DRM(2009-04-26 13:10:45)

Android 技术专题系列之十一 -- DRM(2009-04-26 13:10:45)
标签:drm android it 分类:android
简而言之,DRM系统提供一套机制对用户使用手机上的媒体内容(如ringtong, mp3等)进行限制,如限制拷贝给第三方,限制使用次数或时限等,从而保护内容提供商的权利。建议读者可以阅读OMA DRM 的规范,以便更好的了解代码。

相关代码主要位于下列目录:
frameworks/base/media/java/android/drm/mobile1
frameworks/base/media/libdrm/moblile1
frameworks/base/media/libdrm/mobile2
packages/apps/Mms/src/com/android/mms/drm: drm
packages/providers/DrmProvider/src/com/android/providers/drm

下面先作简要分析:
frameworks/base/media/libdrm/moblile1应该是提供OMA DMA1.0的本地/c++ 实现
frameworks/base/media/libdrm/moblile2应该是提供OMA DAM2.0的本地/c++ 实现。

frameworks/base/media/java/android/drm/mobile1 对应用提供OMA DRM 1.0 (即frameworks/base/media/libdrm/moblile1)的java接口。目前尚没有OMA DRM 2.0的java接口,也就意味着OMA DRM 2.0在Android中尚未应用。

packages/apps/Mms/src/com/android/mms/drm 实现在MMS中如何使用DRM。
packages/providers/DrmProvider/src/com/android/providers/drm ?

下面再作具体介绍:
一 目录 frameworks/base/media/java/android/drm/mobile1
这个目录是OMA DRM 1.0的java 接口。
OMA DRM 1.0 定义了几个重要概念:
DRM消息(DRM message):用户下载的一条DRM内容。根据下面介绍的DRM内容 传输方式的不同,DRM消息中包含的内容亦有所不同,可能只包含一个未经加密的媒体对象;也可能即包含未加密的媒体对象,也包含一个权利对象;也可能只包含一个经过加密的媒体对象(.dcf)。
媒体对象 (media object):包含媒体资源,如一个mp3, ringtone等。
权利对象(Right object): 限制用户如何使用媒体对象。


OMA DRM 1.0 定义了DRM内容的四种传输方式:
1. Combined delivery: 这种传输方式中,媒体对象以未经加密的方式(plain) 与Right object打成一个包,一块传输。这个包下载到手机设备上后,不允许转发给其他设备。
2. Forward lock: 这种传输方式是combined delivery的一个特例。媒体对象没有对应的Rigth object。媒体对象不允许转发给其他设备
3. Separate delivery: 媒体对象和权利对象分别传输。媒体对象采用对称密钥加密,文件以.dcf为后缀。权利对象中则包含对应的密钥。
4. Superdistribution:类似separate delivery, 但媒体内容允许转发给其他设备。

相应地,本目录的代码中:

DrmConstraintInfo对象描述了(对媒体内容的)一组限制属性,如开始/结束使用日期,使用次数等;

DrmRights对象代表了一个OMA 权利对象。

DrmRightsManager管理设备上的Rigth Objects。所有下载的权利对象,不论是由separate delivery 还是 combined delivery,权利对象都要首先安装到设备上。

DrmRawContent代表一条DRM内容,分为两类:DRM_MIMETYPE_MESSAGE_STRING --DRM内容是经combined delivery 或 forward lock传输的 (也即媒体对象没有加密,没有或者有一个权利对象); DRM_MIMETYPE_CONTENT_STRING -- DRM内容是经 separate delivery传输的(也即媒体对象是经过加密的dcf格式,不包含权利对象)。

DrmInputStream:这个对象从一个DrmRawContent对象中读出经过权利对象验证并解密后的(需要的话)媒体对象内容。这个内容之后就可以传给相关应用输出给用户。


二 目录frameworks/base/media/libdrm/moblile1
本目录真正实现DRM DRM 1.0。
drm1_jni.c提供对DrmRawContent中方法的本地实现;
objmng目录下是具体实现。frameworks/base/media/libdrm/mobile1/include/objmng/svc_drm.h中有关于每个方法的详细解释,基本思路是每打开一个Drm 内容,就创建一个session,然后提供一系列方法,以此session为参数,对Drm进行各种操作,如获取属性,读取解密后内容等。这里不再赘述。