博客被墙一年,借助goagent正式回归

GoAgent是个好东西,借助GoAgent我回到了离别一年多的blog,让我重新回到了web2.0的大家庭。这一年发生了很多事情,工作忙房价高博客被墙,心生倦怠,没有心思去更新博客,去记录自己的成长。最近一位视翻墙如生命的朋友强烈推荐我用GoAgent,经过繁琐的配置,最终结果让人觉得非常满意,一改以往翻墙技术的龟速与种种限制,终于可以让自己透透气,看一看外面多彩的世界。

主要参考:

http://blog.csdn.net/tender001/article/details/7694913

http://www.x-berry.com/goagent

Linux bootup procedure

this is the standard process for launching a Linux system in text mode (runlevel 3, nongraphical):
* The computer’s BIOS loads LILO from the boot sector or master boot record of the hard disk.
* LILO loads the Linux kernel.
* The kernel starts the init program.
* If the kernel was started in single-user mode, the init program starts the sulogin program.
* In standard modes (nonsingle user), the init program starts a getty program on each terminal (tty), which waits for someone to log in. (You’ll see getty programs called mgetty, mingetty, agetty, etc., all with slightly different features, but all doing the same task: waiting for someone to log in.)
* getty collects a user name from the person logging in and launches /bin/login.
* The login command processes the password entry and launches a shell.

优先级翻转 priority inversion [转]

1. 优先级反转(Priority Inversion)
由于多进程共享资源,具有最高优先权的进程被低优先级进程阻塞,反而使具有中优先级的进程先于高优先级的进程执行,导致系统的崩溃。这就是所谓的优先级反转(Priority Inversion)。

2. 产生原因
其实,优先级反转是在高优级(假设为A)的任务要访问一个被低优先级任务(假设为C)占有的资源时,被阻塞.而此时又有优先级高于占有资源的任务(C)而低于被阻塞的任务(A)的优先级的任务(假设为B)时,于是,占有资源的任务就被挂起(占有的资源仍为它占有),因为占有资源的任务优先级很低,所以,它可能一直被另外的任务挂起.而它占有的资源也就一直不能释放,这样,引起任务A一直没办法执行.而比它优先低的任务却可以执行.

所以,一个解决办法就是提高占有资源任务的优先级,让它正常执行,然后释放资源,以让任务A能正常获取资源而得以执行.

3. 解决方案 ( 优先级继承 / 优先级天花板 )

目前解决优先级反转有许多种方法。其中普遍使用的有2种方法:一种被称作优先级继承(priority inheritance);另一种被称作优先级极限(priority ceilings)。

A. 优先级继承(priority inheritance)
优先级继承是指将低优先级任务的优先级提升到等待它所占有的资源的最高优先级任务的优先级.当高优先级任务由于等待资源而被阻塞时,此时资源的拥有者的优先级将会自动被提升.

B. 优先级天花板(priority ceilings)
优先级天花板是指将申请某资源的任务的优先级提升到可能访问该资源的所有任务中最高优先级任务的优先级.(这个优先级称为该资源的优先级天花板)

A 和B的区别:

优先级继承,只有当占有资源的低优先级的任务被阻塞时,才会提高占有资源任务的优先级,而优先级天花板,不论是否发生阻塞,都提升.

android系统编译

android系统要求使用64位Linux系统进行编译,而android系统在编译的过程中却依赖于各中32位的库文件,所以在使用64位系统编译的过程中会经常提示类似/usr/bin/ld: cannot find -lz的错误,通常在这种情况下只需要安装指定的以lib32开的对应的库文件就可以了。android系统默认要求是用64位系统进行编译,但我们只要稍微修改一下android系统的makefile就可以使用32位系统进行编译,下面分别介绍这两种系统的编译方法。

64位系统下的编译

执行如下命令开始系统的编译:

make -j4

-j用于指定编译的线程数,适当的线程数量可以加快系统的编译,通常线程数量为CPU核心数目的两倍,笔者使用的是AMD64位双核处理器,所以这里指定的线程数为4。

采用默认编译方法编译出来的系统用于android模拟器。

