关于作者

姓名:

性别:男

出生日期:--

地区:上海

联系电话:

QQ:--

婚否:未婚
用户名:wenhsuan
笔名:wenhsuan
地区: 上海
行业:其他

日历  

快速登录

+ 用户名:
+ 密 码:

在线留言



链接

访问统计:
文章个数:26
评论个数:3
留言条数:1




Powered by BlogDriver 2.1

wenhsuan的博客

 

欢迎访问wenhsuan的技术博客:Linux & Embedded

文章

这个博客暂停更新,请大家到新博客访问最新内容
这个博客暂停更新,请大家到新博客访问最新内容

新博客: wenhsuan's space

wenhsuan

- 作者: wenhsuan 2006年07月22日, 星期六 18:00  回复(0) |  引用(1) 加入博采

gdb中list命令用不了的问题
这几天gdb中list命令老是用不了,在网上查了一下,终于解决了. 问题是: (gdb) list 1 ../sysdeps/i386/elf/start.S: No such file or directory. in ../sysdeps/i386/elf/start.S 解决方法是: gcc编译时加上-g参数: gcc -g my.c -o my 我看的贴子在:http://www.chinalinuxpub.com/bbs/showthread.php?t=45487

- 作者: wenhsuan 2006年07月8日, 星期六 14:21  回复(0) |  引用(1) 加入博采

学习嵌入式Linux系统的笔记和体会(转)
一个典型的桌面Linux系统包括3个主要的软件层---linux内核、C库和应用程序代码。

内核是唯一可以完全控制硬件的层,内核驱动程序代表应用程序与硬件之间进行会话。内核之上是C库,负责把POSIX API转换为内核可以识别的形式,然后调用内核,从应用程序向内核传递参数。应用程序依靠驱动内核来完成特定的任务。

在设计嵌入式应用的时候,可以不按照这种层次,应用程序越过C库直接和内核会话,或者把应用和内核捆绑在一起,甚至可以把应用写为内核的一个线程,在内核中运行,虽然这样在移植上带来了困难,但考虑嵌入式系统对尺寸要求小的特点,是完全可行的。不过我们使用三层软件结构的模式来学习嵌入式linux将会是我们认识更清晰,简单可行并使应用具有弹性。

快速入门

最简单的建立嵌入式Linux应用的方法就是从我们使用的桌面Linux入手,安装一个喜爱的版本,把我们的某个应用作为初始化的一部分,框架就算完成了。

当然,嵌入式linux应用远比我们的桌面版本功能简单专一,它也许就是一个用于足彩的终端机,或是一个数码音频播放器,这些系统除了使用嵌入式CPU外,仅仅再需要一个串口,网口等少量的输入输出接口就可以完成它们特定的应用了。

在软件上,它可以按照三层的概念由内核装载器,定制的内核和较少的为特定任务设计的静态连接的应用程序组成。之所以使用静态连接的应用程序,是因为少量的静态连接程序所要的存储空间,比同样数量的动态连接的程序所占的空间小,这个平衡点需要我们在实际开发中去获取。也许你正在设计的是个PDA,它的应用程序较多,那么你很可能就要使用动态连接程序来减少存储空间。在你的/bin或者/sbin目录下,用厂列表看看bash,ifconfig,vi...,也许只用几十K,当你运行 ldd /bin/bash 时,你会看到它们都和好几个库文件相连。好了,这样看来,我们得把PC想像成一个嵌入式硬件平台,再重新制作一个特定功能的嵌入式linux。

基础知识



再进行实际操作之前,先来搞清楚几个基础知识。

内核装载器Loader,它的作用是把内核从外部存储器,移动到内存中。它只作这个事情,一旦完成了调入内核的工作,Loader就跳转到内核位置开始执行。不同架构有不同的 Loader,在x86结构的PC上,通常使用的loader有LILO,GRUB,syslinux,syslinux在嵌入式linux中也同样工作。其他非x86架构的应用中,你必须使用专门的loader,或者自己编写loader来装入内核。也有不使用loader的情况,系统加电以后,内核直接从烧录有映象的Flash上开始执行。

内核,一旦内核开始执行,它将通过驱动程序初始化所有硬件,这可以从我们的pc机监视器的输出看出来,每个驱动程序都打印一些有关它的信息。初始化完成后,计算机就准备运行嵌入式应用。也许一个,也许是多个应用程序组成了嵌入式应用,但通常首先调用的是init(通过loader 向核心传入init=/program 可以定制首先运行的程序)。桌面linux中,init会读取/etc/inittab文件,来决定执行级别和哪些脚本和命令。嵌入式应用中,可以根据实际的情况决定是否使用标准的init执行方式,也许这个init是个静态程序,它能够完成我们的嵌入应用的特定任务,那完全不用考虑inittab了。

initrd文件系统,initrd以一种把内核从存储介质装入到内存的相同的机制来装入一个小型文件系统。这个文件系统最好是以压缩的方式存储在介质上的,解压缩到RAM盘上。通过使用initrd,包含有核心驱动和启动脚本的小文件系统,就可以直接从介质上和内核一起启动起来,内核届压缩这个文件系统,并执行这个文件系统上叫做/linuxrc的脚本文件,这个脚本通常会把启动过程中所需要的驱动程序装入。脚本退出以后,initrd文件系统也卸下了,启动过程进入真正初始化过程。对于嵌入式来讲,可以将需要的应用软件都运行在这个initrd文件系统上,只要/linxrc文件不结束,内核启动过程的其他部分就不会继续。

做个试验:

