油库7号

Last Day Of The Summer
 
ajk@油库7号 @ 2008-06-17 11:17

Programing Server-Side Applications for Microsoft windows 2000
Part 1 Required reading
Chapter 2 Device I/O and interthread communication

Receiving Completed I/O Requested Notifications


Creating an I/O Completion Port
I/O完成端口背后的理论是同时运行的线程数必须有个上界;也就是,500个并发的客户端请求不必要500个线程存在。那么,合适的并发线程数是多少呢?你会意识到,如果一个机器有两个CPU,那么在此基础上多余两个以上的线程实在是没有意义。因为,当有超过CPU数量的线程数时,系统不得不耗费时间来进行线程之间的切换,这会浪费宝贵的CPU时钟周期。
为每个客户端创建一个线程还有一个不足,就是创建一个线程虽然比创建一个进程廉价,但它远远不是免费的。如果服务器应用程序在启动时创建一个线程池,并且池子中的线程留驻在程序的整个生命期内,那么服务器的性能会得到提高。I/O完成端口在设计上就是使用了线程池。
I/O完成端口可能是最复杂的核心对象。要创建一个完成端口你可以使用CreateIoCompletionPort函数。

HANDLE CreateIoCompletionPort(
   HANDLE    hfile,
   HANDLE    hExistingCompPort,
   ULONG_PTR CompKey,
   DWORD     dwNumberOfConcurrentThreads);
   
或者封装:
HANDLE CreateNewCompletionPort(DWORD dwNumberOfConcurrentThreads) {

   return(CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0,
      dwNumberOfConcurrentThreads));
}

这个函数根据传入的参数的不同,可以完成两个完全不同的功能。一是可以创建一个完成端口,另一个是可以将一个设备与完成端口关联起来。例如要产生一个完成端口,看起来可以这样调用函数:
hCompPort = CreateIoCompletionPort(
   INVALID_HANDLE_VALUE,
   NULL,
   0,
   0);

FileHandle —— 文件或设备的句柄。在产生完成端口时,这个参数应设置为INVALID_HANDLE_VALUE,于是产生一个没有和任何句柄有关系的port。
ExistingCompletionPort —— 在产生完成端口时此参数设定为NULL,产生一个新的port。如果此参数指定为一个已存在的完成端口的句柄,那么在上一个参数FileHandle中指定的句柄就会被加到此port上,而不会产生新的port。
CompletionKey —— 用户自定义的一个值,将被交给提供服务的线程。此值和FileHandle有关联。
NumberOfConcurrentThreads —— 与此完成端口有关联的线程个数。如果设定为0则为CPU的个数。
NumberOfConcurrentThreads这个参数是告诉完成端口可同时运行的最大线程数。如果传0,则默认值为CPU的个数。在大多数情况下为了避免不必要的线程切换你可以传0。有时你也许会想要增加这个值,那就是当来自客户端的请求需要一个长时间的计算甚至发生阻塞时,但通常不鼓励增加这个值。你可以尝试往这个参数传不同的值来测试比较服务器的性能。

关联一个设备到完成端口
当你创建完成端口时,系统内核会创建5个不同的数据结构.

第一个数据结构是设备列表,指明了被关联到完成端口上的设备。将设备关联到完成端口上,你同样是调用CreateIoCompletionPort函数。看起来可以这样调用函数:
HANDLE h = CreateIoCompletionPort(hDevice, hCompPort, dwCompKey, 0);

或者封装:
BOOL AssociateDeviceWithCompletionPort(
   HANDLE hCompPort, HANDLE hDevice, DWORD dwCompKey) {

   HANDLE h = CreateIoCompletionPort(hDevice, hCompPort, dwCompKey, 0);
   return(h == hCompPort);
}
这样就添加一个记录(entry)到一个已经存在的完成端口设备列表上。你在第一个参数上传递一个已经存在的完成端口句柄(在第一次调CreateIoCompletionPort时得到),在第二个参数上传递设备句柄(这个设备可以是file,socket,
mailslot,pipe等等),在第三个参数上传递一个完成键(由用户定义的值,操作系统不关心你传递的是什么值)。每当你关联一个设备到完成端口上时,系统就添加这些信息到完成端口设备列表上。
注意:CreateIoCompletionPort很复杂,推荐把它看成两个函数来使用。下面有个例子把创建端口和关联放在一次调用中完成,但是不推荐这样使用。下面的代码在打开一个文件并在同一时间创建一个新的完成端口,同时关联该设备与完成端口。所有这个文件I/O请求都将在完成时使用CK_FILE这个完成键,完成端口将允许两个线程来并发执行。
#define CK_FILE   1
HANDLE hfile = CreateFile(...);
HANDLE hCompPort = CreateIoCompletionPort(hfile, NULL, CK_FILE, 2);


