http://www.cnblogs.com/HappyQQ/archive/2008/05/31/1211078.html
Open Source Flash
http://www.openstudio.fr/jQuery-Multimedia-Portfolio.html
This plugin for jQuery will automatically detect the extension of each media and apply the adapted player.
2008年6月27日星期五
2008年6月24日星期二
howto备份和移植PLONE
plone站点数据史保存在什么地方,怎样备份和移植plone站点?
Plone站点所有内容是保存在 Data.fs 文件理,这个文件位于 var 目录 (具体定位可以搜索 Data.fs文件). 这个数据库也通常作为zope的对象数据库 (ZODB)。
这个文件是一个完整的数据库,可以通过简单的COPY来作备份。备份时不必停下plone站点,但如果是访问量非常大的站点,有可能在备份期间,有数据更新,这时可以通过 repozo.py script 来进行自动调度备份。
如果你要移植plone站点,在作Data.fs 备份的同时,你还要备份 Products 目录,并且为避免麻烦,要确保各产品的版本在移植后是一致的。移植时,将 Data.fs 文件拷贝过去,然后将产品COPY过去。
提示:在从Windows移植Plone到LInux 时必须保证产品文件的权限有执行权限(我自己在移植时因为忽略了这点,结果产品移植就是不成功,为此在网上搜索了三天资料未果,结果还是自己仔细比对分析,发现时权限的问题),否则你的产品就不会出现在plone的可添加产品列表中。
Plone站点所有内容是保存在 Data.fs 文件理,这个文件位于 var 目录 (具体定位可以搜索 Data.fs文件). 这个数据库也通常作为zope的对象数据库 (ZODB)。
这个文件是一个完整的数据库,可以通过简单的COPY来作备份。备份时不必停下plone站点,但如果是访问量非常大的站点,有可能在备份期间,有数据更新,这时可以通过 repozo.py script 来进行自动调度备份。
如果你要移植plone站点,在作Data.fs 备份的同时,你还要备份 Products 目录,并且为避免麻烦,要确保各产品的版本在移植后是一致的。移植时,将 Data.fs 文件拷贝过去,然后将产品COPY过去。
提示:在从Windows移植Plone到LInux 时必须保证产品文件的权限有执行权限(我自己在移植时因为忽略了这点,结果产品移植就是不成功,为此在网上搜索了三天资料未果,结果还是自己仔细比对分析,发现时权限的问题),否则你的产品就不会出现在plone的可添加产品列表中。
2008年6月19日星期四
FileZilla源代码分析
分析版本:
FileZilla_3.0.11_src.tar.bz2
FileZilla客户端实现分析
http://davidripple.blogchina.com/davidripple/2482012.html
FileZilla_3.0.11_src.tar.bz2
FileZilla客户端实现分析
http://davidripple.blogchina.com/davidripple/2482012.html
2008年6月16日星期一
Mg2 分析
mg2
|-includes
|-exif.php
|-mg2_functions.php
|-mg2_version.php
|-mg2admin_functions.php
|-skins
|-admin.php 管理入口
|-index.php 首页前端入口
|-md2_install.php 安装页面入口
|-upgrade.php 升级页面入口 ,用来升级到 0.5.0
md2_install.php
1-1) 判断是否已经安装,即是否存在文件mg2_settings.php
1-2) 判断是否有当前目录和pictures目录的读写权限。前者用于存放配置文件mg2_settings.php,后者用于存放上传的图片。
1-3) 判断系统的几个主要文件是否存在,确认系统能正常工作;
1-4) 判断GD库的版本是否大于等于2;
2-1) 判断语言包目录是否存在,让用户选择安装语言;
3-1) 保存各项具体配置;
3-2) 将各项配置信息统一写到文件mg2_settings.php中;
判断是否已经安装,通过判断文件mg2_settings.php 是否是一个文件。
------------------------------
if (is_file("mg2_settings.php")) {
......
------------------------------
判断是否有 pictures 目录的读写权限
------------------------------
@rmdir("pictures/x");
if (@mkdir("pictures/x")) {
@rmdir("pictures/x");
} else {echo "ERROR: Cannot write to 'pictures' folder. Chmod 'pictures' to 777 before continuing!";exit();}
------------------------------
|-includes
|-exif.php
|-mg2_functions.php
|-mg2_version.php
|-mg2admin_functions.php
|-skins
|-admin.php 管理入口
|-index.php 首页前端入口
|-md2_install.php 安装页面入口
|-upgrade.php 升级页面入口 ,用来升级到 0.5.0
md2_install.php
1-1) 判断是否已经安装,即是否存在文件mg2_settings.php
1-2) 判断是否有当前目录和pictures目录的读写权限。前者用于存放配置文件mg2_settings.php,后者用于存放上传的图片。
1-3) 判断系统的几个主要文件是否存在,确认系统能正常工作;
1-4) 判断GD库的版本是否大于等于2;
2-1) 判断语言包目录是否存在,让用户选择安装语言;
3-1) 保存各项具体配置;
3-2) 将各项配置信息统一写到文件mg2_settings.php中;
判断是否已经安装,通过判断文件mg2_settings.php 是否是一个文件。
------------------------------
if (is_file("mg2_settings.php")) {
......
------------------------------
判断是否有 pictures 目录的读写权限
------------------------------
@rmdir("pictures/x");
if (@mkdir("pictures/x")) {
@rmdir("pictures/x");
} else {echo "ERROR: Cannot write to 'pictures' folder. Chmod 'pictures' to 777 before continuing!";exit();}
------------------------------
2008年6月12日星期四
2008年6月11日星期三
系统变慢的三种原因和处理方法
http://hehe2.net/linuxhowto/3-reasons-why-your-system-might-be-slow/
1. CPU 资源被占用 --> top => renice 20 26210 => kill 26210
2. 物理内存几乎被用完 --> free -m => top => shutdown the program that’s using the most memory.
3. 大量的IO操作 --> top => atop => ionice -p30956 -n7
1. CPU 资源被占用 --> top => renice 20 26210 => kill 26210
2. 物理内存几乎被用完 --> free -m => top => shutdown the program that’s using the most memory.
3. 大量的IO操作 --> top => atop => ionice -p30956 -n7
2008年6月10日星期二
Build with Visual Studio Express 2008, JGE , PSP
http://www.jetcube.eu/archives/2008/02/entry_74.html
Build with Visual Studio Express 2008, JGE , PSP
Build with Visual Studio Express 2008, JGE , PSP
离线安装Visual C++ Express
0.下载安装.Net Framework 2.0
http://www.microsoft.com/downloads/info.aspx?na=90&p=&SrcDisplayLang=zh-cn&SrcCategoryId=&SrcFamilyId=0856eacb-4362-4b0d-8edd-aab15c5e04f5&u=http%3a%2f%2fdownload.microsoft.com%2fdownload%2f5%2f6%2f7%2f567758a3-759e-473e-bf8f-52154438565a%2fdotnetfx.exe
1.直接下载Microsoft .NET Framework 2.0 语言包 - 简体中文(langpack.exe):http://go.microsoft.com/fwlink/?LinkId=54024&clcid=0x804
2.安装langpack.exe
3.直接下载安装文件客户端(vcsetup.exe):http://go.microsoft.com/fwlink/?Linkid=51410&clcid=0x804
4.直接下载Microsoft Visual C++ 2005 Express Edition - CHS(Ixpvc.exe):http://go.microsoft.com/fwlink/?LinkId=51417&clcid=0x804
5.解压缩vcsetup.exe和Ixpvc.exe到某个目录。
6.运行解压缩出来的setup.exe安装。
7.下载并安装SP1:http://download.microsoft.com/download/7/7/3/7737290f-98e8-45bf-9075-85cc6ae34bf1/vs80sp1-kb926748-x86-intl.exe
8.下载并安装 PSDK platform SDK
http://www.microsoft.com/msdownload/platformsdk/sdkupdate/XPSP2FULLInstall.htm
or iso image
http://download.microsoft.com/download/9/7/a/97a5ac16-69ae-4672-b93e-40d66d77b278/5.2.3790.2075.51.PlatformSDK_Svr2003R2_rtm.img
or
http://www.microsoft.com/msdownload/platformsdk/sdkupdate/XPSP2FULLInstall.htm
P.S Express版本与Visual Studio个版本之间的功能差异比较详见:http://msdn2.microsoft.com/en-us/library/hs24szh9(vs.80).aspx
http://www.microsoft.com/downloads/info.aspx?na=90&p=&SrcDisplayLang=zh-cn&SrcCategoryId=&SrcFamilyId=0856eacb-4362-4b0d-8edd-aab15c5e04f5&u=http%3a%2f%2fdownload.microsoft.com%2fdownload%2f5%2f6%2f7%2f567758a3-759e-473e-bf8f-52154438565a%2fdotnetfx.exe
1.直接下载Microsoft .NET Framework 2.0 语言包 - 简体中文(langpack.exe):http://go.microsoft.com/fwlink/?LinkId=54024&clcid=0x804
2.安装langpack.exe
3.直接下载安装文件客户端(vcsetup.exe):http://go.microsoft.com/fwlink/?Linkid=51410&clcid=0x804
4.直接下载Microsoft Visual C++ 2005 Express Edition - CHS(Ixpvc.exe):http://go.microsoft.com/fwlink/?LinkId=51417&clcid=0x804
5.解压缩vcsetup.exe和Ixpvc.exe到某个目录。
6.运行解压缩出来的setup.exe安装。
7.下载并安装SP1:http://download.microsoft.com/download/7/7/3/7737290f-98e8-45bf-9075-85cc6ae34bf1/vs80sp1-kb926748-x86-intl.exe
8.下载并安装 PSDK platform SDK
http://www.microsoft.com/msdownload/platformsdk/sdkupdate/XPSP2FULLInstall.htm
or iso image
http://download.microsoft.com/download/9/7/a/97a5ac16-69ae-4672-b93e-40d66d77b278/5.2.3790.2075.51.PlatformSDK_Svr2003R2_rtm.img
or
http://www.microsoft.com/msdownload/platformsdk/sdkupdate/XPSP2FULLInstall.htm
P.S Express版本与Visual Studio个版本之间的功能差异比较详见:http://msdn2.microsoft.com/en-us/library/hs24szh9(vs.80).aspx
PSP websites
4 + 5 应该是比较好的开发环境
1. http://www.psp-hacks.com/
2.
PSP 3.71m 开发环境的建立
http://www.xici.net/b902738/d63863130.htm
3.
TOPOC
http://hi.baidu.com/topoc
4. Minimalist PSPSDK for Windows, 应该是当前最好的编译工具集了。可以仿照打造 Ezx/LJ的工具集
http://sourceforge.net/project/showfiles.php?group_id=223830
5. JGE++
http://code.google.com/p/jge/
6.
http://www.jetcube.eu/
1. http://www.psp-hacks.com/
2.
PSP 3.71m 开发环境的建立
http://www.xici.net/b902738/d63863130.htm
3.
TOPOC
http://hi.baidu.com/topoc
4. Minimalist PSPSDK for Windows, 应该是当前最好的编译工具集了。可以仿照打造 Ezx/LJ的工具集
http://sourceforge.net/project/showfiles.php?group_id=223830
5. JGE++
http://code.google.com/p/jge/
6.
http://www.jetcube.eu/
2008年6月8日星期日
PSP2000 Hello world
1. PSP2000 3.60 M33
2. devkitProUpdater-1.4.7.exe 安装到 D:\devkitPro
3. 开始 --> 所有程序 --> devkitPro --> MSys
4. cd /d
5. mkdir /d/PSP
6. cd /d/PSP
7. mkdir HelloPSP2K
8. cd HelloPSP2K
9. vi main.c
10 vi Makefile
11. export PATH=$PATH:/d/devkitPro/devkitPSP/bin
12. make
13. 查看生成的文件
$ ls
EBOOT.PBP Makefile PARAM.SFO hello.elf hello.prx main.c main.o
14. 连接PSP2000到电脑,假设 盘符为 F盘
15. 将文件复制到PSP2000上(F:\PSP\GAME),创建目录 F:\PSP\GAME\hello
将文件:EBOOT.PBP hello.elf hello.prx PARAM.SFO 复制到目录 F:\PSP\GAME\hello
16. 断开USB连接
17. 在游戏下将可以看到 Hello PSP这个新的目录项
18. 运行 Hello PSP , 在屏幕左上角将出现 Hello PSP 字样
至此,终于将PSP2000的开发和运行环境搭建完毕。
2. devkitProUpdater-1.4.7.exe 安装到 D:\devkitPro
3. 开始 --> 所有程序 --> devkitPro --> MSys
4. cd /d
5. mkdir /d/PSP
6. cd /d/PSP
7. mkdir HelloPSP2K
8. cd HelloPSP2K
9. vi main.c
/// Hello PSP - My First App for the PSP
#include <pspkernel.h>
#include <pspdebug.h>
PSP_MODULE_INFO("Hello PSP", 0, 1, 0);
PSP_HEAP_SIZE_KB(20480);
// Exit callback
int exit_callback(int arg1, int arg2, void *common)
{
sceKernelExitGame();
return 0;
}
// Callback thread
int CallbackThread(SceSize args, void *argp)
{
int cbid;
cbid = sceKernelCreateCallback("Exit Callback", exit_callback, NULL);
sceKernelRegisterExitCallback(cbid);
sceKernelSleepThreadCB();
return 0;
}
// Sets up the callback thread and returns its thread id
int SetupCallbacks(void)
{
int thid = 0;
thid = sceKernelCreateThread("update_thread", CallbackThread, 0x11, 0xFA0, 0, 0);
if(thid >= 0)
{
sceKernelStartThread(thid, 0, 0);
}
return thid;
}
int main()
{
pspDebugScreenInit();
SetupCallbacks();
pspDebugScreenPrintf("Hello PSP");
sceKernelSleepThread();
return 0;
}
10 vi Makefile
TARGET = hello
OBJS = main.o
CFLAGS = -O2 -G0 -Wall
CXXFLAGS = $(CFLAGS) -fno-exceptions -fno-rtti
ASFLAGS = $(CFLAGS)
EXTRA_TARGETS = EBOOT.PBP
PSP_EBOOT_TITLE = Hello PSP
PSPSDK=$(shell psp-config --pspsdk-path)
BUILD_PRX=1
PSP_FW_VERSION=371
include $(PSPSDK)/lib/build.mak
11. export PATH=$PATH:/d/devkitPro/devkitPSP/bin
12. make
13. 查看生成的文件
$ ls
EBOOT.PBP Makefile PARAM.SFO hello.elf hello.prx main.c main.o
14. 连接PSP2000到电脑,假设 盘符为 F盘
15. 将文件复制到PSP2000上(F:\PSP\GAME),创建目录 F:\PSP\GAME\hello
将文件:EBOOT.PBP hello.elf hello.prx PARAM.SFO 复制到目录 F:\PSP\GAME\hello
16. 断开USB连接
17. 在游戏下将可以看到 Hello PSP这个新的目录项
18. 运行 Hello PSP , 在屏幕左上角将出现 Hello PSP 字样
至此,终于将PSP2000的开发和运行环境搭建完毕。
2008年6月5日星期四
ARM Linux 中断分析
ARM体系结构中,把复位、中断、快速中断等都看作‘异常’,当这些‘异常’发生时,CPU会到固定地址处去找指令,他们对应的地址如下:
地址 异常类型 进入时的工作模式
0x00000000 Reset Supervisor
0x00000004 Und Undefined
0x00000008 Soft interupt Supervisor
0x0000000c Abort(prefetch) Abort
0x00000010 Abort(data) Abort
0x00000014 Reserved Reserved
0x00000018 IRQ IRQ
0x0000001c FIQ FIQ
首先要明确的一点就是,无论内存地址空间是如何映射的,以上这些地址都不会变,比如当有快速中断发生时,ARM将铁定到0X0000001C这个地址处取指令。这也是BOOTLOADER把操作系统引导以后,内存必须重映射的原因!否则操作系统不能真正接管整套系统!
LINUX启动以后要初始化这些区域,初始化代码在main.c中的start_kernel()中,具体是调用函数trap_ini()来实现的。如下面所示(具体可参照entry-armv.S):
.LCvectors: swi SYS_ERROR0
b __real_stubs_start + (vector_undefinstr - __stubs_start)
ldr pc, __real_stubs_start + (.LCvswi - __stubs_start)
b __real_stubs_start + (vector_prefetch - __stubs_start)
b __real_stubs_start + (vector_data - __stubs_start)
b __real_stubs_start + (vector_addrexcptn - __stubs_start)
b __real_stubs_start + (vector_IRQ - __stubs_start)
b __real_stubs_start + (vector_FIQ - __stubs_start)
ENTRY(__trap_init)
stmfd sp!, {r4 - r6, lr}
adr r1, .LCvectors @ set up the vectors
ldmia r1, {r1, r2, r3, r4, r5, r6, ip, lr}
stmia r0, {r1, r2, r3, r4, r5, r6, ip, lr}
add r2, r0, #0x200
adr r0, __stubs_start @ copy stubs to 0x200
adr r1, __stubs_end
1: ldr r3, [r0], #4
str r3, [r2], #4
cmp r0, r1
blt 1b
LOADREGS(fd, sp!, {r4 - r6, pc})
以上可以看出这个函数初始化了中断向量,实际上把相应的跳转指令拷贝到了对应的地址。
当发生中断时,不管是从用户模式还是管理模式调用的,最终都要调用do_IRQ():
__irq_usr: sub sp, sp, #S_FRAME_SIZE
stmia sp, {r0 - r12} @ save r0 - r12
ldr r4, .LCirq
add r8, sp, #S_PC
ldmia r4, {r5 - r7} @ get saved PC, SPSR
stmia r8, {r5 - r7} @ save pc, psr, old_r0
stmdb r8, {sp, lr}^
alignment_trap r4, r7, __temp_irq
zero_fp
1: get_irqnr_and_base r0, r6, r5, lr
movne r1, sp
adrsvc ne, lr, 1b
@
@ routine called with r0 = irq number, r1 = struct pt_regs *
@
bne do_IRQ @ 调用do_IRQ来实现具体的中断处理
mov why, #0
get_current_task tsk
b ret_to_user
对于以上代码,在很多文章中都有过分析,这里不再赘述。
Linux每个中断通过一个结构irqdesc来描述,各中断的信息都在这个结构中得以体现:
struct irqdesc {
unsigned int nomask : 1; /* IRQ does not mask in IRQ */
unsigned int enabled : 1; /* IRQ is currently enabled */
unsigned int triggered: 1; /* IRQ has occurred */
unsigned int probing : 1; /* IRQ in use for a probe */
unsigned int probe_ok : 1; /* IRQ can be used for probe */
unsigned int valid : 1; /* IRQ claimable */
unsigned int noautoenable : 1; /* don't automatically enable IRQ */
unsigned int unused :25;
void (*mask_ack)(unsigned int irq); /* Mask and acknowledge IRQ */
void (*mask)(unsigned int irq); /* Mask IRQ */
void (*unmask)(unsigned int irq); /* Unmask IRQ */
struct irqaction *action;
/*
* IRQ lock detection
*/
unsigned int lck_cnt;
unsigned int lck_pc;
unsigned int lck_jif;
};
在具体的ARM芯片中会有很多的中断类型,每一种类型的中断用以上结构来表示:
struct irqdesc irq_desc[NR_IRQS]; /* NR_IRQS根据不同的MCU会有所区别*/
在通过request_irq()函数注册中断服务程序的时候,将会把中断向量和中断服务程序对应起来。
我们来看一下request_irq的源码:
int request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *),
unsigned long irq_flags, const char * devname, void *dev_id)
{
unsigned long retval;
struct irqaction *action;
if (irq >= NR_IRQS || !irq_desc[irq].valid || !handler ||
(irq_flags & SA_SHIRQ && !dev_id))
return -EINVAL;
action = (struct irqaction *)kmalloc(sizeof(struct irqaction), GFP_KERNEL);
if (!action) /* 生成action结构*/
return -ENOMEM;
action->handler = handler;
action->flags = irq_flags;
action->mask = 0;
action->name = devname;
action->next = NULL;
action->dev_id = dev_id;
retval = setup_arm_irq(irq, action); /*把中断号irq和action 对应起来*/
if (retval)
kfree(action);
return retval;
}
其中第一个参数irq就是中断向量,第二个参数即是要注册的中断服务程序。很多同仁可能疑惑的是,我们要注册的中断向量号是怎么确定的呢?这要根据具体芯片的中断控制器,比如三星的S3C2410,需要通过读取其中的中断状态寄存器,来获得是哪个设备发生了中断:
if defined(CONFIG_ARCH_S3C2410)
#include
.macro disable_fiq
.endm
.macro get_irqnr_and_base, irqnr, irqstat, base, tmp
mov r4, #INTBASE @ virtual address of IRQ registers
ldr irqnr, [r4, #0x8] @ read INTMSK 中断掩码寄存器
ldr irqstat, [r4, #0x10] @ read INTPND 中断寄存器
bics irqstat, irqstat, irqnr
bics irqstat, irqstat, irqnr
beq 1002f
mov irqnr, #0
1001: tst irqstat, #1
bne 1002f @ found IRQ
add irqnr, irqnr, #1
mov irqstat, irqstat, lsr #1
cmp irqnr, #32
bcc 1001b
1002:
.endm
.macro irq_prio_table
.endm
以上代码也告诉了我们,中断号的确定,其实是和S3C2410手册中SRCPND寄存器是一致的,即:
/* Interrupt Controller */
#define IRQ_EINT0 0 /* External interrupt 0 */
#define IRQ_EINT1 1 /* External interrupt 1 */
#define IRQ_EINT2 2 /* External interrupt 2 */
#define IRQ_EINT3 3 /* External interrupt 3 */
#define IRQ_EINT4_7 4 /* External interrupt 4 ~ 7 */
#define IRQ_EINT8_23 5 /* External interrupt 8 ~ 23 */
#define IRQ_RESERVED6 6 /* Reserved for future use */
#define IRQ_BAT_FLT 7
#define IRQ_TICK 8 /* RTC time tick interrupt */
#define IRQ_WDT 9 /* Watch-Dog timer interrupt */
#define IRQ_TIMER0 10 /* Timer 0 interrupt */
#define IRQ_TIMER1 11 /* Timer 1 interrupt */
#define IRQ_TIMER2 12 /* Timer 2 interrupt */
#define IRQ_TIMER3 13 /* Timer 3 interrupt */
#define IRQ_TIMER4 14 /* Timer 4 interrupt */
#define IRQ_UART2 15 /* UART 2 interrupt */
#define IRQ_LCD 16 /* reserved for future use */
#define IRQ_DMA0 17 /* DMA channel 0 interrupt */
#define IRQ_DMA1 18 /* DMA channel 1 interrupt */
#define IRQ_DMA2 19 /* DMA channel 2 interrupt */
#define IRQ_DMA3 20 /* DMA channel 3 interrupt */
#define IRQ_SDI 21 /* SD Interface interrupt */
#define IRQ_SPI0 22 /* SPI interrupt */
#define IRQ_UART1 23 /* UART1 receive interrupt */
#define IRQ_RESERVED24 24
#define IRQ_USBD 25 /* USB device interrupt */
#define IRQ_USBH 26 /* USB host interrupt */
#define IRQ_IIC 27 /* IIC interrupt */
#define IRQ_UART0 28 /* UART0 transmit interrupt */
#define IRQ_SPI1 29 /* UART1 transmit interrupt */
#define IRQ_RTC 30 /* RTC alarm interrupt */
#define IRQ_ADCTC 31 /* ADC EOC interrupt */
#define NORMAL_IRQ_OFFSET 32
这些宏定义在文件irqs.h中,大家可以看到它的定义取自S3C2410的文档。
总结: linux在初始化的时候已经把每个中断向量的地址准备好了!就是说添加中断服务程序的框架已经给出,当某个中断发生时,将会到确定的地址处去找指令,所以我们做驱动程序时,只需要经过request_irq()来挂接自己编写的中断服务程序即可。
另:对于快速中断,linux在初始化时是空的,所以要对它挂接中断处理程序,就需要单独的函数set_fiq_handler()来实现,此函数在源文件fiq.c中,有兴趣的读者可进一步研究。
地址 异常类型 进入时的工作模式
0x00000000 Reset Supervisor
0x00000004 Und Undefined
0x00000008 Soft interupt Supervisor
0x0000000c Abort(prefetch) Abort
0x00000010 Abort(data) Abort
0x00000014 Reserved Reserved
0x00000018 IRQ IRQ
0x0000001c FIQ FIQ
首先要明确的一点就是,无论内存地址空间是如何映射的,以上这些地址都不会变,比如当有快速中断发生时,ARM将铁定到0X0000001C这个地址处取指令。这也是BOOTLOADER把操作系统引导以后,内存必须重映射的原因!否则操作系统不能真正接管整套系统!
LINUX启动以后要初始化这些区域,初始化代码在main.c中的start_kernel()中,具体是调用函数trap_ini()来实现的。如下面所示(具体可参照entry-armv.S):
.LCvectors: swi SYS_ERROR0
b __real_stubs_start + (vector_undefinstr - __stubs_start)
ldr pc, __real_stubs_start + (.LCvswi - __stubs_start)
b __real_stubs_start + (vector_prefetch - __stubs_start)
b __real_stubs_start + (vector_data - __stubs_start)
b __real_stubs_start + (vector_addrexcptn - __stubs_start)
b __real_stubs_start + (vector_IRQ - __stubs_start)
b __real_stubs_start + (vector_FIQ - __stubs_start)
ENTRY(__trap_init)
stmfd sp!, {r4 - r6, lr}
adr r1, .LCvectors @ set up the vectors
ldmia r1, {r1, r2, r3, r4, r5, r6, ip, lr}
stmia r0, {r1, r2, r3, r4, r5, r6, ip, lr}
add r2, r0, #0x200
adr r0, __stubs_start @ copy stubs to 0x200
adr r1, __stubs_end
1: ldr r3, [r0], #4
str r3, [r2], #4
cmp r0, r1
blt 1b
LOADREGS(fd, sp!, {r4 - r6, pc})
以上可以看出这个函数初始化了中断向量,实际上把相应的跳转指令拷贝到了对应的地址。
当发生中断时,不管是从用户模式还是管理模式调用的,最终都要调用do_IRQ():
__irq_usr: sub sp, sp, #S_FRAME_SIZE
stmia sp, {r0 - r12} @ save r0 - r12
ldr r4, .LCirq
add r8, sp, #S_PC
ldmia r4, {r5 - r7} @ get saved PC, SPSR
stmia r8, {r5 - r7} @ save pc, psr, old_r0
stmdb r8, {sp, lr}^
alignment_trap r4, r7, __temp_irq
zero_fp
1: get_irqnr_and_base r0, r6, r5, lr
movne r1, sp
adrsvc ne, lr, 1b
@
@ routine called with r0 = irq number, r1 = struct pt_regs *
@
bne do_IRQ @ 调用do_IRQ来实现具体的中断处理
mov why, #0
get_current_task tsk
b ret_to_user
对于以上代码,在很多文章中都有过分析,这里不再赘述。
Linux每个中断通过一个结构irqdesc来描述,各中断的信息都在这个结构中得以体现:
struct irqdesc {
unsigned int nomask : 1; /* IRQ does not mask in IRQ */
unsigned int enabled : 1; /* IRQ is currently enabled */
unsigned int triggered: 1; /* IRQ has occurred */
unsigned int probing : 1; /* IRQ in use for a probe */
unsigned int probe_ok : 1; /* IRQ can be used for probe */
unsigned int valid : 1; /* IRQ claimable */
unsigned int noautoenable : 1; /* don't automatically enable IRQ */
unsigned int unused :25;
void (*mask_ack)(unsigned int irq); /* Mask and acknowledge IRQ */
void (*mask)(unsigned int irq); /* Mask IRQ */
void (*unmask)(unsigned int irq); /* Unmask IRQ */
struct irqaction *action;
/*
* IRQ lock detection
*/
unsigned int lck_cnt;
unsigned int lck_pc;
unsigned int lck_jif;
};
在具体的ARM芯片中会有很多的中断类型,每一种类型的中断用以上结构来表示:
struct irqdesc irq_desc[NR_IRQS]; /* NR_IRQS根据不同的MCU会有所区别*/
在通过request_irq()函数注册中断服务程序的时候,将会把中断向量和中断服务程序对应起来。
我们来看一下request_irq的源码:
int request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *),
unsigned long irq_flags, const char * devname, void *dev_id)
{
unsigned long retval;
struct irqaction *action;
if (irq >= NR_IRQS || !irq_desc[irq].valid || !handler ||
(irq_flags & SA_SHIRQ && !dev_id))
return -EINVAL;
action = (struct irqaction *)kmalloc(sizeof(struct irqaction), GFP_KERNEL);
if (!action) /* 生成action结构*/
return -ENOMEM;
action->handler = handler;
action->flags = irq_flags;
action->mask = 0;
action->name = devname;
action->next = NULL;
action->dev_id = dev_id;
retval = setup_arm_irq(irq, action); /*把中断号irq和action 对应起来*/
if (retval)
kfree(action);
return retval;
}
其中第一个参数irq就是中断向量,第二个参数即是要注册的中断服务程序。很多同仁可能疑惑的是,我们要注册的中断向量号是怎么确定的呢?这要根据具体芯片的中断控制器,比如三星的S3C2410,需要通过读取其中的中断状态寄存器,来获得是哪个设备发生了中断:
if defined(CONFIG_ARCH_S3C2410)
#include
.macro disable_fiq
.endm
.macro get_irqnr_and_base, irqnr, irqstat, base, tmp
mov r4, #INTBASE @ virtual address of IRQ registers
ldr irqnr, [r4, #0x8] @ read INTMSK 中断掩码寄存器
ldr irqstat, [r4, #0x10] @ read INTPND 中断寄存器
bics irqstat, irqstat, irqnr
bics irqstat, irqstat, irqnr
beq 1002f
mov irqnr, #0
1001: tst irqstat, #1
bne 1002f @ found IRQ
add irqnr, irqnr, #1
mov irqstat, irqstat, lsr #1
cmp irqnr, #32
bcc 1001b
1002:
.endm
.macro irq_prio_table
.endm
以上代码也告诉了我们,中断号的确定,其实是和S3C2410手册中SRCPND寄存器是一致的,即:
/* Interrupt Controller */
#define IRQ_EINT0 0 /* External interrupt 0 */
#define IRQ_EINT1 1 /* External interrupt 1 */
#define IRQ_EINT2 2 /* External interrupt 2 */
#define IRQ_EINT3 3 /* External interrupt 3 */
#define IRQ_EINT4_7 4 /* External interrupt 4 ~ 7 */
#define IRQ_EINT8_23 5 /* External interrupt 8 ~ 23 */
#define IRQ_RESERVED6 6 /* Reserved for future use */
#define IRQ_BAT_FLT 7
#define IRQ_TICK 8 /* RTC time tick interrupt */
#define IRQ_WDT 9 /* Watch-Dog timer interrupt */
#define IRQ_TIMER0 10 /* Timer 0 interrupt */
#define IRQ_TIMER1 11 /* Timer 1 interrupt */
#define IRQ_TIMER2 12 /* Timer 2 interrupt */
#define IRQ_TIMER3 13 /* Timer 3 interrupt */
#define IRQ_TIMER4 14 /* Timer 4 interrupt */
#define IRQ_UART2 15 /* UART 2 interrupt */
#define IRQ_LCD 16 /* reserved for future use */
#define IRQ_DMA0 17 /* DMA channel 0 interrupt */
#define IRQ_DMA1 18 /* DMA channel 1 interrupt */
#define IRQ_DMA2 19 /* DMA channel 2 interrupt */
#define IRQ_DMA3 20 /* DMA channel 3 interrupt */
#define IRQ_SDI 21 /* SD Interface interrupt */
#define IRQ_SPI0 22 /* SPI interrupt */
#define IRQ_UART1 23 /* UART1 receive interrupt */
#define IRQ_RESERVED24 24
#define IRQ_USBD 25 /* USB device interrupt */
#define IRQ_USBH 26 /* USB host interrupt */
#define IRQ_IIC 27 /* IIC interrupt */
#define IRQ_UART0 28 /* UART0 transmit interrupt */
#define IRQ_SPI1 29 /* UART1 transmit interrupt */
#define IRQ_RTC 30 /* RTC alarm interrupt */
#define IRQ_ADCTC 31 /* ADC EOC interrupt */
#define NORMAL_IRQ_OFFSET 32
这些宏定义在文件irqs.h中,大家可以看到它的定义取自S3C2410的文档。
总结: linux在初始化的时候已经把每个中断向量的地址准备好了!就是说添加中断服务程序的框架已经给出,当某个中断发生时,将会到确定的地址处去找指令,所以我们做驱动程序时,只需要经过request_irq()来挂接自己编写的中断服务程序即可。
另:对于快速中断,linux在初始化时是空的,所以要对它挂接中断处理程序,就需要单独的函数set_fiq_handler()来实现,此函数在源文件fiq.c中,有兴趣的读者可进一步研究。
2008年6月4日星期三
JavaScript中的try...catch和异常处理
在JavaScript可以使用try...catch来进行异常处理。例如:
try {
foo.bar();
} catch (e) {
alert(e.name + ": " + e.message);
}
目前我们可能得到的系统异常主要包含以下6种:
* EvalError: raised when an error occurs executing code in eval()
* RangeError: raised when a numeric variable or parameter is outside of its valid range
* ReferenceError: raised when de-referencing an invalid reference
* SyntaxError: raised when a syntax error occurs while parsing code in eval()
* TypeError: raised when a variable or parameter is not a valid type
* URIError: raised when encodeURI() or decodeURI() are passed invalid parameters
上面的六种异常对象都继承自Error对象。他们都支持以下两种构造方法:
new Error();
new Error("异常信息");
手工抛出异常的方法如下:
try {
throw new Error("Whoops!");
} catch (e) {
alert(e.name + ": " + e.message);
}
如要判断异常信息的类型,可在catch中进行判断:
try {
foo.bar();
} catch (e) {
if (e instanceof EvalError) {
alert(e.name + ":" + e.message);
}
else if (e instanceof RangeError) {
alert(e.name + ": " + e.message);
}
// etc
}
Error具有下面一些主要属性:
* description: 错误描述 (仅IE可用).
* fileName: 出错的文件名 (仅Mozilla可用).
* lineNumber: 出错的行数 (仅Mozilla可用).
* message: 错误信息 (在IE下同description)
* name: 错误类型.
* number: 错误代码 (仅IE可用).
* stack: 像Java中的Stack Trace一样的错误堆栈信息 (仅Mozilla可用).
因此为了更好的了解错误信息我们可以将catch部分改为如下形式:
try {
foo.bar();
} catch (e) {
if (browserType != BROWSER_IE) {
alert("name: " + e.name +
"message: " + e.message +
"lineNumber: " + e.lineNumber +
"fileName: " + e.fileName +
"stack: " + e.stack);
}
else {
alert("name: " + e.name +
"errorNumber: " + (e.number & 0xFFFF ) +
"message: " + e.message");
}
}
JavaScript中的throw命令事实上可以抛出任何对象,并且我们可以在catch接受到此对象。例如:
try {
throw new Date(); // 抛出当前时间对象
} catch (e) {
alert(e.toLocaleString()); // 使用本地格式显示当前时间
}
try {
foo.bar();
} catch (e) {
alert(e.name + ": " + e.message);
}
目前我们可能得到的系统异常主要包含以下6种:
* EvalError: raised when an error occurs executing code in eval()
* RangeError: raised when a numeric variable or parameter is outside of its valid range
* ReferenceError: raised when de-referencing an invalid reference
* SyntaxError: raised when a syntax error occurs while parsing code in eval()
* TypeError: raised when a variable or parameter is not a valid type
* URIError: raised when encodeURI() or decodeURI() are passed invalid parameters
上面的六种异常对象都继承自Error对象。他们都支持以下两种构造方法:
new Error();
new Error("异常信息");
手工抛出异常的方法如下:
try {
throw new Error("Whoops!");
} catch (e) {
alert(e.name + ": " + e.message);
}
如要判断异常信息的类型,可在catch中进行判断:
try {
foo.bar();
} catch (e) {
if (e instanceof EvalError) {
alert(e.name + ":" + e.message);
}
else if (e instanceof RangeError) {
alert(e.name + ": " + e.message);
}
// etc
}
Error具有下面一些主要属性:
* description: 错误描述 (仅IE可用).
* fileName: 出错的文件名 (仅Mozilla可用).
* lineNumber: 出错的行数 (仅Mozilla可用).
* message: 错误信息 (在IE下同description)
* name: 错误类型.
* number: 错误代码 (仅IE可用).
* stack: 像Java中的Stack Trace一样的错误堆栈信息 (仅Mozilla可用).
因此为了更好的了解错误信息我们可以将catch部分改为如下形式:
try {
foo.bar();
} catch (e) {
if (browserType != BROWSER_IE) {
alert("name: " + e.name +
"message: " + e.message +
"lineNumber: " + e.lineNumber +
"fileName: " + e.fileName +
"stack: " + e.stack);
}
else {
alert("name: " + e.name +
"errorNumber: " + (e.number & 0xFFFF ) +
"message: " + e.message");
}
}
JavaScript中的throw命令事实上可以抛出任何对象,并且我们可以在catch接受到此对象。例如:
try {
throw new Date(); // 抛出当前时间对象
} catch (e) {
alert(e.toLocaleString()); // 使用本地格式显示当前时间
}
2008年6月3日星期二
如何svn使用http代理 proxy
编辑 ~/.subversion/servers
[global]
# http-proxy-exceptions = *.exception.com, www.internal-site.org
http-proxy-host = 172.16.204.50
http-proxy-port = 8080
# http-compression = no 有的需要将压缩屏蔽
[global]
# http-proxy-exceptions = *.exception.com, www.internal-site.org
http-proxy-host = 172.16.204.50
http-proxy-port = 8080
# http-compression = no 有的需要将压缩屏蔽
2008年6月2日星期一
UML设计经验
一. 需求阶段
1. 需求阶段使用use-case图描述需求
顶层use-case:粗粒度地描述系统,给出系统的概况
细分use-case:将顶层use-case细化
Use_case图的方法是:从参与者开始寻找用例,用use-case diagram来表示参与者与用例之间的关系。
Use-case描述的方法是:就是use-case规约,用详尽的文字来描述用例的执行流程(包括主业务流程及所有分支流程及异常流程)。
Active diagram:可选,类似业务流程图,业务比较复杂时,用流程图来描述业务。
注:use-case图和use-case描述,是整个系统设计的主要产品,今后的分析和设计,都将围绕这两个产品进行。以前看过的好多资料,都没有讲到这一点,use-case的作用非常大,将贯穿分析到设计到编码的全过程。
二. 分析阶段
分析阶段的主要工作,是围绕use-case图,及use-case规约,从中寻找参与系统的各种对象。
1. 用sequence diagram来描述use-case的执行过程(包括主流程及备选流程):
方法:a.从参与者开始,寻找参与系统流程的对象
b.依照参与者 -> 边界类 -> 控制类 -> 生命周期类 -> 实体对象 的次序,区分各对象的职责,将参与系统的各种对象,依次添加到sequence中。
c.从后向前验证序列,检查每个对象是否拥有它提供服务所必需的信息。如果没有,需要重新考虑对象的职责划分,确保每个对象有相应的方法或通过调用能够找到必需的信息。
注:分析阶段,不应涉及到具体技术。关注的重点还是系统做什么,而不是怎么做。
思考:1.现有项目,往往技术是已经确定的,这种情况下,分析应该关注什么内容?
2. 生命周期类,是开发中工作量较大的部分,也有很多可选的frmework来支持,常用的有自己实现DAO、hibernate等技术路线。
3. 分析阶段,已经确定了系统中的package。有一点要注意,package一定要根据业务来进行划分,而不是在系统建立时,就根据客户的意见来强行划分包。根据包之间最小依赖关系,来划分包,并注意一定要避免包之间的相互依赖。
三. 从技术的角度考虑系统技术方法及软件构架
主要工作是为边界类、实体类、控制类选择候选技术。但往往项目已经是确定了技术路线的,比如我们公司项目已经规定使用J2EE技术。但即便如此,也有很多地方需要考虑。比如是使用JMS通信,还是自己实现Observer。是使用EJB,还是不使用。个人认为,使用EJB一定要慎重。
这部分工作需要系统设计经验比较丰富的人来做,因为技术路线一时选错,对系统后期带来的不良影响是十分大的。
根据可扩展性、可维护性、可靠性、可伸缩性的综合考虑,来确定系统的构架。
四. 设计阶段
设计阶段,同样围绕use-case图及use-case描述来进行,和分析阶段的不同,是加入了选定的具体技术,同时使用面向对象的理论及设计模式等,对系统进行设计。
使用UML的建模工具,在设计阶段产生的产品,主要有两大作用:一是便于交流,也便于修改。二是便于进度安排和工作分配。
五. 实现阶段
实现阶段是针对设计阶段的产品,编码来进行系统的实现。
工具选择:ArgoUML --> (Eclipse + ArgoEclipse) 因为前者不稳定,可能会丢失文件,但是和Eclipse整合后,稳定很多,起码不会存盘失败了。
已经解决:从UML1.4到XMI的正向生成,ArgoUML支持该功能。
待解决1,从xmi到php代码的正向生成。
待解决2,从修改过的php代码反向同步回到xmi文件。
已经解决:从XMI到UML1.4的方向倒入,ArgoUML支持该功能。
1. 需求阶段使用use-case图描述需求
顶层use-case:粗粒度地描述系统,给出系统的概况
细分use-case:将顶层use-case细化
Use_case图的方法是:从参与者开始寻找用例,用use-case diagram来表示参与者与用例之间的关系。
Use-case描述的方法是:就是use-case规约,用详尽的文字来描述用例的执行流程(包括主业务流程及所有分支流程及异常流程)。
Active diagram:可选,类似业务流程图,业务比较复杂时,用流程图来描述业务。
注:use-case图和use-case描述,是整个系统设计的主要产品,今后的分析和设计,都将围绕这两个产品进行。以前看过的好多资料,都没有讲到这一点,use-case的作用非常大,将贯穿分析到设计到编码的全过程。
二. 分析阶段
分析阶段的主要工作,是围绕use-case图,及use-case规约,从中寻找参与系统的各种对象。
1. 用sequence diagram来描述use-case的执行过程(包括主流程及备选流程):
方法:a.从参与者开始,寻找参与系统流程的对象
b.依照参与者 -> 边界类 -> 控制类 -> 生命周期类 -> 实体对象 的次序,区分各对象的职责,将参与系统的各种对象,依次添加到sequence中。
c.从后向前验证序列,检查每个对象是否拥有它提供服务所必需的信息。如果没有,需要重新考虑对象的职责划分,确保每个对象有相应的方法或通过调用能够找到必需的信息。
注:分析阶段,不应涉及到具体技术。关注的重点还是系统做什么,而不是怎么做。
思考:1.现有项目,往往技术是已经确定的,这种情况下,分析应该关注什么内容?
2. 生命周期类,是开发中工作量较大的部分,也有很多可选的frmework来支持,常用的有自己实现DAO、hibernate等技术路线。
3. 分析阶段,已经确定了系统中的package。有一点要注意,package一定要根据业务来进行划分,而不是在系统建立时,就根据客户的意见来强行划分包。根据包之间最小依赖关系,来划分包,并注意一定要避免包之间的相互依赖。
三. 从技术的角度考虑系统技术方法及软件构架
主要工作是为边界类、实体类、控制类选择候选技术。但往往项目已经是确定了技术路线的,比如我们公司项目已经规定使用J2EE技术。但即便如此,也有很多地方需要考虑。比如是使用JMS通信,还是自己实现Observer。是使用EJB,还是不使用。个人认为,使用EJB一定要慎重。
这部分工作需要系统设计经验比较丰富的人来做,因为技术路线一时选错,对系统后期带来的不良影响是十分大的。
根据可扩展性、可维护性、可靠性、可伸缩性的综合考虑,来确定系统的构架。
四. 设计阶段
设计阶段,同样围绕use-case图及use-case描述来进行,和分析阶段的不同,是加入了选定的具体技术,同时使用面向对象的理论及设计模式等,对系统进行设计。
使用UML的建模工具,在设计阶段产生的产品,主要有两大作用:一是便于交流,也便于修改。二是便于进度安排和工作分配。
五. 实现阶段
实现阶段是针对设计阶段的产品,编码来进行系统的实现。
工具选择:ArgoUML --> (Eclipse + ArgoEclipse) 因为前者不稳定,可能会丢失文件,但是和Eclipse整合后,稳定很多,起码不会存盘失败了。
已经解决:从UML1.4到XMI的正向生成,ArgoUML支持该功能。
待解决1,从xmi到php代码的正向生成。
待解决2,从修改过的php代码反向同步回到xmi文件。
已经解决:从XMI到UML1.4的方向倒入,ArgoUML支持该功能。
订阅:
博文 (Atom)