cp /boot/initrd-2.4.20.img /tmp

cd /tmp

mv initrd-2.4.2-.img initrd.img.gz

gunzip initrd.img.gz

mount -o loop initrd.img /mnt

cd /mnt

ls

cat linuxrc 可以看到里面执行了加载了两个模块的操作,你在启动linxu的时候会看见屏幕打印信息。

  入门试验,制作一个简单的应用



我们使用一张软盘启动一台假象的只有一个串口,键盘输入,显示输出的x86架构的linux系统,执行的特定应用就是运行minicom,通过串口拨号。需要软件: minicom-xx.src.tar.gz 和 syslinux-xx.tar.gz,xx代表版本号,开始之前,在主目录建立一个目录,来释放这两个软件包:

cd

mkdir -p project/minilinux

cd project/minilinux

tar zxvf minicom-xx.src.tar.gz

tar zxvf syslinux-xx.tar.gz

1、裁减linux内核(需要系统安装内核文件包)

配置内核的时候,我们需选择这些:摸块编入内核,386处理器、物理内存off、支持ELF、标准PC软盘、支持RAM盘(4096)、支持 initial RAM disk (initrd)、虚你终端、虚拟终端控制台、标准串口、ext2文件系统、控制台驱动,VGA text console、DOS FAT、MSDOS文件系统,其他的都可以不要,这样内核编出来较小。

步骤:

cd /usr/src/linux

make mrproper

make xconfig

make dep && make bzImage

得到 /usr/src/linux/arch/i386/boot/目录的内核文件bzIamge。

2、编译一个静态的minicom ,把它作为将来的linuxrc

cd minicom-xx/src

vi Makefile

修改下面这行

minicom: $(minicom_OBJECTS) $(minicom_DEPENDENCIES)

rm -f minicom 下面的行加上 -static,连接为静态程序

(LINK) -static $(minicom_LDFLAGS) $(minicom_OBJECTS) $(minicom_LDADD) $(LIBS)

vi minicom.c

找到 if (real_uid==0 && dosetup==0 ) 删除这个判断条件语句,主要是用于权限判断的,因为这个嵌入应用不关注权限问题,否则会出错。

make

得到可执行程序,用ldd 检查一下是不是静态程序。

3、准备initrd压缩文件image.gz

dd if=/dev/zero of=image bs=1k count=4096

losetup /dev/loop0 image

mke2fs -m 0 /dev/loop0

mounmt -t ext2 /dev/loop0 /mnt/

mkdir -p /mnt/dev

mkdir -p /mnt/usr/share/terminfo/l/

cd /dev

cp -a consle null tty tty0 zero mem /mnt/dev

cp -P /usr/share/terminfo/l/linux /mnt/usr/share/terminfo/l/linux

cp ~/project/minilinux/mincom/src/minicom /mnt/linuxrc

umount /mnt

losetup -d /dev/loop0

sync

gzip -9 image

4、制作软盘引导,并拷贝文件 bzimage image.gz 到软盘



A.使用grub

fdformat /dev/fd0

mke2fs /dev/fd0

mount /mnt/fd0 /mnt/floppy

mkdir -p /mnt/floppy/boot/grub

cp /boot/grub/stage1 /boot/grub/stage2 /mnt/floppy/boot/grub

执行 grub,在软盘上创建引导

grub > root (fd0)

grub > setup (fd0)

grub > quit

cp /usr/src/linux/arch/i386/boot/bzImge /mnt/floppy

cp ~/porject/minilinux/image.gz /mnt/floppy

编辑 /mnt/floppy/boot/grub/grub.conf

default =0

timeout-=10

title minilinux

root (fd0)

kernel /bzImage

initrd /image.gz

卸下软盘

umount /mnt/floppy

B. 使用syslinux

fdformat /dev/fd0

mkfs.msdos /dev/fd0

mount -t msdos /dev/fd0 /mnt/floppy

cp /usr/src/linux/arch/i386/boot/bzImge /mnt/floppy

cp ~/porject/minilinux/image.gz /mnt/floppy

cp syslinux-xx/ldlinxu.sys /mnt/floppy

cat > /mnt/floppy/syslinux.cfg

LABEL linux

KERNEL bzimage

APPEND initrd=image.gz

umont /mnt/floppy

syslinux-xx/syslinux /dev/fd0

sync

5、用软盘启动计算机,如果幸运,minicom的运行画面出现在屏幕上。

到此,我们的单应用嵌入式linux做好了,但它还很简陋,没有什么实际用途,但通过这个实验,可以了解嵌入式系统的大致结构和开发过程。在进行实际的嵌入式开发时,通常要在PC机上借助嵌入式linux开发工具包,如:uclinux,bluecat等,对相应的硬件平台(目标机)进行软件编写编译,调试成功后,将内核及应用程序写入到目标机的存储器中,从而完成整个应用。

- 作者: wenhsuan 2006年07月4日, 星期二 13:13  回复(0) |  引用(1) 加入博采

VMware与Redhat的网络设置兼容性

VMware与Redhat的网络设置兼容性

VMware4.5以上和Redhat 9以上之间,网卡驱动有些不兼容:
Redhat 9.0作Guest OS时,激活虚拟网卡时,总是提示
诸如 "Determining IP information for eth0... failed; no link present. Check cable?"

原因貌似是VMware提供的虚拟网卡驱动有一点点问题,可以通过下面方法解决:
以root权限,编辑 /etc/sysconfig/network-scripts/ifcfg-eth0和
              /etc/sysconfig/networking/devices/ifcfg-eth0

