我们需要做的第一件事是在自己的电脑上搭建一个操作系统的开发环境。然后写个能显示"Hello World"的操作系统。

下面是我们需要准备的东西: 1、一个你操作熟练的编辑器,比如Vim 2、编辑C语言代码的gcc和binutils 3、用于自动化编译和链接的GNU Make 4、一个用于运行我们的操作系统的虚拟机,推荐使用Bochs。

安装上面的软件非常容易。在我的archlinux系统上面只需要一行命令

yaourt -S vim gcc binutils make bochs

就瞬间全部搞定了。 Ubuntu和Fedora系统安装这些工具应该也非常容易,如果有问题,大家自己用百度Google下就行了。 都安装好后,我们就要分析下下面需要做的事情了。我们已经有了Bochs作为虚拟机,而这个虚拟机可以通过模拟插入软盘或硬盘然后引导启动。我们可以把Bochs看作是一台电脑——事实上我们真的可以用一台电脑来代替它,只是在一开始的阶段,使用Bochs更方便于开发调试。 有了“电脑“后我们需要开发的第一个东西就是启动引导程序(Boot Loader),也就是让电脑在通电后通过引导程序运行我们的操作系统。但是这个Boot Loader的开发非常复杂,需要用到大量汇编知识。在这里我要介绍下UIUC的SigOps uBoot System。 这个uBoot系统是我们初学者的一个神器,它可以让你跳过令人头疼的Boot Loader的开发,而直接进入到下一阶段。它的用法很简单,使用一个bootmaker程序编译elf或者是其它文件然后创建一个SBBB (SigOps Bitchin' Boot Block 哈哈)的启动映像。然后我们就可以利用我们的Bochs来运行这个SBBB的启动映像了——这实在是太强大的一个工具了! 下面我来叙述下过程。 首先下载下面的文件

http://os-quake.googlecode.com/files/kernel_0_0_f.tar.gz

解压后我们看到里面有8个文件。其中main.c是我们写的"hello world"的主程序,bloader.h是对应的头文件,Makefile是对应的make文件,helloworld.ini是配置文件。bochsrc是我们所用的虚拟机bochs的配置文件,bootmaker我之前介绍过了。kernel.elf和helloworld.img是我们编译时才会生成的文件,我刚刚打包的时候一并打包进去了,你可以删掉然后让它重新生成。 再进行下一步之前,我们先熟悉下这些文件。首先看下main.c

#include "bloader.h"
int main();

/*Global Variables BAD BAD:) */
unsigned int oldEBP;
struct boot_dir *viewableDirectory;
int totalMem;
char * passedParams;
/*end global vars */

void _start(int memSize, char *parms, struct boot_dir *loadedfiles)
{
	asm("mov %%ebp, %0":"=m"(oldEBP));
	viewableDirectory = loadedfiles; /*make file mem locations global*/
	totalMem = memSize; /*make mem of system global*/
	passedParams = parms; /*make paramaters passed to system global*/
	main();

	asm("hlt");		/* this halts the machine, solving the problem of triple-faults on 
							some machines, but also making it impossible to return to DOS */
}

int main()
{
	char *vidmem = (char *) 0xb8000;
	
	/* "Hello " */
	vidmem[0] = 'H';
	vidmem[1] = 0x7;
	vidmem[2] = 'H';
	vidmem[3] = 0x7;
	vidmem[4] = 'e';
	vidmem[5] = 0x7;
	vidmem[6] = 'l';
	vidmem[7] = 0x7;
	vidmem[8] = 'l';
	vidmem[9] = 0x7;
	vidmem[10] = 'o';
	vidmem[11] = 0x7;

	/* "World " */
	vidmem[12] = 'W';
	vidmem[13] = 0x7;
	vidmem[14] = 'o';
	vidmem[15] = 0x7;
	vidmem[16] = 'r';
	vidmem[17] = 0x7;
	vidmem[18] = 'l';
	vidmem[19] = 0x7;
	vidmem[20] = 'd';
	vidmem[21] = 0x7;
	vidmem[22] = ' ';
	vidmem[23] = 0x7;

	/* "OS" */
	vidmem[24] = 'O';
	vidmem[25] = 0x7;
	vidmem[26] = 'S';
	vidmem[27] = 0x7;

	return 0;
}

main.c只有60多行,仔细看下。void start()应该就是我们的C程序和SigOps提供的bootloader之间的桥接函数,也就是我们使用bootmaker为我们的程序添加bootloader外壳时,它会让我们的程序从start()开始运行。事实上,实现这一点并不难。 我们可以通过类似下面的代码来实现一个类似的内核的main函数,并实现汇编和C之间的链接。 kernel_asm.asm

[bits 32] ; hey, we're in PMode

[global start]
[extern _kernel_main] ; always add a "_" in front of a C function to call it

start:
  call _kernel_main
  jmp $ ; halt

kernel_c.c

kernel_main()
{
     k_init();
     k_sayhello();
     ...
};

void _start()函数一开始做了一些初始化的工作,然后就直接跳转到main()函数。char vidmem = (char )0xb8000是CGA显示卡显示内存的地址(确切来说是 0XB800-0XBC00),一般的IBM PC在这一部分使用了统一编址的方式。因此若要让一个彩色字符显示在屏幕上,我们可以直接使用内存操作指令往这个内存区域执行写操作即可。0x7是一个控制字符,它用来设置字体的颜色为黑底白字。你可以查阅相关资料把它替换为其它颜色。(相关资料见 《Linux 内核完全剖析--基于0.12内核》 赵炯编著 机械工业出版社 2009.1 第24页 2.4.6 “显示控制”一节) Makefile没什么稀奇的,调用gcc把main.c编译为main.o 然后调用bootmaker读取helloworld.ini中的配置文件,bootloader生成一个包含bootloader的kernel.elf,然后利用ld命令把main.o和kernel.elf链接到一起生成helloworld.img。

好了,在了解了我们的程序之后,我们需要做的就是启动bochs虚拟机加载helloworld.img这个映像文件,然后启动。 下面是我们动手操作下,先输入命令make,生成helloworld.img映像文件,然后再用bochs加载它。

$ make $ bochs -f bochsrc

之后我们根据提示一路回车就可以看到我们的"Hello world"了! 就在屏幕的左上方,仔细找下 :)