第二个数据结构是一I/O完成队列。当针对一个设备的异步I/O请求完成时,系统就检查看这个设备在之前是否被关联到一个完成端口过,如果是,系统就将这个已经完成的I/O请求作为一条记录,添加到对应的完成端口的I/O完成队列末尾。在队列中的每条记录都指明了4个值:已传输的字节数;完成键(当设备被关联到端口上时通过第三个参数设定的);一个指针,指向I/O请求的OVERLAPPED结构;和一个错误码。
注意:对一个设备发出一个I/O请求,但并不将它记录到完成端口的完成队列上是可以的。这不是常态,但有时却派得上用场。比如,当你通过socket发送数据,但你并不在意这些数据是否被处理时。(或者有时候想用旧有的受激发的event对象机制来激发,而不用I/O完成队列)
为了这么做,请你设定一个OVERLAPPED结构,内含一个合法的手动重置(manual-reset)event对象,放在hEvent栏位。然后把该handle的最低位设置为1。虽然这听起来象是一种黑客行为,但档案中有交代。看起来象这样:
Overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
Overlapped.hEvent = (HANDLE) ((DWORD_PTR) Overlapped.hEvent | 1);
ReadFile(..., &Overlapped);

同样,不要忘记在关闭event句柄之前重置低位:
CloseHandle((HANDLE) ((DWORD_PTR) Overlapped.hEvent & ~1));


Architecting Around an I/O Completion Port
当你的服务器应用程序启动时,它将通过调用像CreateNewCompletionPort这样的函数来创建I/O完成端口。然后应用程序创建一个线程池来处理client请求。现在你有个问题要问,“线程池中有多少线程?”。这是一个很难回答的问题,我将在“How Many Threads in the Pool?”这个小节中给出详细的回答。目前,单凭经验的标准是本地机器上的CPU数再乘以2。比如,一个双CPU的机器上,你就应该在线程池中创建4个线程。
池子里的所有线程都将执行相同的线程函数。通常,这个线程函数执行一些初始化工作,然后就进入一个循环,直到服务进程被指示停止时才结束掉。在循环里,线程将自己休眠,等待关联到完成端口上的设备的I/O请求完成。线程通过调用GetQueuedCompletionStatus函数进入等待。
下面是函数原型:

BOOL GetQueuedCompletionStatus(
   HANDLE       hCompPort,
   PDWORD       pdwNumBytes,
   PULONG_PTR   CompKey,
   OVERLAPPED** ppOverlapped,
   DWORD        dwMilliseconds);
第一个参数hCompPort传递一个完成端口句柄,表明线程监控的是哪一个完成端口。许多服务程序都是使用的单一完成端口,所有I/O请求的完成通知都在这一个端口上。基本上,GetQueuedCompletionStatus的工作就是将调用此函数的线程休眠,直到一条记录出现在指定完成端口上的I/O完成队列中,或者直到指定的超时时间到达(超时时间在dwMilliseconds参数中指定)。
第三个数据结构就是等待中的线程队列(the waiting thread queue)。在线程池中的每一个调用了GetQueuedCompletionStatus的线程,其线程ID就被放置到等待中的线程队列里,以使I/O完成端口核心对象能知道当前有哪些线程正等待着处理已经完成了的I/O请求。当有一条记录出现在端口的I/O完成队列时,完成端口就唤醒一个在等待队列中的线程。这个线程会得到组成这条完成记录的一些信息:已传输的字节数,完成键,和一个OVERLAPPED结构的地址。这些信息通过传递给GetQueuedCompletionStatus函数的三个参数(pdwNumBytes、CompKey、ppOverlapped)返回到线程中。GetQueuedCompletionStatus函数的返回值有点复杂,下面的代码演示了如何处理不同的返回值:
DWORD dwNumBytes;
ULONG_PTR CompKey;
OVERLAPPED* pOverlapped;

// hIOCP is initialized somewhere else in the program
BOOL fOk = GetQueuedCompletionStatus(hIOCP,
   &dwNumBytes, &CompKey, &pOverlapped, 1000);
DWORD dwError = GetLastError();