在每个文件中添加:
   check_link_down () {
       return 1;
   }

然后激活虚拟网卡eth0  应该就可以了

另外,VMware的几种网络模式,选择NAT (网络地址转换模式) 就行了,
这个对于让Guest OS访问Internet是最简单的,对应"VMnet8"
                              

- 作者: wenhsuan 2006年06月15日, 星期四 19:50  回复(0) |  引用(1) 加入博采

什么叫网关的精解(超经典)

转自(协议分析论坛)
计算机主机网关的作用是什么?
假设你的名字叫小不点,你住在一个大院子里,你的邻居有很多小伙伴,在门口传达室还有个看大门的李大爷,李大爷就是你的网关。当你想跟院子里的某个小伙伴玩,只要你在院子里大喊一声他的名字,他听到了就会回应你,并且跑出来跟你玩。
  但是你不被允许走出大门,你想与外界发生的一切联系,都必须由门口的李大爷(网关)用电话帮助你联系。假如你想找你的同学小明聊天,小明家住在很远的另外一个院子里,他家的院子里也有一个看门的王大爷(小明的网关)。但是你不知道小明家的电话号码,不过你的班主任老师有一份你们班全体同学的名单和电话号码对照表,你的老师就是你的DNS服务器。于是你在家里拨通了门口李大爷的电话,有了下面的对话:

小不点:李大爷,我想找班主任查下小明的电话号码行吗?

李大爷:好,你等着。(接着李大爷给你的班主任挂了一个电话,问清楚
了小明的电话)问到了,他家的号码是211.99.99.99

小不点:太好了!李大爷,我想找小明,你再帮我联系一下小明吧。

李大爷:没问题。(接着李大爷向电话局发出了请求接通小明家电话的请
求,最后一关当然是被转接到了小明家那个院子的王大爷那里,然后王大
爷把电话给转到小明家)

  就这样你和小明取得了联系。

  至于DHCP服务器嘛,可以这样比喻:

  你家院子里的居民越来越多了,传达室李大爷那里的电话交换机已经不能满足这么多居民的需求了,所以只好采用了一种新技术叫做DHCP,居民们开机的时候随机得到一个电话号码,每一次得到的号码都可能会不同。

你家门口的李大爷:就是你的网关
你的班主任:就是你的DNS服务器
传达室的电话交换机:就是你的DHCP服务器

同上,李大爷和王大爷之间的对话就叫做路由。

  另:如果还有个小朋友叫做小暗,他住的院子看门的是孙大爷,因为小暗的院子刚盖好,孙大爷刚来不久,他没有李大爷和王大爷办公室的电话(李大爷和王大爷当然也没有他的电话),这时会有两种情况:
1、居委会的赵大妈告诉了孙大爷关于李、王两位大爷的电话(同时赵大妈也告诉了李、王关于孙的电话),这就叫静态设定路由
2、赵大妈病了,孙大爷自己到处打电话,见人就说:“我是小暗他们院子管电话的”,结果被李、王二位听到了,就记在了他们的通讯录上,然后李、王就给孙大爷回了个电话说:“我是小明(小不点)他们院子管电话的”,这就叫动态设定路由

然后有一天小不点要找小暗,结果自然是小不点给李大爷打电话说:“大爷,我找小暗”(这里省略了李大爷去查小暗电话的过程,假设他知道小暗的电话),李大爷一找通讯录:“哦,小暗的院子的电话是孙大爷管着的,要找小暗自然先要通知孙大爷,我可以通知王大爷让他去找孙大爷,也可以自己直接找孙,那当然是自己直接找孙方便了”,于是李大爷给孙大爷打了电话,然后孙大爷又把电话转到了小暗家。

  这里李大爷的通讯录叫做路由表。
  李大爷选择是自己直接找孙大爷还是让王大爷帮忙转接叫做路由选择。

  李大爷之所以选择直接找孙大爷是有依据的,因为他直接找孙大爷就能一步到位,如果要王大爷转接就需要两步才能完成,这里的“步”叫做“跳数”,李大爷的选择遵循的是最少步骤(跳数)原则(如果他不遵守这个原则,小不点可能就会多等些时间才能找到小暗,最终结果可能导致李大爷因工作不力被炒鱿鱼,这叫做“延时太长,选路原则不合理,换了一个路由器”)

  当然,事情总是变化的,小不点和小明吵架了,这些天小不点老是给小暗打电话,小明心里想:“操,他是不是在说我坏话啊?”于是小明决定偷听小不点和小暗的通话,但是他又不能出院子,怎么办呢?小明做了这样一个决定:

  首先他告诉自己院里管电话的王大爷说:“你给李大爷打个电话说小暗搬到咱们院子了,以后凡是打给他的电话我来接”,王大爷没反映过来(毕竟年纪大了啊!)就给李大爷打了电话,说:“现在我来管理小暗的电话了,孙已经不管了”,结果李大爷就把他的通讯录改了,这叫做路由欺骗。

  以后小不点再找小暗,李大爷就转给王大爷了(其实应该转给孙大爷的),王大爷收到了这个电话就转给了小明(因为他之前已经和小明说好了),小明收到这个电话就假装小暗和小不点通信。因为小明作贼心虚,害怕明天小不点和小暗见面后当面问他,于是通信断了之后,又自己以小不点的名义给小暗通了个电话复述了一遍刚才的话,有这就叫数据窃听