如果运行起来有问题的话,请检查下虚拟机的配置文件里面的内容是否正确。确保里面romimage和varomimage的目录确实在对应的目录下。

好了,我们的"hello world"已经完成了。下面我们需要做的是要为它添加一些基础的功能,比如初学者最喜欢的printf()。

本系列文章旨在一步步指导读者完成一个操作系统的编写。不同于于渊大牛的《Orange's 一个操作系统的实现》,本系列文章将直接用C语言开始编写自己的内核,略过较为艰涩的汇编内容。因为作者我也是第一次尝试编写内核,所以文章中的错误应该不少,希望大家能够及时留言指正。

本系列文章使用的UIUC的SIGOPS代码。先向UIUC的大牛们致敬!

- 前言: 能够自己动手写一个操作系统是很多编程爱好者的梦想。不过,我们不得不承认,编写操作系统还是要比编写日常的程序要困难一些。因为这次你所编写的是操作系统,而这个系统要能够运行别人编写的日常程序。一个操作系统的创建,是一个白手起家的过程,你除了硬件和对应的手册,其他什么都没有。在我们操作系统开发的初期,我们会面临缺乏各种常用库,调试器和各种常用代码的窘境。好在现在我们有了互联网,网上丰富的资料把一切都变得简单了。

所需知识和设备: C or C++的语法知识 对计算机体系结构的了解,了解汇编语言。因为我们使用了SigOPS,而它已经提供了一个i386的结构,所以读者不需要一上来就陷入到汇编和计算机体系结构的细节当中去。我们会在后面根据需要逐步介绍。 一台装了Linux的机器:我只喜欢使用Linux。 坚持坚持坚持坚持坚持坚持坚持坚持

最终目标: 我希望在系列教程的最后,我们能够完成一个非常NX的操作系统。虽然在写下这段文字的我还没有达到这一目标,但是我会不断努力的。 我所说的NX的操作系统至少要具备: 能运行多个程序 能够有进程调度程序 有系统调用 有硬件中断....等等

好了 坑已经挖好了。下面开始动工!