32位系统下的编译

如果需要在32位系统中编译android系统,在编译前需要对部分makefile进行修改,笔者使用patch的格式列出需要修改的部分。

首先修改build/core/main.mk,修改的内容如下所示:

diff --git a/core/main.mk b/core/main.mk
index 3877bb2..a6b5003 100644
--- a/core/main.mk
+++ b/core/main.mk
@@ -72,7 +72,7 @@ $(info Checking build tools versions...)

 ifeq ($(BUILD_OS),linux)
 build_arch := $(shell uname -m)
-ifneq (64,$(findstring 64,$(build_arch)))
+ifneq (i686,$(findstring i686,$(build_arch)))
 $(warning ************************************************************)
 $(warning You are attempting to build on a 32-bit system.)
 $(warning Only 64-bit build environments are supported beyond froyo/2.2.)

其次修改如下四个文件:

external/clearsilver/cgi/Android.mk
external/clearsilver/java-jni/Android.mk
external/clearsilver/util/Android.mk
external/clearsilver/cs/Android.mk

这四个文件的修改结果如下所示:

diff --git a/cgi/Android.mk b/cgi/Android.mk
index 071c9c4..3132563 100644
--- a/cgi/Android.mk
+++ b/cgi/Android.mk
@@ -13,8 +13,8 @@ LOCAL_C_INCLUDES := $(LOCAL_PATH)/..
 LOCAL_CFLAGS := -fPIC

 # This forces a 64-bit build for Java6
-LOCAL_CFLAGS += -m64
-LOCAL_LDFLAGS += -m64
+LOCAL_CFLAGS += -m32
+LOCAL_LDFLAGS += -m32
 # We use the host compilers because the Linux SDK build
 # uses a 32-bit toolchain that can't handle -m64
 LOCAL_CC := $(CC)
diff --git a/cs/Android.mk b/cs/Android.mk
index 51f2bb1..0f5f627 100644
--- a/cs/Android.mk
+++ b/cs/Android.mk
@@ -9,8 +9,8 @@ LOCAL_C_INCLUDES := $(LOCAL_PATH)/..
 LOCAL_CFLAGS := -fPIC

 # This forces a 64-bit build for Java6
-LOCAL_CFLAGS += -m64
-LOCAL_LDFLAGS += -m64
+LOCAL_CFLAGS += -m32
+LOCAL_LDFLAGS += -m32
 # We use the host compilers because the Linux SDK build
 # uses a 32-bit toolchain that can't handle -m64
 LOCAL_CC := $(CC)
diff --git a/java-jni/Android.mk b/java-jni/Android.mk
index 64c3c0f..f17fd15 100644
--- a/java-jni/Android.mk
+++ b/java-jni/Android.mk
@@ -34,8 +34,8 @@ LOCAL_C_INCLUDES := \
 LOCAL_CFLAGS += -fPIC

 # This forces a 64-bit build for Java6
-LOCAL_CFLAGS += -m64
-LOCAL_LDFLAGS += -m64
+LOCAL_CFLAGS += -m32
+LOCAL_LDFLAGS += -m32
 # We use the host compilers because the Linux SDK build
 # uses a 32-bit toolchain that can't handle -m64
 LOCAL_CC := $(CC)
diff --git a/util/Android.mk b/util/Android.mk
index 93f24c9..c7f070c 100644
--- a/util/Android.mk
+++ b/util/Android.mk
@@ -18,8 +18,8 @@ LOCAL_C_INCLUDES := $(LOCAL_PATH)/..
 LOCAL_CFLAGS := -fPIC

 # This forces a 64-bit build for Java6
-LOCAL_CFLAGS += -m64
-LOCAL_LDFLAGS += -m64
+LOCAL_CFLAGS += -m32
+LOCAL_LDFLAGS += -m32
 # We use the host compilers because the Linux SDK build
 # uses a 32-bit toolchain that can't handle -m64
 LOCAL_CC := $(CC)

也就是将LOCAL_CFLAGS和LOCAL_LDFLAGS由-m64改为-m32,从而指定使用32位系统进行编译。