if (fOk) {
   // Process a successfully completed I/O request
} else {
   if (pOverlapped != NULL) {
      // Process a failed completed I/O request
      // dwError contains the reason for failure
   } else {
      if (dwError == WAIT_TIMEOUT) {
         // Time-out while waiting for completed I/O entry
      } else {
         // Bad call to GetQueuedCompletionStatus
         // dwError contains the reason for the bad call
      }
   }
}
如你所预料到的,I/O完成队列是以队列的方式移出记录的,也就是先进先出FIFO。然而,你可能没有料到的是,调用GetQueuedCompletionStatus函数进入等待的线程却是以栈的方式来唤醒的,也就是LIFO,最后调用函数进入等待的那个线程被最先唤醒。这样做的理由是为了进一步提高处理性能。例如,有四个线程在“等待线程队列”中等待着。如果这个时候只有一条“已完成的I/O”记录出现了,那么最后一个调用GetQueuedCompletionStatus的线程被唤醒来处理这条记录。当这个线程处理完这条记录时,此线程再次调用GetQueuedCompletionStatus函数进入等待线程队列。那么,此时当有另一条I/O完成记录出现时,刚才那个线程被继续唤醒来处理这条新的记录。(这样的好处就是可以避免线程间的切换)
只要I/O请求完成得够迟缓,那么单个线程就足以处理他们。系统只需要保持唤醒同一个线程,并让其他三个线程持续休眠。使用LIFO算法,线程就不必花时间将他们在内存中的资源置换到磁盘中(例如栈空间),并从CPU的cache中替换出去。

I/O完成端口如何管理线程池
现在该讨论为什么完成端口是如此有用了。首先,在你创建完成端口的时候,你通过CreateIoCompletionPort函数的最后一个参数指定可以并行运行的线程数量。我们说过,这个值通常设置为主机上的CPU数量。当已经完成的I/O记录进入队列时,完成端口就唤醒等待中的线程。然而,完成端口只能唤醒你所指定的那么多个线程。于是,在双CPU的机器中,如果有4个I/O请求完成,并有4个线程在等待,但完成端口却只能唤醒2个线程,另2个线程保持休眠。每个得到唤醒在处理已完成的I/O记录的线程,处理完成后,又重新调用GetQueuedCompletionStatus函数进入等待。因为线程的唤醒算法是后进先出,所以系统看到还有已完成的I/O记录在排队时,又唤醒刚才被唤醒过的线程。
仔细思考这个过程,你似乎会觉得好象有些问题:既然完成端口仅允许指定数量的线程被唤醒,为什么还要产生多余的线程等待在线程池中呢?比如,在上一段中所叙述的那种情况,在一个双CPU的主机上,创建一个完成端口,并告之只允许2个线程并行处理。但是创建线程池时我们创建了4个线程(按照2倍CPU的数量),看上去我们好象创建了2个多余的线程——他们永远也不会被唤醒。(因为LIFO)
但是不用担心,完成端口是非常聪明的。当完成端口唤醒一个线程时,会将此线程的ID放入第四个数据结构中——被释放的线程列表。这可以让完成端口记住哪些线程是被唤醒了的,并监控这些线程的执行。如果某个在“被释放的线程列表”中的线程因为调用了某功能而被挂起(即产生阻塞)那么完成端口会侦测到这一情况,并将此线程的ID从“被释放的线程列表”中移动到“暂停的线程列表”——即第五个数据结构中。
完成端口的目标就是要保证在“被释放的线程列表”中的记录条数保持为你在创建完成端口时所指定的并发线程数。因此,当一个被唤醒的线程因为某种原因而被挂起时,完成端口就会释放掉另一个在等待中的线程,以次来维持“被释放的线程列表”中的记录数保持为你所指定的并发线程数。如果被挂起的线程重新被唤醒,它又会从“暂停的线程列表”中移动到“被释放的线程列表”。这个时候需要注意的是加上刚才被另外唤醒的一个线程,此时在“被释放的线程列表”中的记录数会超过你所指定的并发线程数(看上去很奇怪,但却是这样)。完成端口意识到这一情况后将不会再唤醒其他线程,除非并行线程数又降到CPU数量之下。实际并行线程数将只在你指定的最大并行线程数之上停留很短的时间并且线程在循环到再次调用GetQueuedCompletionStatus时这种情况将快速的停止下来。
注意:一旦线程调用GetQueuedCompletionStatus,那么这个线程就被“分配”到那指定的完成端口上去。系统会假定那些被“分配”的线程代表完成端口在工作。你可以终止线程和完成端口间的这种“分配”关系,通过以下三种方式:
 终止线程;
 让线程调用GetQueuedCompletionStatus,并传递另一个完成端口的句柄,这样就终止掉当前的“分配”关系,与另一完成端口产生新的“分配”关系。
 销毁完成端口。