这个脚本是根据图书的isbn号来抓取对应的图书评分信息的。data.csv是一个包含图书的isbn的列表文件,每一行是一个图书的isbn号。 这个版本只使用了单线程抓取,并且只能从csv文件中读取数据。因为是朋友拜托抓取的,数据量不大,以后如果有需要再慢慢改进好了。 整个Python脚本很简单,主要使用了BeautifulSoup进行html的内容提取。

P.S.最近手头还有几个非常有趣的项目正在制作,希望我能够早日搞定它们 :)

import urllib,urllib2
import re
import BeautifulSoup

def isbn_2_score(isbn):
    url = 'http://www.douban.com/subject_search?search_text='
    try:
        response = urllib2.urlopen(url+isbn)
    except Exception,e:
        return 0.0
    doc = response.read()
    soup = BeautifulSoup.BeautifulSoup(''.join(doc))
    try:
        book_info = soup.find("a",{"class":"nbg"})
    except Exception,e:
        return 0.0
    if isinstance(book_info,BeautifulSoup.Tag):
        url_book_info = book_info['href']
        try:
            response = urllib2.urlopen(url_book_info)
        except Exception,e:
            return 0.0
        book_page = response.read()
        soup = BeautifulSoup.BeautifulSoup(''.join(book_page))
        score_info = soup.find('strong','ll rating_num')
        if isinstance(book_info,BeautifulSoup.Tag):
            score = score_info.string
            return score
        return 0.0
    return 0.0

def read_file(file_name):
    file_handler = open(file_name,'r')
    return file_handler

def return_isbn(file_handler):
    isbn = file_handler.readline()
    return isbn


if __name__ == '__main__':
data = read_file('data.csv')
	f = open('dump','w')
	k = return_isbn(data)
	while k is not None:
    	score = isbn_2_score(k)
    	result = k[0:-1]+":"+str(score)+"\n"
    	print result
    	f.write(result)
    	k=return_isbn(data)
	f.close()

项目地址:https://github.com/quake0day/douban_crawler

看完这部电影,感慨良多。这是一部关于成长的电影,当自己成长起来的时候,也就失去了原有的童真。在成长的过程中,我们会得到许多,也会失去许多。

从高中到大学再到美国读研,8年的时间,让我从一个孩子,渐渐成长为一个成年人。在这8年里,发生了很多很多事情。我失去了很多,也得到了很多。有的时候静下心来想想自己走过的路,我会觉得这段路真的很漫长。和Kiki一样,在自己不断往前奔跑的时候,丢掉了一些人,抛掉了许多暖色晨曦。

魔女kiki和我一样,都是到了一个全新的地方去生活。离开自己的家,到一个陌生的国度,自然会遇到各种困难。当我们开始新的生活的时候,很多事情并不是那样一帆风顺,我们会像Kiki那样遇到很多困难,遇到很多挫折。但是,只要坚持,只要努力,我们也总会越过那些坎。

从来美国到现在,已经过了8个月了,在这段时间里,我也遇到很多不顺,有的时候很困惑,感觉就像是和kiki一样,原先拿手的东西突然不好用了,就像魔力消失了一样。

后来我发现,其实魔力并没有消失,只是之前没有足够的努力,没有完全适应这边的生活。现在我已经慢慢适应了独自一个人在异国他乡生活,小心翼翼,一点点细心的把自己的生活打理的井井有条。

谁都有事情无法继续下去的时候,这个时候干着急是没有用的。在重新获得灵感之前,我可能要休息一下。

希望自己多一点点勇敢与坚持。山重水复疑无路,柳暗花明又一村。

非常喜欢这部电影。

以上是对最近5个月没有写新日志,没有新作品的解释。

美国的新年快到了,国内的同学们已经迎来了新的一年,我正好利用时差赶在这2010的最后一天里面发篇技术文章。

昨晚偶然发现Create Chen写了一篇利用基于HTTP的QQ api实现的QQ登陆的文章。作者给出了实现的C#代码,并对协议做出了详尽的分析。(URL1 URL2

通过HTTP的POST动作控制QQ,就可以开发很多很有意思的应用。比如我们可以开发QQ消息轰炸机(呵呵 :) ),可以制作QQ机器人...等等

我按照文章中所叙述的方法用我所喜欢的Python重新实现了一遍,下面是Python的代码。代码只实现了基本功能,对于返回的信息并没有进行处理,如果大家有兴趣,可以结合Create Chen的文章自行对返回信息进行处理。