再后来,小不点还是不断的和小暗联系,而零落了小明,小明心里嘀咕啊:“我不能总是这样以小暗的身份和小不点通话啊,外一有一天露馅了怎么办!”于是他想了一个更阴险的招数:“干脆我也不偷听你们的电话了,你小不点不是不给我打电话吗!那我让你也给小暗打不了,哼哼!”,他怎么做的呢?我们来看:

  他联系了一批狐朋狗友,和他们串通好,每天固定一个时间大家一起给小暗院子传达室打电话,内容什么都有,只要传达室的孙爷爷接电话,就会听到“打雷啦,下雨收衣服啊!”、“人是人他妈生的,妖是妖他妈生的”、“你妈贵姓”等等,听的脑袋都大了,不听又不行,电话不停的响啊!终于有一天,孙爷爷忍不住了,大喊一声:“我受不了拉!!!!”,于是上吊自杀了!

  这就是最简单的DDOS攻击,孙爷爷心理承受能力弱的现象叫做“数据报处理模块有BUG”,孙爷爷的自杀叫做“路由器瘫痪”。如果是我,就会微笑着和他们拉家常,例如告诉他们“我早就听了天气预报,衣服10分钟前已经收好了”或者“那你妈是人还是妖”或者“和你奶奶一个姓”等等,我这种健全的心理叫做“健壮的数据报处理,能够抵御任何攻击”

  孙爷爷瘫了之后,小不点终于不再给小暗打电话了,因为无论他怎么打对方都是忙音,这种现象叫做“拒绝服务”,所以小明的做法还有一个名字叫做“拒绝服务攻击”。

小明终于安静了几天,...

  几天后,小明的院子来了一个美丽的女孩,名字叫做小丽,小明很喜欢她(小小年纪玩什么早恋!)可是小丽有个很帅的男朋友,小明干瞪眼没办法。当然这里还是要遵循上面的原则:小丽是不能出院子的。那个男的想泡小丽自然只能打电话,于是小明又蠢蠢欲动了:
还记得王爷爷是院子的电话总管吗?他之所以能管理电话是因为他有一个通讯录,因为同一个院子可能有2个孩子都叫小明,靠名字无法区分,所以通讯录上每一行只有两项:

门牌   电话
一号门   1234567 (这个是小明的)
二号门   7654321 (这个是小丽的)
......

  王爷爷记性不好,但这总不会错了吧(同一个院子不会有2个“二号门”吧)?每次打电话人家都要说出要找的电话号码,然后通过通讯录去院子里面敲门,比如人家说我找“1234567”,于是王爷爷一比较,哦,是一号门的,他就去敲一号门“听电话”,如果是找“7654321”,那他就找二号门“听电话”。

  这里的电话号码就是传说中的“IP地址”
  这里的门牌号就是传说中的网卡的’MAC‘地址(每一块网卡的MAC地址都是不一样的,这是网卡的制造商写死在网卡的芯片中的)

  小明心里想“奶奶的,老子泡不到你也别想泡”,于是他打起了王爷爷通讯录的主意,经过细心的观察,周密的准备,他终于发现王爷爷有尿频的毛病(毕竟是老人啊...),终于在一个月黑风高的白天,王爷爷去上厕所了,小明偷偷的摸进传达室,小心翼翼的改了王爷爷的通讯录......

  过了几天,小丽的男朋友又给小丽打来了电话,对方报的电话是“7654321”,王爷爷一看通讯录,靠:

门牌   电话
一号门   1234567 (这个是小明的)
一号门   7654321 (注意:这个原来是小丽的,但是被小明改了)
......

  王爷爷不知道改了啊,于是就去找一号门的小明了,小明心里这个美啊,他以小丽父亲的口吻严厉的教训了那个男的和小丽之间不正当的男女关系,结果那个男的恭恭敬敬的挂了电话。当然小丽并不知道整个事情的发生...

  这里小明的行为叫做“ARP欺骗”(因为在实际的网络上是通过发送ARP数据包来实现的,所以叫做“ARP欺骗”),王爷爷的通讯录叫做“ARP表”

  这里要注意:王爷爷现在有两个通讯录了,一个是记录每个院子传达室电话的本本,叫做“路由表”,一个是现在说的记录院子里面详细信息的本本,叫做“ARP表”。

  有句命言是“人们总是在追求完美的,尽管永远也做不到”(请记住这句话,因为这是一个大名人--也就是我,说的)

  王爷爷的制度中有一条是这么写的“每个月要重新检查一下门牌号和电话的对应本(也就是ARP表)”,这个动作叫做“刷新ARP表”,每个月的时间限制叫做“刷新ARP表的周期”。这样小明为了让那个男的永远不能找到小丽,之后每个月都要偷偷改一次那个通讯录,不过这样也是不得不做的事啊!
  补充一点,小明是很聪明的,如果通讯录(ARP表)被改成了这样:

门牌(MAC)   电话(IP)
一号门         1234567 (这个是小明的)
二号门         1234567 (注意:这个被小明改了,但是他一时头晕改错了)
......

  就会是计算机就会弹出一个对话框提示“出现重复的IP地址”,最终会导致王爷爷不知所措,于是通知一号门和二号门,你们的电话重复了。这样小丽就知道有人在破坏她的好事,这个现象叫做“骗局被揭穿了”