在线程池中创建多少个线程?
现在是讨论线程池中需要多少个线程的好时机。考虑两点:首先,当服务程序启动时你应该创建一个最小的线程集,这样你就不用象每客户端每线程模型那样频繁地创建和销毁线程了。因为创建和销毁线程都需要耗费CPU时间,所以你应该尽可能的少做这些操作。其次,你应该设置线程数的最大值,因为创建太多的线程会相应耗费太多的系统资源。就算大多数资源能被交换出RAM,但你还是应该最小限度地使用系统资源,不要浪费,最好不要产生paging file space。
你也许应该测试不同的线程数。大多数服务程序(包括MS的IIS)都使用启发式(heuristic,实际就是枚举选择,通过可选择的方法发现的几种解决办法中最合适的一种,在一个程序的连续阶段被选择出来以用于该程序的下一步的一种解决问题的技巧的应用)算法来管理他们的线程池。我推荐你也使用同样的方法。例如,你可以创建如下的变量来管理线程池。
LONG g_nThreadsMin;    // Minimum number of threads in pool
LONG g_nThreadsMax;    // Maximum number of threads in pool
LONG g_nThreadsCrnt;   // Current number of threads in pool
LONG g_nThreadsBusy;   // Number of busy threads in pool

当你的程序启动时,你可以创建g_nThreadsMin这么多个线程,都执行相同的线程函数。线程函数看起来是这样:
DWORD WINAPI ThreadPoolFunc(PVOID pv) {

   // Thread is entering pool
   InterlockedIncrement(&g_nThreadsCrnt);
   InterlockedIncrement(&g_nThreadsBusy);

   for (BOOL fStayInPool = TRUE; fStayInPool;) {

      // Thread stops executing and waits for something to do
      InterlockedDecrement(&m_nThreadsBusy);
      BOOL fOk = GetQueuedCompletionStatus(...);
      DWORD dwIOError = GetLastError();

      // Thread has something to do, so it's busy
      int nThreadsBusy = InterlockedIncrement(&m_nThreadsBusy);

      // Should we add another thread to the pool?
      if (nThreadsBusy == m_nThreadsCrnt) {    // All threads are busy
         if (nThreadsBusy < m_nThreadsMax) {   // The pool isn't full
            if (GetCPUUsage() < 75) {   // CPU usage is below 75%

               // Add thread to pool
               CloseHandle(chBEGINTHREADEX(...));
            }
         }
      }

      if (!fOk && (dwIOError == WAIT_TIMEOUT)) {   // Thread timed-out
         if (!ThreadHasIoPending()) {
            // There isn't much for the server to do, and this thread
            // can die because it has no outstanding I/O requests
            fStayInPool = FALSE;
         }
      }

      if (fOk || (po != NULL)) {
         // Thread woke to process something; process it
         ...

         if (GetCPUUsage() > 90) {       // CPU usage is above 90%
            if (!ThreadHasIoPending()) { // No pending I/O requests
               if (g_nThreadsCrnt > g_nThreadsMin)) { // Pool above min

                  fStayInPool = FALSE;   // Remove thread from pool
               }
            }
         }
      }
   }

   // Thread is leaving pool
   InterlockedDecrement(&g_nThreadsBusy);
   InterlockedDecrement(&g_nThreadsCurrent);
   return(0);
}

注意:在这章稍早的时候(in the section “Canceling Queued Device I/O Requests”)。我说过:当线程退出的时候,系统自动取消所有已发起但处于挂起中的I/O请求。这就是为什么伪代码像ThreadHasIoPending这样的函数是必须的。如果线程还有未完成的I/O请求,就不要允许线程结束。
许多服务都提供了管理工具,以使管理员可以通过此来控制线程池的行为。例如,设置线程数的最小值和最大值,CPU使用极限,以及最大并发线程数。

Simulating Completed I/O Requests
I/O完成端口并不是只能用于device I/O,它也是用来解决线程间通讯问题的卓越的机制。
BOOL PostQueuedCompletionStatus(
   HANDLE      hCompPort,
   DWORD       dwNumBytes,
   ULONG_PTR   CompKey,
   OVERLAPPED* pOverlapped);