修改完成后使用如下命令开始系统的编译:

make -j4

编译结果与64位系统的编译结果相同。

android源代码下载

首先创建编译目录

mkdir -p android/{bin,mydroid}

android系统由多个不同的项目组成,包括kernel、Dalvik、Bionic、prebuilt、build等,这些项目使用git单独管理,为了管理这些单独的git项目,android项目中创建了一个叫repo的python脚本用于管理这些项目,所以我们首先需要下载repo脚本,然后使用repo脚本来获取android系统的源代码。

我们使用如下命令将repo脚本下载到bin目录并设置可执行权限,然后将repo的路径加入到可执行文件的搜索目录:

cd android/bin
wget http://android.git.kernel.org/repo
chmod a+x repo
export PATH=$(pwd):$PATH

此时,新建的目录有如下结构:

$ tree android/
android/
|-- bin
|   `-- repo
`-- mydroid

2 directories, 1 file

进入mydroid目录,初始化android源码库,然后将源码同步到mydroid目录中:

cd mydroid
repo init -u git://android.git.kernel.org/platform/manifest.git
repo sync

如果在同步过程中网络中断,只需在网络恢复正常后再次执行repo sync命令,该命令将比较已下载的项目是否最新,如果不是则更新,如果项目尚未下载,则接着下载。

android系统源代码库相当庞大,需要下载大约5.3GB的内容,所以在下载前请先预留好足够的空间。如果需要编译,则需要为编译分配额外的空间,建议预留不少于10GB的硬盘空间。

交叉编译mtd-utils

mtd-utils依赖于zlib、lzo和uuid,所一在编译mtd-utils之前需要先编译好这三个库。本文假定要安装的根文件系统为/mnt/rootfs

zlib的编译与安装

下载地址:

http://zlib.net/zlib-1.2.5.tar.gz

编译流程:

tar xfz zlib-1.2.5.tar.gz
cd zlib-1.2.5
CC=arm-none-linux-gnueabi-gcc ./configure --prefix=/usr --shared
make
make DESTDIR=/mnt/roofs install

lzo的编译与安装

下载地址:

http://www.oberhumer.com/opensource/lzo/download/lzo-2.04.tar.gz

编译流程:

tar xvfz lzo-2.04.tar.gz
cd lzo-2.04
CC=arm-none-linux-gnueabi-gcc ./configure --prefix=/usr \
--build=$MACHTYPE --host=arm-linux --enable-shared
make
make DESTDIR=/mnt/rootfs install

uuid的编译与安装

uuid位于e2fsprogs软件包中

下载地址:

http://prdownloads.sourceforge.net/e2fsprogs/e2fsprogs-1.41.14.tar.gz

编译流程:

tar xvfz e2fsprogs-1.41.14.tar.gz
cd e2fsprogs-1.41.14
CC=arm-none-linux-gnueabi-gcc ./configure --prefix=/mnt/rootfs/usr \
--build=$MACHTYPE --host=arm-linux --enable-elf-shlibs
cd lib/uuid
make
make install

mtd-utils的编译与安装

下载地址:

ftp://ftp.infradead.org/pub/mtd-utils/mtd-utils-1.4.2.tar.bz2

编译流程:

tar xvfz mtd-utils-1.4.2.tar.bz2
cd mtd-utils-1.4.2
export CPPFLAGS=-I/mnt/rootfs/usr/include
export ZLIBCPPFLAGS=-I/mnt/rootfs/usr/include
export ZLIBLDFLAGS=-L/mnt/rootfs/usr/lib
export LZOCPPFLAGS=-I/mnt/rootfs/usr/include
export LZOLDFLAGS=-L/mnt/rootfs/usr/lib
make CROSS=arm-none-linux-gnueabi- WITHOUT_XATTR=1
make CROSS=arm-none-linux-gnueabi- DESTDIR=/mnt/rootfs install

Linux内核等待队列

进程由于需要等待某些事件的到来而进入睡眠状态,当事件到来时再唤醒等待该事件的进程。Linux中进程睡眠是通过等待队列来实现的。我们以读磁盘文件为例,简述一下等待队列的基本原理。