小不点知道了小明偷听他和小暗的电话,于是就和小暗约定好了密码。小不点在家里把要说的加密了之后告诉小暗。土豆-〉星期三,地瓜-〉请客,笨蛋-〉小不点家。于是小不点告诉小暗:土豆笨蛋地瓜。小明听了???不懂。。。。郁闷了。。。这是加密。
除此之外,小丽也知道了小明改他家的电话号码了。于是王爷爷就登门一个一个把电话和门牌号记下来。并且藏起来不允许外人修改,只能自己有钥匙(密码)。这是ip地址和MAC地址绑定。当有人改了电话号码的时候,就得找王爷爷改。麻烦是麻烦了,但是安全了。不过小明偷偷的把王爷爷的钥匙偷配了一把(盗窃密码成功),于是他还可以修改。这样么,就这样了。

- 作者: wenhsuan 2006年05月11日, 星期四 15:50  回复(0) |  引用(1) 加入博采

Windows CE.NET进程、线程和调度管理

Windows CE.NET是一个抢占多任务操作系统,在调度过程中,内核的调度系统包含一个当前所有进程中线程的优先级列表,并对所有的线程按优先级排列顺序。当中断发生时,调度系统重新安排所有线程的排列顺序。

一个进程是一个正运行的应用程序的实例。它由两个部分组成:一个是操作系统用来管理这个进程的内核对象。另一个是这个进程拥有的地址空间。这个地址空间包含应用程序的代码段、静态数据段、堆、栈,非XIPExecute In PlaceDLL。从执行角度方面看,一个进程由一个或多个线程组成。一个线程是一个执行单元,它控制CPU执行进程中某一段代码段。一个线程可以访问这个进程中所有的地址空间和资源。线程是Windows CE操作系统分配CPU时间的基本单位。一个进程最少包括一个线程来执行代码,这个线程又叫做主线程。

Windows CE.NET最多支持32个进程同时运行。这是由整个系统分配给所有进程的总地址空间决定的。低于Windows CE 4.0版本(也就是低于.NET的版本)的CE操作系统,总进程空间从0x0000 00000x4200 0000 ,每32MB地址空间为一个槽(Slot),共33个槽。当一个进程启动时,内核选择一个没有被占用的槽作为这个进程的地址空间。其中0x0000 00000x01FF FFFF这个槽称为Slot 0。每个进程在即将得到CPU控制权时,将整个地址映射到Slot 0[7,12]

线程有五中状态,分别为运行、挂起、睡眠、阻塞、终止。当所有线程全部处于阻塞状态时,内核处于空闲模式(Idle mode),这时对CPU的电力供应将减小。

Windows CE.NET不像其他Windows操作系统将进程分为不同的优先级类,Windows CE.NET只将线程分为256个优先级。0优先级最高,255最低。在需要时进程创建线程,附加线程的数量只受限于设备中RAM的可用容量。在Windows CE中,通过CreateThread()函数可以创建线程,通过GetThreadPriority()SetThreadPriority()可以获得和改变线程优先级。挂起一个线程使用SuspendThread()函数。恢复线程使用ResumeThread()函数。

同时Windows CE提供了线程同步机制,线程同步的有些解决办法运行在用户模式,有些运行在内核模式。互锁和临界区运行在用户模式,事件对象,互斥对象运行在内核模式。同步机制还包括信号,消息队列等。

- 作者: wenhsuan 2006年04月14日, 星期五 15:24  回复(0) |  引用(1) 加入博采

WinCE下的多线程
 WinCE下的多线程

一、创建进程

线程由两部分构成.线程的内核对象和线程堆栈
HANDLE handle1;
static DWORD ThreadProc(PVOID pArg);
CListBox * pLstOne;
DWORD dwThreadId1;

handle1=CreateThread(NULL,0,ThreadProc,pLstOne,0,&dwThreadId1);
if(!handle1)
 {
  AfxMessageBox(_T("Thread 1 Create fail"));
 }

CreateThread函数创建了一个新的线程,返回值为新线程的句柄。原型
HANDLE CreateThread(
  LPSECURITY_ATTRIBUTES lpsa,
  DWORD cbStack,
  LPTHREAD_START_ROUTINE lpStartAddr,
  LPVOID lpvThreadParam,
  DWORD fdwCreate,
  LPDWORD lpIDThread
);

Parameters

lpsa
[in] Ignored. Must be NULL.  //线程安全指针,不支持
cbStack
[in] Ignored unless the STACK_SIZE_PARAM_IS_A_RESERVATION flag is used; in that case the cbStack parameter specifies the virtual memory reserved for the new thread.

When ignored, the default stack size for a thread is determined by the linker setting /STACK.

//为自己所用堆栈分配的地址空间大小,不支持

lpStartAddr
[in] Long pointer to the application-defined function of type LPTHREAD_START_ROUTINE to be executed by the thread; epresents the starting address of the thread. For more information on the thread function, see ThreadProc.
//线程函数地址

Note   It is invalid to set lpStartAddr to NULL. If this value is passed in the parameter, the function will return ERROR_INVALID_PARAMETER.
lpvThreadParam
[in] Long pointer to a single 32-bit parameter value passed to the thread. 
//传入线程函数的参数
fdwCreate
[in] Specifies flags that control the creation of the thread.

//控制线程创建的附加标志

The following table shows the values for this parameter.

ValueDescription
CREATE_SUSPENDEDThe thread is created in a suspended state and does not run until the ResumeThread function is called.

The thread can be run immediately after creation if the flag is not specified.

STACK_SIZE_PARAM_IS_A_RESERVATIONThe cbStack parameter specified the maximum stack size instead of being ignored.

This is a Windows CE only flag for the CreateThread function and is used to reserve the stack size for the thread created.

By default, a thread is created with 64 KB stack reserved. You can use this flag to increase the stack size for the new thread that was created.