这个函数将一个“已完成的I/O”通知添加到一个完成端口的队列上。第一个参数指定要针对的完成端口,剩下的三个参数,dwNumBtyes,ComKey和pOverlapped指出了线程调用 GetQueuedCompletionStatus时将从对应的返回参数中返回的值。当一个线程从“I/O完成队列”中取出一条模拟记录时,GetQueuedCompletionStatus函数将返回TRUE,表明已经成功地执行了一个I/O请求。
函数PostQueuedCompletionStatus难以置信的有用,它提供了一种方式让你可以跟线程池里的所有线程通讯。例如,当用户终止服务应用程序时,你当然希望所有的线程都能够干干净净地退出。但如果有线程等待在完成端口上,并且此时又一直没有I/O请求到达,那么这些线程就不能被唤醒。通过对线程池中的每个线程调用一次PostQueuedCompletionStatus,每个线程都能得以唤醒,你就可以设计检查从GetQueuedCompletionStatus的返回参数中得到的值(比如设定完成键),可以判断出是否是程序要终止了,由此可决定是否要做清理工作并退出。
当使用这项技术,你必须要特别小心。我的例子能工作是因为在池中的线程会死去并不会再次调用GetQueuedCompletionStatus。不管怎样,如果你想要通知其池中的每个线程一些事情,并且它们会循环到再次调用GetQueuedCompletionStatus,你将会有一个问题。因为线程是按LIFO的顺序醒来。所以你的应用程序中你将会使用一些额外的线程同步来确保每个线程获得了合适的机会来查看它的伪I/O记录。不使用这个格外的线程同步,一个线程可能会查看相同的通知好几次。


 



 
ajk@油库7号 @ 2008-05-13 12:47

愿尘世的痛不再在天国继续。






 
ajk@油库7号 @ 2008-04-15 12:28

年轻的安徒生
他爸爸死了
妈妈进了疯人院
他住在哥本哈根低矮的狗窝
当他在河边哭泣时遇见国王的法律顾问
于是来到乡村的一所学校开始四年的学习
孩子们向他仍石块
他长出了白色的翅膀
只有托克看见了

托克想要一个农场
一些苹果树
还有两头奶牛
年轻的安徒生在河边给托克讲起了童话
托克笑了
托克从来没有笑过 但他笑了
校长告诉安徒生 幻想
总是伴随着愚蠢 所以
不要再写诗了

托克做了只巨大的风筝
他将年轻的安徒生绑在风筝上
飞上了天空
“我讨厌拉丁文,讨厌所有的问题还有那些烟”

托克告诉年轻的安徒生
所有的事物都是存在的
你只需要发现
牛顿爵士就是这样发现万有引力的
校长烧掉安徒生的笔记本
并在安徒生每次犯了错误后抽打托克
托克说
我能去哪呢
有个小巨人 他喜欢骑在蝴蝶的翅膀上