我们使用read系统调用来读取磁盘上的一个文件,相对于CPU的运行速度,从磁盘读取一个文件需要相当长的时间,在磁盘返回指定的文件内容前,CPU还可以做很多其他工作。为了提高CPU的利用率,read系统调用在内核中的实现会将该进程移出运行队列,将其加入到磁盘相关的内核子系统的等待队列中,当磁盘读取完毕,磁盘相关的内核子系统将唤醒等待队列中的进程。

Linux内核等待队列有两个抽象,分别是等待队列和需要放入等待队列的任务。等待队列是一个简单的链表,等待队列链表头定义如下:

struct __wait_queue_head {
        spinlock_t lock;
        struct list_head task_list;
};
typedef struct __wait_queue_head wait_queue_head_t;

lock为自旋锁,用于保护等待队列。task_list是等待队列头。

等待任务的抽象如下所示:

struct __wait_queue {                                                                                                         
        unsigned int flags;
#define WQ_FLAG_EXCLUSIVE       0x01
        void *private;
        wait_queue_func_t func;
        struct list_head task_list;
};

private通常指向当前进程的任务控制块(PCB),func是任务唤醒操作方法,通常为autoremove_wake_function函数,task_list用于将当前任务加入到等待队列中。

常用的等待队列操作API如下所示:

init_waitqueue_head()  初始化等待队列头
add_wait_queue()       将任务加入到等待队列中
remove_wait_queue()    将任务从等待队列中移除
wake_up()              唤醒等待队列中的任务

Unix编程的错误处理风格

Unix系统编程总体而言有三种不同的错误处理风格,分别是Unix风格、Posix风格和DNS风格。

1. Unix风格

Unix风格的函数返回值可以包含正确的值,也可以是错误代码。如下所示:

if (pid = wait(NULL)) < 0) {
    fprintf(stderr, "wait error: %s\n", strerror(errno));
    exit(0);
}

2. Posix风格

只用返回返回成功(0)或失败(非0),任何有用的结果都通过函数的参数返回。如下所示:

if (retcode = pthread_create(&tid, NULL, thread, NULL) != 0) {
    fprintf(stderr, "pthread_create error: %s\n", strerror(retcode));
    exit(0);
}

3. DNS风格

失败时返回NULL指针,并设置全局变量变量h_errno。如下所示:

if ((p = gethostbyname(name)) == NULL) {
    fprintf(stderr, "gethostbyname error: %s\n", hstrerror(h_errno));
    exit(0);
}

Linux的framebuffer使用方法

概述

Linux通过framebuffer将显卡的显存映射到用户空间,进而允许用户直接操作显存,而显存的数个字节通常对应屏幕的一个像素,因此可以通过操纵显存来操纵屏幕每个像素的显示。

framebuffer的使用相当直接,首先是打开帧缓冲设备,帧缓冲设备为/dev/fb[0-n],n意味着每个系统可能会有多个帧缓冲设备。其次是使用ioctl从打开的帧缓冲设备中获取显存和屏幕的相关信息,然后将显存对应的物理地址空间通过mmap系统调用映射到用户空间的虚拟地址空间,通过操作该虚拟地址空间便可以操纵屏幕的显示。

打开帧缓冲设备

有些系统具有多个帧缓存设备,每个帧缓冲设备在dev下面都有对应的设备节点,设备节点以fd为前缀,后接有一个数字以示区别。帧缓冲设备的打开如下所示:

int fd;
fd = open("/dev/fd0", O_RDWR, 0);

获取屏幕信息

屏幕相关的信息通过两个结构体来表示,首先是屏幕的固定信息struct fb_fix_screeninfo,该结构体表示的信息都是显示芯片的固定信息,用户无法改变。该结构体声明如下所示:

struct fb_fix_screeninfo {
	char id[16];			/* identification string eg "TT Builtin" */
	unsigned long smem_start;	/* Start of frame buffer mem */
						/* (physical address) */
	__u32 smem_len;			/* Length of frame buffer mem */
	__u32 type;				/* see FB_TYPE_*		*/
	__u32 type_aux;			/* Interleave for interleaved Planes */
	__u32 visual;			/* see FB_VISUAL_*		*/
	__u16 xpanstep;			/* zero if no hardware panning  */
	__u16 ypanstep;			/* zero if no hardware panning  */
	__u16 ywrapstep;		/* zero if no hardware ywrap    */
	__u32 line_length;		/* length of a line in bytes    */
	unsigned long mmio_start;	/* Start of Memory Mapped I/O   */
						/* (physical address) */
	__u32 mmio_len;			/* Length of Memory Mapped I/O  */
	__u32 accel;			/* Indicate to driver which	*/
						/*  specific chip/card we have	*/
	__u16 reserved[3];		/* Reserved for future compatibility */
};

其中smem_start是帧缓冲区的物理起始地址,smem_len是帧缓冲区长度,line_length是屏幕显示一行需要的显存的字节长度。

屏幕的可变信息使用struct fb_var_screeninfo表示,该结构体中表示屏幕的相关信息可以根据用户的需要进行调整。该结构题声明如下所示:

struct fb_var_screeninfo {
	__u32 xres;				/* visible resolution		*/
	__u32 yres;
	__u32 xres_virtual;		/* virtual resolution		*/
	__u32 yres_virtual;
	__u32 xoffset;			/* offset from virtual to visible */
	__u32 yoffset;			/* resolution			*/

	__u32 bits_per_pixel;		/* guess what			*/
	__u32 grayscale;			/* != 0 Graylevels instead of colors */

	struct fb_bitfield red;		/* bitfield in fb mem if true color, */
	struct fb_bitfield green;	/* else only length is significant */
	struct fb_bitfield blue;
	struct fb_bitfield transp;	/* transparency			*/	

	__u32 nonstd;			/* != 0 Non standard pixel format */

	__u32 activate;			/* see FB_ACTIVATE_*		*/

	__u32 height;			/* height of picture in mm    */
	__u32 width;			/* width of picture in mm     */

	__u32 accel_flags;		/* (OBSOLETE) see fb_info.flags */

	/* Timing: All values in pixclocks, except pixclock (of course) */
	__u32 pixclock;			/* pixel clock in ps (pico seconds) */
	__u32 left_margin;		/* time from sync to picture	*/
	__u32 right_margin;		/* time from picture to sync	*/
	__u32 upper_margin;		/* time from sync to picture	*/
	__u32 lower_margin;
	__u32 hsync_len;			/* length of horizontal sync	*/
	__u32 vsync_len;			/* length of vertical sync	*/
	__u32 sync;				/* see FB_SYNC_*		*/
	__u32 vmode;			/* see FB_VMODE_*		*/
	__u32 rotate;			/* angle we rotate counter clockwise */
	__u32 reserved[5];		/* Reserved for future compatibility */
};

fb_var_screeninfo结构题的成员对于做过图像处理的用户不会陌生,xres和yres用于表示屏幕的可视分辨率,xres_virtual和yres_virtual用于表示屏幕的虚拟分辨率,虚拟分辨率通常会大于或等于可视分辨率。虚拟分辨率是指显示面板的实际大小,但是有些显示面板的边缘会失真,于是生产商特意将面板做得比最终可见的大一些,然后将失真的边沿遮住,剩下的可视区域便是可视分辨率。xoffset和yoffset用于表示可视分辨率原点到虚拟分辨率的原点的偏移。bits_per_pixel用于表示多少个位表示一个像素。

Linux中使用ioctl系统调用来获取屏幕的固定信息和可变信息,如下所示:

struct fb_fix_screeninfo fix_info;
struct fb_var_screeninfo var_info;
ioctl(fd, FBIOGET_FSCREENINFO, &fix_info);
ioctl(fd, FBIOGET_VSCREENINFO, &var_info);

帧缓冲区地址映射

使用如下代码段将帧缓冲区的地址空间映射到系统指定的用户虚拟地址空间中:

char *fb_bufptr;
fb_bufptr = (char *)mmap(0, fix_info.smem_len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);