下面是代码:

import urllib,httplib,md5,time
from time import sleep
class qq:
    def __init__(self,qq="",pwd=""):
        self.pwd=md5.new(pwd).hexdigest()
        self.headers=""
        self.qq=qq

    def getdata(self):
        self.conn=httplib.HTTPConnection("tqq.tencent.com:8000")
        self.conn.request("POST","",self.headers)
        response=self.conn.getresponse()
        print response.read().decode('utf-8').encode("cp936")
        sleep(1)
        self.conn.close()
    
    def Login(self):
        self.headers=("VER=1.1&CMD;=Login&SEQ;="+\
                       str(int(time.time()*100)%(10**5))+"&UIN;="+\
                       self.qq+"&PS;="+\
                       self.pwd+\
                       "&M5;=1&LC;=9326B87B234E7235")
        self.getdata()

    def GetInfo(self,friend=""):
        self.headers=("VER=1.1&CMD;=GetInfo&SEQ;="+\
                      str(int(time.time()*100)%(10**5))+"&UIN;="+\
                      self.qq+"&LV;=2&UN;="+\
                      friend)
        self.getdata()
        
    def AddToList(self,friend=""):
        self.headers=("VER=1.1&CMD;=AddToList&SEQ;="+\
                      str(int(time.time()*100)%(10**5))+"&UIN;="+\
                      self.qq+"&UN;="+\
                      friend)
        self.getdata()

    #agree_Type = 0 agree
    #agree_Type = 1 deny
    def Ack_AddToList(self,fri_Num,agree_Type):
        self.headers=("VER=1.1&CMD;=Ack_AddToList&SEQ;="+\
                      str(int(time.time()*100)%(10**5))+"&UIN;="+\
                      self.qq+"&UN;="+\
                      fri_Num+"&CD;="+agree_Type+"&RS;=")
        self.getdata()
                     
    def SendMsg(self,friend="",msg=""):
        self.headers=("VER=1.1&CMD;=CLTMSG&SEQ;="+\
                      str(int(time.time()*100)%(10**5))+"&UIN;="+\
                      self.qq+"&UN;="+\
                      friend+"&MG;="+\
                      msg.decode("cp936").encode('utf-8'))
        self.getdata()
                     
    def GetMsg(self):
        self.headers=("VER=1.1&CMD;=GetMsgEx&SEQ;="+\
                      str(int(time.time()*100)%(10**5))+"&UIN;="+\
                      self.qq)
        self.getdata()

    def Query_Stat(self):
        self.headers=("VER=1.1&CMD;=Query_Stat&SEQ;="+\
                      str(int(time.time()*100)%(10**5))+"&UIN;="+\
                      self.qq+"&TN;=50&UN;=0")
        self.getdata()

    def List(self):
        self.headers=("VER=1.1&CMD;=List&SEQ;="+\
                      str(int(time.time()*100)%(10**5))+"&UIN;="+\
                      self.qq+"&TN;=160&UN;=0")
        self.getdata()

    #stat = 10 online
    #stat = 20 offline
    #stat = 30 busy
    def Change_Stat(self,stat=""):
        self.headers=("VER=1.1&CMD;=Change_Stat&SEQ;="+\
                      str(int(time.time()*100)%(10**5))+"&UIN;="+\
                      self.qq+"&ST;="+stat)
        self.getdata()
    
    def Logout(self):
        self.headers=("VER=1.1&CMD;=Logout&SEQ;="+\
                      str(int(time.time()*100)%(10**5))+"&UIN;="+\
                      self.qq)
        self.getdata()



test = qq('9918xxxx','xxxxx')
test.Login()
print "OK"
#i =0 
#while i<5:
 #   print i
  #  sleep(2)
 #   test.SendMsg('xxxxxx',"I am robot")
#    i = i+1
i = 0
while i<10:
    print i
    test.Change_Stat('10')
    sleep(1)
    test.Change_Stat('30')
    sleep(1)
    test.Change_Stat('20')
    sleep(1)
    i = i + 1
test.Logout()

代码下载地址:http://www.darlingtree.com/download/qqlogin.tar.gz 祝大家新年快乐!