年轻的安徒生离开了学校
他告诉托克
他会给他写信的
他终于走散
剩下一只空酒瓶 无人再将它端起
就象无弦的吉他 再也发不出声响
我们常常提起安徒生
因为很多年后的一天
我们会跟我们的孩子讲起他的童话




 
ajk@油库7号 @ 2008-04-08 12:44

    我26岁生日的时候,阿牛送了一支VENSON的吉他给我。在这之前我自己有两把琴。老早阿牛说要学吉他,我就把自己常用的那一把给了他。阿牛来的那天,带着琴和他的女朋友梅梅。我记得他们是晚上来的,因为吃过饭之后我们还在蛋糕上点满了蜡烛。阿牛说我们来唱个歌吧。阿牛说话的时候脸上会洋溢着阳光般的笑容,这总让我想起《弟弟》里的弟弟。当然,阿牛比弟弟幸运多了,阿牛有着一个深爱他的女人。阿牛说为什么琴的共鸣箱要做成这个形状。我把琴竖起来,把琴的背面转向他。我说这个形状你觉得象什么。梅梅说是女体。
    的确如此,吉他与提琴一样拥有曼妙的身体。二十世纪三十年代“实验摄影大师”Man Ray拍过一张照片,那张在曲线玲珑的女背两边各以提琴琴面f洞为装饰的经典照片,令人对提琴有情欲的联想。俄罗斯名家Maxim Vengerov一九九八年购进名为克罗采的Strad时,就宣称“我们结了婚”。
    不过,同样令人产生女体联想的除了琴还有苹果。
    你见过苹果的形状吗?
    亚当与夏娃在伊甸园的时候虽然光着身子,却并无羞耻的感觉。上帝所造的野兽当中,蛇最为狡猾。后世多把蛇看作是狸狸的化身。也就是蛇妖。歌德《浮士德》中,浮士德博士为求美人海伦,由侏儒载到希腊,夜遇狮身人面、美人鱼、巨蚁等神怪,接着就有蛇妖出场。在古巴比伦神话中也有狸狸的原型:面如处女而羽足似鹰、黑夜起飞猎食婴儿。
    蛇问女人:这园子树上结的果子,你们一概吃不得吗?
    女人回答:只有园子中央那棵树上结的苹果,上帝说了:你们不要去吃,也不许碰,否则你们一定死掉。
    蛇说,不会吧,你们怎么会死!当然上帝不会不知,那苹果一旦被你们吃了,你们就开了眼,就会象神明一样,懂得辨善恶了。
    女人望着那棵树上的果子,那么鲜美悦目,还能赐人智慧!就忍不住摘下一个,吃了,又给身旁的丈夫一个。两人一吃,眼就开了,发现自己光着身子。色欲坏了乐园。但此罪起于违背上帝的戒谕,根源在骄傲;所以骄傲在“七宗罪”之首。
    傍晚,凉风中传来上帝走进园子的脚步声,他们忙往树丛里躲。耶和华唤道:亚当何在?
    亚当回答:听见你走进园子,我光着身子,就躲起来了。
    耶和华问:光着身子,谁告诉你的?莫非你吃了我不许你吃的果子?
    亚当说:是你赐给我做伴的女人,她给我果子吃,我就吃了。这是人类第一次撒谎,推托责任。其实蛇对女人说禁果的好处时,亚当在一旁听着。
    耶和华便问女人:为什么?
    女人说:是蛇诱骗了我,我才吃的。
    于是上帝对蛇说:因为你干下这桩坏事——

    一切家畜野兽
    惟有有你受此诅咒:
    从此用肚皮爬行,
    终生以尘土为食。
    女人与你,永世为敌,
    子子孙孙,互相仇恨;
    人打扁你的头,
    你咬伤他脚跟。

    接着,对女人说:

    我要倍增你怀孕的苦,
    分娩时越发痛不可忍!
    然而你却要依恋丈夫,
    要丈夫做你的主人。

    最后,对亚当说:因为你听从妻子的言语,偷吃我名令禁食的果子——

    因为你,这土地要受我诅咒!
    从此你一辈子辛劳
    才能勉强果腹。
    遍野荆棘杂草,
    是你谋食的去处。
    汗流满面,才吃得上一口,
    直到你复归大地;
    因为你是尘土所造,尘土
    终是你的归宿。


    阿牛走的那天来向我道别。阿牛说他要去上海。
    很久过后阿牛给我打电话,说起每天很忙碌,很多代码要写,很多新的程序架构要掌握,说很想念一起弹吉他的时候。然后我告诉他其实我已经很久不弹吉他了。




 
ajk@油库7号 @ 2008-03-22 10:20

《吃过晚饭毛老师和他的女朋友玩的捉迷藏游戏》

毛老师让女朋友先呆在卫生间
然后在剩余的四间房子里
寻找藏身的地方
三分钟以后
毛老师把自己藏好了
五分钟以后
女朋友把毛老师找出来了
他们都觉得很高兴 

女朋友又让毛老师躲进卫生间
然后自己在剩余的四间房子里
寻找藏身的地方
三分钟以后
女朋友把自己藏好了
五分钟以后
毛老师把女朋友找出来了
他们仍然觉得很高兴 

女朋友说,再来一遍
说完就进了卫生间
她隔着卫生间的门
又嘱咐了一句
"藏得高明一点!"
 
毛老师来到房门口,悄悄
把房门打开
来到了街上
 
啊,天色已经不早了
毛老师把手插到口袋里
一直往前走
再也没有回去           
                                        -------李红旗                 


 
油库7号 @ 2008-03-18 12:47

再没有王公发号施令,
再没有领主高贵的身影。
那里宫殿将长满荆棘,
荒草和蒺藜覆盖城堡。
那里豺狼安家,鸵鸟做窝,
野猫邂逅鬣狗,羊鬼遥相呼应。
还有狸狸,四处出没,
寻找她栖身的角落。

                  《以赛亚书》34:11

    狸狸与亚当吵架、跟天使争执、发誓拿婴儿报复,乃至她被世人想像为利比亚蛇妖——“上身形若美人,脐以下不可名状,炽烈如火”(《光明左流论》之十九)。人开窗睡觉,她便乘风而来。
    那么,狸狸是谁?
    上帝用泥土照自己的模样造就了亚当。上帝说:亚当一个人孤零零的不好,我给他造个帮手做伴吧。于是取一些泥土,也按照自己的形象,捏成一个女人,从鼻孔吹入生命之气,取名狸狸。亚当看了,非常喜欢,尤其是她赤焰般飘舞的头发。
    狸狸一直活在世上,遁入荒野,与魔鬼为伍,做了一切鬼魅的主母并撒旦的王后。
    孩子气的撒旦,可怜的撒旦,连他女人心里念念不忘的还是以前的男人亚当,尽管是诅咒。



 