帧缓冲的使用

如果屏幕可变信息的bits_per_pixel为32则表示系统使用32位显存表示一个像素,也就是说,如果想让屏幕的某一个像素显示特定的颜色,用户只需要在该像素对应的那个32位空间中存入相关的颜色值即可。下面是一个在屏幕指定位置画一个指定大小和颜色的矩形的代码示例:

void drawRect_rgb32(int x0, int y0, int width, int height, int color)
{
	const int bytes_per_pixel = 4;
	const int stride = fix_info.line_length / bytes_per_pixel;
	int x, y;

	int *dest = (int *)(fb_bufptr) + (y0 + var_info.yoffset) * stride + (x0 + var_info.xoffset);

	for (y = 0; y < height; y++) {
		for (x = 0; x < width; ++x)
			dest[x] = color;

		dest += stride;
	}
}

Building Qt for DM365

Installing the Qt Demos

This section closely follows Building Qt for ARM only devices.

  • Download Qt from here. Grab the “Framework Only” version (not the “Complete Development Environment”).
  • Untar the download somewhere in your filesystem:
$ tar xzf qt-everywhere-opensource-src-4.6.0.tar.gz
  • Create a copy of the directory containing qmake.conf for ARM:
cp -R [qt-install-dir]/mkspecs/qws/linux-arm-g++/ [qt-install-dir]/mkspecs/qws/linux-dm365-g++/
  • Modify the file mkspecs/qws/linux-omapl1-g++/qmake.conf appropriately. Make sure to include the correct paths for QMAKE_INCDIR and QMAKE_LIBDIR to ensure touchscreen support. For example:
#
# qmake configuration for building with arm-linux-g++
#

include(../../common/g++.conf)
include(../../common/linux.conf)
include(../../common/qws.conf)

# modifications to g++.conf
QMAKE_CC                = arm_v5t_le-gcc
QMAKE_CXX               = arm_v5t_le-g++
QMAKE_LINK              = arm_v5t_le-g++
QMAKE_LINK_SHLIB        = arm_v5t_le-g++

#modifications to include ts-lib
QMAKE_INCDIR            = /home/user/workdir/dm365/usr/include
QMAKE_LIBDIR            = /home/user/workdir/dm365/lib

# modifications to linux.conf
QMAKE_AR                = arm_v5t_le-ar cqs
QMAKE_OBJCOPY           = arm_v5t_le-objcopy
QMAKE_STRIP             = arm_v5t_le-strip

load(qt_config)
  • Run the configure script with the following options (prefix should point to the path where qt-embedded will be installed on the root filesystem):
$ ./configure -prefix /opt/qt-embedded -embedded arm -platform /qws/linux-x86-g++ \
-xplatform /qws/linux-dm365-g++ -depths 16,24,32 -no-cups -no-largefile -no-accessibility \
-no-openssl -qt-mouse-pc -qt-mouse-linuxtp -qt-mouse-linuxinput -plugin-mouse-linuxtp \
-plugin-mouse-pc –fast -qt-mouse-tslib

Note:

  1. “-prefix” <PATH> option in the configure above points to the path inside the rootfs where qt-e will be installedon Target FileSystem
  2. ‘”-platform ‘/qws/linux-x86-g++” in configure above assumes your host machine CPU to be of 32-bit. For 64-bit, use “-platform /qws/linux-x86_64-g++”
  3. For TouchScreen, you need to append the configure options “-qt-mouse-tslib” in above mentioned recommended options
  • Compile and install (This can take a long time)
$ gmake
$ gmake install
  • Add the following lines to the /root/.profile file on the target
export QWS_MOUSE_PROTO=tslib:/dev/input/event0
export QWS_DISPLAY=LinuxFb:/dev/fb2

Running the Qt Demos

  • Once booted, use the following commands to run a demo (It is assumed you installed qt-embedded in the /opt/qt-embedded directory:
# export LD_LIBRARY_PATH=/lib:/usr/lib:/usr/local/lib:/opt/qt-embedded/lib
# ./<Demo Name> -qws