lpIDThread
[out] Long pointer to a 32-bit variable that receives the thread identifier.
//新线程的id值。

If this parameter is NULL, the thread identifier is not returned.

线程函数必须写成这样的形式:
DWORD CThreadAsynDlg::ThreadProc(PVOID pArg)
{
   CListBox * pListBox;
   pListBox=(CListBox*)pArg; //这里对参数进行强制类型转换
}

线程函数应该尽可能使用函数参数和局部变量.
DWORD WINAPI FirstThread(PVOID pvParam) {
int x=0;
DWORD dwThreadID();
HANDLE hThread=createThread(NULL,0,SecondThread,(PVOID) &x,0, &dwThreadId);
closeHandle(hThread);
return(0);
}
DWORD WINAPI SecondThread(PVOID pvParam) {
//do some
*((int *) pvParam)=5;
.
.
return(0);
}

二、线程同步
方法1: 利用事件对象同步,具体在绿皮的evc高级编程那本书的p164。

简单地说是这样的步骤
利用CreateEvent创建一个事件对象,将参数bManualReset设为FALSE,参数bInitialState也设为FALSE,此时事件对象状态为“未标识”。然后在线程里通过调用WaitForSingleObject来等待事件被标识。此时,只要主线程调用SetEvent函数,将事件对象设置成已标识。那么线程里的WaitForSingleObject函数便会返回,继续执行,同时将事件对象状态设置成未标识。

1. 添加一个线程同步事件句柄 HANDLE m_hSynEvent;
2. 创建一个线程同步事件对象,一般在窗口的OnInitDialog事件中
    m_hSynEvent = CreateEvent(NULL, false, true, NULL);
    在窗口关闭时候,要关闭线程同步事件对象:
    CloseHandle(m_hSynEvent);