油库7号 @ 2008-03-08 14:38

没了



 
油库7号 @ 2008-03-03 16:04

<Man on the moon>

maybe this time you are here
mayby this time i am there
mayby next time we will be wrong
we are all late
maybe this time
maybe next time
nobody knows
i wanna go down the line
i wanna go down the road
i wanna go down the line to find where in the world you are crying
i wanna go down the line to find you

maybe this time you are alone
maybe this time i am alone
maybe next time we all are wrong
we'll be late

i wanna go down the line
i wanna go down the road
i wanna go down the line to find where on the planet you are crying
i wanna go down the line to find you

i will see you again in another day
i will see you again in another town
i will see you again in another hotel room

i wanna go down the line
i wanna go down the road
i wanna go down the line to find where on the moon you are crying
i wanna go down the line to find you 


<Hello!Passengers>

now i know in the town,the river is never like it used to be
now i see in the street,the girl is never like she used to be there
what i feel in the room,the love is never like it used to be
now i here in the city ,the story is never like it used to be there

where are all the passengers?
in the days of the passing memories
where are all the passengers
in the days of what you said in the end
where are all the passengers
in the days of the passing memories
where are all the passengers~~

some days i was standing in the rain
some days i was sleeping in your arms
some days i see you in the night
some days i see you on the road

when i was wandering in the town, i saw you were walking along the river
when i was floating on the sea,i saw you are sleeping in the street.
when i was walking on the moon,i saw you are dreaming in the room
now i kown,in my home town ,the river is never like it used to be there

she is out,out of all the places
she is lost
she is out of all the places
she lost all her passing days

where are all the passengers

<ladysister>
Hey girls my lady sisters Your shoes come from the hottest island Hey boy my Mr.Brothers Your pants come from the magazine Are you such a shiboy? Are you such a shigirl? Waking up in the comic world I don’t know your name Waking up in the comic world I can’t see your face What does the 21th centry youths against? What does the 21th centry youths against? What does the 21th centry youths against? What does the 21th centry youths against? 


<Love Is Pop>
love is pop
love is a party
we join it everyday

love is a room
love is a hotel
we are outside every night

we have a fast dream
we have got a terrible life
we have a horror travel in it

when you leave the room
the pink elephant comes
when you leave the car
the rozd comes to the end
when you leave the bed
the night becomes darkness
when you leave the room
the light comes to be starduse

love is a hit
love is a drunk
we dance with it every time
love is pop
love is a hotel
we are outside every night

we have a fast dream
we have got a terrible life
we have a horror travel with you

love is pop
love is a party
love is a room
love is a hit

给个试听的链接
http://www.32yy.com/7/34c36371/all/




 
ajk@油库7号 @ 2008-01-11 12:34

      耶和华取地上的尘土造人,朝他鼻孔里吹进生命之气,亚当就有了灵魂,活了。接着,上帝耶和华在东方的伊甸开辟了一座园子,即后世所谓的天堂乐园;伊甸本义丰美。给新造的人居住。自伊甸发源一条河,灌溉那座园子;出园子,分为四支:一支叫皮逊河,流经沙国全境,即阿拉伯。那地方的黄金最纯,还盛产树胶、玛瑙。一支叫基雄河,流经古实全境。指埃及以南,今苏丹、埃塞俄比亚、也门一带。一支叫底格里斯河,流经亚述东部。亚述是两河流域古国,今伊拉克北部。一支叫幼发拉底河。
      上帝耶和华将亚当带到伊甸园里安置了,命他修整照看园子。并下达戒谕:这园子树上的果子,你可以随意摘吃。只有那善恶智慧之树的果子,你不要去吃它。
      上帝耶和华还说:亚当一个人孤零零的不好,我要给他造个般配的帮手。便取尘土造了飞禽走兽,带到亚当面前,让他命名;每一种动物都要由他口中得一个名字。可是亚当给飞禽走兽取完了名字,也没见着一个般配的帮手。于是耶和华使亚当沉沉的睡去。乘他酣眠之际,抽下他一根肋骨,再把肉重新合上。就用这根从他身上取下的肋骨,耶和华造了一个女人,领到他的面前。亚当说:
终于找到了,我的
骨中骨,肉中肉!
她该叫做“女人”,
因为她出自男人!

      这就是为什么男人要离开自己父母去依恋妻子,与她结为一体。



 