3. 利用WaitForSingleObject来阻塞。具体看绿皮书165页。
if(WaitForSingleObject(pDlg->m_hSynEvent,INFINITE)==WAIT_OBJECT_0)  
//上面的WaitForSingleObject实现阻塞,如果m_hSynEvent被标识了,那么才运行下面的代码。
 {
  for(int i=0;i  {
   pDlg->m_incNum++;
   pDlg->m_aGlobalData[i] = pDlg->m_incNum;
   Sleep(5);
  }

  for(i=0;i  {
   _itow(pDlg->m_aGlobalData[i],buffer,10);
   pListBox->AddString(buffer);
  }
 }

标记事件对象
函数原型: BOOL SetEvent(HANDLE hEvent); //hEvent是事件对象的句柄
例如这样:SetEvent(pDlg->m_hSynEvent);
SetEvent是把事件对象设置成已标识状态。

BOOL ResetEvent(HANDLE hEvent);
此函数用于把事件对象设置成未标识状态。

- 作者: wenhsuan 2006年04月14日, 星期五 14:27  回复(0) |  引用(1) 加入博采

Treo 700w正式发布,兼谈为何Palm会输给Windows Mobile(转)

Palm、Microsoft和Verizon在北京时间今天早上举行了联合新闻发布会,发布了新的Treo 700w手机,运营商是Verizon,操作系统是Windows Mobile 5.0。BillG代表Microsoft参加了新闻发布会,代表Palm的是Ed Colligan,不是Jeff Hawkins。

也就是短短两年多前的2003年6月,那时Palm收购了Handspring,Jeff Hawkins回到了他创建的Palm,人们纷纷将其与重回苹果的Steve Jobs相提并论。还记得Treo 600发布时候的轰动,在纽约的发布会上,Jeff Hawkins手里拿着Treo 600:

但自那以后,2004第一季度Pocket PC的出货量追平基于Palm OS的PDA,2004年底微软首次在手持设备市场上超越Palm占据最大份额,2005年初Sony宣布不再生产(基于Palm OS)的PDA,直到今天,Palm开始在自己的设备中使用微软的嵌入式操作系统,这在即便是一年前也是令人难以想象的。而曾几何时,Palm OS在九十年代末占据了超过50%的市场份额,拥有压倒性的优势。

微软已经可以把Palm从竞争对手名单中划掉了——正如以前Netscape、VisiCalc、WordPerfect一样——尽管这个名单不断的有新的加进来,例如Google、RIM。

Windows Mobile赢得对Palm的竞争是无可挑剔的,尽管Palm的用户始终嘲笑微软不懂的嵌入式系统,嘲笑微软用写PC操作系统的方式来写嵌入式的OS,嘲笑Pocket PC上的软件不如Palm多。没有人可以指责微软利用了垄断优势来排挤Palm:微软并没有在Windows XP里预装ActiveSync。尽管Palm的开发者和拥趸始终认为微软缺乏创新、缺乏技术、缺乏对市场的认识,但事实上,缺乏这些的是Palm——否则微软不可能赢得对Palm的竞争。终端用户市场里,用户是用脚投票的,掏不掏钱全在买者;开发者社区中,程序员总是喜欢给更多的用户写软件,也总是喜欢在容易开发移植的平台上写软件;销售渠道中,零售商总是喜欢代理更好卖的产品,电信运营商也总是希望co-branding受欢迎的设备。

在移动和嵌入式领域,微软是一个后起的公司。后起的公司要取代已经取得成功的公司,一定要等到他们犯错误。李彦宏说Netscape犯了1.5个错误,那Palm呢?

我觉得Palm有点过于相信来自于早期成功的经验

九十年代末,手持设备还处于黑白显示、内存只有2MB或更少的状态。当时的Palm OS能够螺蛳壳里做道场,在有限的硬件上实现了丰富的功能。但很多Palm的开发者和支持者就此相信,手持设备就是应该是简单的,不应该把台式机上的事情拿到手持设备上来做。

Palmer认为微软在Pocket PC上采用和desktop windows类似的编程模型(消息机制、API、动态链接库、TCP/IP等)是荒谬的,他们嘲笑微软不懂handheld device。没错,在当时的硬件上,类似桌面系统的编程模型会造成很大的负担。但Palmer没有看到硬件的发展,事实上,时至今日,PDA的内存已经达到64MB或者128MB,CPU的时钟频率已经和Pentium II时代的台式机CPU一样块,与此同时,同样的编程模型带来的好处逐渐显现出来——VC和eVC,以及.NET和.NET CF——桌面的程序员可以很轻易的转移到Windows Mobile上。别说我事后诸葛亮,其实2003年的时候我在hi-pda和人为此争得面红耳赤说的就是同样的话。

对于程序员来说,开发Palm的应用程序相对困难——仍然需要用C语言来写。这和桌面上的快速开发潮流是不相称的。在桌面上,九十年代末就已经是Delphi和Visual Basic大行其道了,到了二十一世纪初,Java也进入桌面,然后就是.NET。程序员总是愿意用更方便的方法来写程序。喜欢炫耀自己“能够修改内核”的毕竟是少数。Palm曾经可以搭上Java的班车。Java的确也有了一个Palm上的VM(叫做Personal Java),我在我当时的Palm Vx上用过,速度出奇的慢。原因也很简单:CPU不行,内存太小。

Palmer过于相信过去的成功经验,他们坚信Palm的哲学是正确的:handheld device上的程序就应该是短小、简洁的。当时,在Palm OS 3.1上,一个程序如果超过100k就会被认为是丑陋的;而如果一个程序小于20kb,就会得到很好的评价。Palmer嘲笑Pocket PC上的程序的体积庞大,动辄500k或更大。但事实上,程序短小带来的好处被闪存的迅速降价完全抵消了。

Palmer在很长的一段时间里认为Pocket PC的彩色是没有必要的,gray scale的Palm足够了。但事实上,没有人能抵挡彩色的诱惑:无论是彩色电视机,还是彩屏手机。Palmer一度还对Palm的电池寿命非常自豪。没错,当时gray scale的Palm的电池寿命至少是在10小时以上的,而Pocket PC的电池通常只能维持两个小时(的确如此,我当时用iPAQ 3630的时候每天都需要充电)。但Palmer并没有很正确地意识到导致Pocket PC的电池寿命过短的主要原因之一是彩色和带背光的屏幕。事实上,当Palm的PDA也用上彩屏以后(例如我曾经用过Palm Tungsten T),电池寿命和Pocket PC相差无几,优势荡然无存。

Palm犯过的错误还包括错过了网络,错过了多媒体,错过了中国市场。受到硬件和编程模型的限制,Palm上始终开发不出类似Pocket PC上的“招商银行掌上银行”的应用。Palmer曾经固执的认为在handheld devices上是不需要上网的(当然,Palm长期的160*160 pixels的分辨率也是无法上网的原因之一)。曾经一度,对于Palm上的音乐播放软件来说,可以后台播放也会成为亮点——而对于Pocket PC来说,完全是与生俱来的。

Palm最终还是进入中国了,大概在2003年的样子,不算太晚,但没有把销售和市场做好,失去了最后的机会。Apple在中国也一直没有做好应该做的事情。人们喜欢iPod,但却不知道去哪里能买到。

- 作者: wenhsuan 2006年04月14日, 星期五 14:25  回复(0) |  引用(1) 加入博采

C语言的精确延时
for循環實現C語言精確延時
                                                 ----- djh2000
   C語言最大的缺點就是實時性差,我在網上到看了一些關於延時的討論,其中有篇文章
   51单片机 Keil C 延时程序的简单研究,作者:InfiniteSpace Studio/isjfk
寫得不錯,他是用while(--i);產生DJNZ 來實現精確延時,后來有人說如果while里面不能放其它語句,否則也不行,用do-while就可以,具體怎樣我沒有去試.所有這些都沒有給出具體的實例程序來.還看到一些延時的例子多多少少總有點延時差.為此我用for循環寫了幾個延時的子程序貼上來,希望能對初學都有所幫助.(晶振12MHz,一個機器周期1us.)
    一. 500ms延時子程序
程序: 
    void delay500ms(void)
       {
  unsigned char i,j,k;
 for(i=15;i>0;i--)
 for(j=202;j>0;j--)
 for(k=81;k>0;k--);
       }
產生的匯編:
    C:0x0800    7F0F     MOV      R7,#0x0F
    C:0x0802    7ECA     MOV      R6,#0xCA
    C:0x0804    7D51     MOV      R5,#0x51
    C:0x0806    DDFE     DJNZ     R5,C:0806
    C:0x0808    DEFA     DJNZ     R6,C:0804
    C:0x080A    DFF6     DJNZ     R7,C:0802
    C:0x080C    22       RET      
計算分析:
    程序共有三層循環
    一層循環n:R5*2 = 81*2 = 162us                  DJNZ  2us
    二層循環m:R6*(n+3) = 202*165 = 33330us          DJNZ  2us + R5賦值 1us = 3us
    三層循環: R7*(m+3) = 15*33333 = 499995us        DJNZ  2us + R6賦值 1us = 3us
    循環外:   5us            子程序調用 2us + 子程序返回 2us + R7賦值 1us  = 5us
    延時總間 = 三層循環 + 循環外 = 499995+5 = 500000us =500ms
計算公式:延時時間=[(2*R5+3)*R6+3]*R7+5
    二. 200ms延時子程序
程序:
void delay200ms(void)
 {
  unsigned char i,j,k;
 for(i=5;i>0;i--)
 for(j=132;j>0;j--)
 for(k=150;k>0;k--);
 }
產生的匯編
C:0x0800    7F05     MOV      R7,#0x05
C:0x0802    7E84     MOV      R6,#0x84
C:0x0804    7D96     MOV      R5,#0x96
C:0x0806    DDFE     DJNZ     R5,C:0806
C:0x0808    DEFA     DJNZ     R6,C:0804
C:0x080A    DFF6     DJNZ     R7,C:0802
C:0x080C    22       RET
    三. 10ms延時子程序
程序:
void delay10ms(void)
 {
  unsigned char i,j,k;
 for(i=5;i>0;i--)
 for(j=4;j>0;j--)
 for(k=248;k>0;k--);
 }
產生的匯編
C:0x0800    7F05     MOV      R7,#0x05
C:0x0802    7E04     MOV      R6,#0x04
C:0x0804    7DF8     MOV      R5,#0xF8
C:0x0806    DDFE     DJNZ     R5,C:0806
C:0x0808    DEFA     DJNZ     R6,C:0804
C:0x080A    DFF6     DJNZ     R7,C:0802
C:0x080C    22       RET      
    四. 1s延時子程序
程序:
void delay1s(void)
 {
  unsigned char h,i,j,k;
 for(h=5;h>0;h--)
 for(i=4;i>0;i--)
 for(j=116;j>0;j--)
 for(k=214;k>0;k--);
 }
產生的匯編
C:0x0800    7F05     MOV      R7,#0x05
C:0x0802    7E04     MOV      R6,#0x04
C:0x0804    7D74     MOV      R5,#0x74
C:0x0806    7CD6     MOV      R4,#0xD6
C:0x0808    DCFE     DJNZ     R4,C:0808
C:0x080A    DDFA     DJNZ     R5,C:0806
C:0x080C    DEF6     DJNZ     R6,C:0804
C:0x080E    DFF2     DJNZ     R7,C:0802
C:0x0810    22       RET
在精確延時的計算當中,最容易讓人忽略的是計算循環外的那部分延時,在對時間要求不高的場合,這部分對程序不會造成影響.

- 作者: wenhsuan 2006年04月2日, 星期日 10:02  回复(0) |  引用(1) 加入博采

J2ME游戏引擎结构

游戏引擎的结构很多,不过基本上都是框架+主循环。J2ME游戏的引擎结构一般由两个class组成,一个是MIDlet,一个是Canvas(或Displayable)。MIDlet类是游戏的框架外壳,负责游戏的启动,停止和退出,并负责命令的监听和处理。Canvas类是屏幕绘图的平台,负责游戏主循环和处理某些按键事件,要么作为特定的低级监控代码事件要么作为抽象的游戏动作,三个主要函数void run(),void render(Graphics g),void keyPressed(int keyCode)。

1。游戏框架

import javax.microedition.midlet.*;

public class GameMIDlet extends MIDlet {

private GameCanvas cgame;
private Command exitCmd = new Command("Exit",Command.EXIT,2);

public GameMIDlet() {
cgame= new gameCanvas();
cgame.addCommand(exitCmd);
cgame.setCommandListener(this);
}

protected void startApp() throws MIDletStateChangeException {
 
  Display.getDisplay(this).setCurrent(cgame);
 
  try{
   Thread mythread = new Thread(cgame);
   mythread.start();
  
  }
  catch(Error e){
   destroyApp(false);
   notifyDestroyed();
  }

 }

public void pauseApp() {
}

public void destroyApp(boolean unconditional) {
Display.getDisplay(this).setCurrent((Displayable)null);
}

public void commandAction(Command c,Displayable d){
    
     if (c == exitCmd) {
           destroyApp(false);
           notifyDestroyed();
         }
        //other commands
        //...

}

2。游戏主循环

Canvas(或FullCanvas,继承自Canvas)类实现Runnable这个接口,然后在其构造函数中创建一个线程,启动其run()函数,而run函数里面就包含了游戏的主循环。

import java.lang.Runnable;
import java.lang.InterruptedException;
import java.lang.Thread;

import javax.microedition.lcdui.*;
import javax.microedition.lcdui.game.*;

public class gameCanvas
extends GameCanvas
implements Runnable {

private boolean mTrucking;
private long mFrameDelay; //帧延时,毫秒为单位

public gameCanvas() {
 
    super(true);
    mFrameDelay = 30;
    init();   
}

public void start() {
 mTrucking = true;


 //开始主线程
 Thread t = new Thread(this);
 t.start();
 }

public void stop() { mTrucking = false; }

public void run() {
 Graphics g = getGraphics();

 while(mTrucking == true) {

 render(g);


 try { Thread.sleep(mFrameDelay); }
 catch (InterrupedException ie) {}
 }
 }

public synchronized void keyPressed(int keyCode) {
 switch(getGameAction(keyCode)){
 case Canvas.LEFT: /* 左移 */
 //...

 }
}

private void render(Graphics g) {
 //绘图。。。
 flushGraphics();
 }

}

- 作者: wenhsuan 2006年03月14日, 星期二 15:50  回复(0) |  引用(1) 加入博采