ajk@油库7号 @ 2008-01-11 12:26

       太初,上帝创造天地。
       经书上以这一句话开头,也就是说创世之前大地无形,一片混沌,黑暗笼罩深渊。实际上,上帝开辟我们这个世界之前,造了七件宝物:一是法,用黑火在白火上写就,她端坐在上帝怀里;二是上帝的宝座,就是后来由四位四脸四翼、汗滴燃起火焰的天尊牵引的那驾战车;三是上帝右手的天堂;四是上帝左手的地狱;五是上帝面前的天庭圣所;六是圣所祭坛镶嵌的宝石上,刻着的救世主的圣名;七是先知摩西咏赞过的那个威严的声音:人子呀,你们归来!(诗篇)
      上帝说:光!就有了光。上帝看那光好,便把它与黑暗分开。称光为昼,称黑暗为夜。夜去晨来,是第一日。为什么夜去晨来为一日呢?古人以黄昏为一日之始。
      上帝说:大水中间要有苍穹,把水分开!水果然一分为二。上帝称苍穹为天。夜去晨来,是第二日。
上帝说:天底下的水要汇集一处,露出干地!干地果然升出水面。上帝称干地为陆,称汇集一处的水为海。上帝看了,觉得很好。又说:陆地要生长草木,有产五谷的也有带果子的,各结各的籽实!果然,地上就长出草木。上帝看了,觉得很好。夜去晨来,是第三日。
       上帝说:天穹上要有光体划分昼夜,标记时令节期,发光照耀大地!果然,就有了天灯。上帝先造两盏最亮的,一大一小,大的管白昼,小的管黑夜。也就是太阳和月亮。然后再造星星。将他们一一安上天穹,让它们照耀大地。上帝看了,觉得很好。夜去晨来,是第四日。
       上帝说:水里要游动无数生灵。空中要鸟雀展翅飞翔!各种各样的水族和飞禽,都是上帝所造。上帝看了,觉得很好,为它们赐福道:生吧,长吧,让鱼儿充盈大海,让鸟儿遍地孵育!夜去晨来,是第五日。
       上帝说:陆地上也要繁衍动物,各种各样的牲畜、爬虫和野兽!果然,就有了野兽、牲畜和爬虫。每一种都是上帝所造。上帝看了,觉得很好。
       于是上帝说:我要造人了。照我自己的形象,如同我的模样!我要人做海里的鱼、空中的鸟以及一切牲畜野兽爬虫的主宰!
       所以上帝造人,
       取的是他自己的形象;
       男人女人,都依照
       他的模样。

      上帝为人赐福道:生儿育女吧,让子子孙孙遍布四方,征服大地!我要你们统治水里的鱼、空中的鸟、所有地上行走的动物。还说:看哪,我把大地出产的五谷和树上结的果实都赐予你们,做你们的食粮;而飞禽走兽,我给它们吃青草嫩叶。果然,一切都按上帝的意愿实现。上帝看了。觉得非常之好。夜去晨来,是第六日。
       就这样,天地万物全数造就。第七日,创世结束,上帝歇工。他为第七日赐福,定为圣日,即安息日。因为在这一天上帝完成了创世,休息了。
       这,就是天地的开端,世界的来历。


 
日历
网志分类
『所有网志』
『汇编语言』
『网络编程简介』
『 Win32程序设计』
『不专业』
『经』
最新留言
08/26 谢谢你昨天来看...
08/23 上网,没事做,...
06/30 你到底在干嘛!...
站内搜索
友情链接
我的歪酷 非非共享界
◇油库8号
◇油库11号
◇不可回收的Nick
◇夜色中的高潮
◇Try To Remember
◇隐床不言欢
◇美术馆旁边的斑马园
◇那么蓝或者到处乱走
◇王不在
◇中国摄影
◇马未都
◇戴老师
◇戴老师2
◇橡树
◇kendiv
◇lu0
◇Sysinternals
◇forum of sysinternals
◇Mark Russinovich's blog
◇windowsitpro
◇codeguru
◇cnpaf
◇xplus
◇codeproject
◇钱晓捷
◇MSTechNet
◇电脑编程技巧与维护
◇phrack
◇rootkit
◇planet-lab
◇刘江@图灵
◇大宝
◇VCTeamBlog
◇三言二拍
◇C++的罗浮宫
◇麦田
◇木玛
◇CHK
◇阿牛
◇独自闯荡的阿丑
◇数学网
◇歌词服务小组
ACM
IEEE
Applicable Algebra in Engineering Communication and Computing
Stochastic Processes and Their Applications
Pacific Journal of Optimization
luojw
hust
p2p research
stanford
订阅 RSS
0021418
歪酷博客