Friday, November 16, 2007

WindowCE多线程编程

WindowCE多线程编程包括线程的启动、线程的运行状态控制、线程同步及数据通信和线程的正常/非正常退出。本项目的软件及架构在多线程设计上,要求通过多线程实现异步的数据采集及绘制,以提高系统运行效率。

Win32API支持多线程的启动,其中以前版本的程序直接调用API函数CreateThread()分配资源启动线程,并返回线程句柄(Handle),以控制线程状态。客观上,这种方法在Win32平台上是通用的。然而,通用不一定最好。

MFC是在EVC下提供的类库,基本上以MFC程序框架下封装了大多API系统调用,优点不仅仅是使用方便,更重要的是对系统的运行稳定性、正确性都提供的更高的保障。通过查阅得知,通过MFC提供的封装API函数AfxCreateThread()创建新线程,返回MFC的CWinThread类对象实例,这样通过对象实例可以更方便安全的进行线程的行为控制。

线程初始化时可以传递参数,在Window CE下线程函数标准声明为UINT FunctionName(PVOID pArg),在CreateThread时将pArg对应实参指定。但传参注意MFC不是线程安全类的指针。本系统传递CDialog指针,因此通过类型转换,即在CDialog子类方法中调用AfxBeginThread(QuerySampleThread,(LPVOID)this,0,0,0,0)。

线程的状态,一般有就绪态、运行台、阻塞态、终止态等。为了实现各线程的功能同步,有必要合理的是各个线程进行状态的切换。在MFC的线程框架下,通过::AfxCreateThread()启动线程、自身CWinTherad::Suspend()阻塞线程、其他线程调用CWinThread::Resume()重新就绪线程、线程return正常退出。考虑到本嵌入式系统对稳定性、效率的高要求,去掉不安全的调用,如CWinThread::Terminate()外部调用终止线程等方法。

WindowsCE提供若干线程通信机制,本系统选用Event(事件)进行线程间的同步。通过CreateEvent()申请Event资源,返回HANFLE实例,以进行行为控制。在申请时即可指定生成时Event状态以及复位机制。本系统中信号生成时为复位状态,有且仅有手动调用ResetEvent()才能复位——即手动方式,则在生成时参数为:CreateEvent(NULL,TRUE,FALSE,NULL)。

对线程控制通过判断各Event资源信号的置位/复位实现。一个特点就是Event资源的静态优先级:多Event的判断通过WaitForMultipleObjects(3,signals,FALSE,-1)实现,对于Event数组signals装入3个Event并按优先级排列HANDLE signals[3] = { hCloseEvent, hPauseSampleEvent, hBeginSampleEvent},如果三个Event同时置位,则首先响应hCloseEvent,实现了线程关闭的可靠性。

线程在某种条件下要进行状态切换,或者是自身行为或者是外界行为。首先线程外部创建线程,并指定线程初始状态。线程内部由于某种条件不满足,需要等待其他线程而挂起时自身调用CWindThread::Suspend()。然后需通过线程外界调用CWinThread::Resume(),使被唤醒线程进入就绪态,准备从CWinThread::Suspend()处继续执行。API的Sleep()可以是当前线程阻塞一定时间,在某些环境下(如与驱动交互时有意延时,以便驱动有时间操作硬件)可以用到。

对于线程的结束,MFC建议线程自身通过return正常结束,而TerminateThread()则简单的收回线程控制块资源,极可能造成县城内部资源不能释放,是系统资源泄漏、变得不稳定。

另外本系统为及时响应驱动的数据传递信号,将与之相关的线程QuerySampleThread提高优先级,通过CWindThread:: SetThreadPriority(THREAD_PRIORITY_ABOVE_NORMAL)实现。但Windows CE严格按优先级进行调度,因此QuerySampleThread一般在WaitForMultipleObjects()阻塞状态,才能使其他线程得以运行。

主要操作的代码为:

Ø 创建线程并设置线程优先级:
CWinThread *pQuerySampleThread = AfxBeginThread(QuerySampleThread,(LPVOID)this,0,0,0,0);
pQuerySampleThread->SetThreadPriority(THREAD_PRIORITY_ABOVE_NORMAL);

Ø 创建Event资源:
HANDLE hCloseEvent = CreateEvent(NULL,TRUE,FALSE,NULL);
if ( hCloseEvent==INVALID_HANDLE_VALUE )
{ ……}

Ø Event的信号控制:
SetEvent(hCloseEvent);
… …
ResetEvent(hCloseEvent);

Ø 线程的Evnet判断及线程状态转换:
HANDLE signals[3] = { hCloseEvent, hPauseSampleEvent, hBeginSampleEvent};
DWORD dwEvent = WaitForMultipleObjects(3,signals,FALSE,-1);
if ( dwEvent ==0 ) { return; }
if ( dwEvent==1) { SuspendThread(pQuerySampleThread->m_hThread); }

Ø 线程的唤醒:
pResampleThread->ResumeThread();
以上提到WIndowCE下基本的多线程编程操作,可以满足小型项目的需要,但在具体开发中,要合理设计线程运行流程,防止并发运行中“与时间有关的错误”。

Windows® CE 系统中的同步机制

目录


摘要... 1

目录... 1

一、WinCE进程/线程模型概览... 1

二、临界区(Critical Section)... 2

三、互斥体(Mutex)... 3

四、信号量(Semaphore)... 4

五、事件(Event)... 5

六、消息队列(MsgQueue P2P)... 6

七、互锁函数(Interlocked Function)... 8

八、Wait函数... 8

总结... 10

参考资料以及进一步阅读... 10

关于作者... 10



一、WinCE进程/线程模型概览
WinCE操作系统实现了进程/线程两级管理模型。连同内核进程和系统进程,以及应用进程一起,WinCE共支持32个进程。进程还可以有自己的线程,进程默认有一个主线程,线程分为256个优先级别,WinCE调度程序按照线程优先级高低来调度。进程是WinCE中最小资源分配的单元,线程是WinCE的最小调度单元。

本文讲述的同步机制有些只适用于线程间同步,有些既能用于线程间同步又能用于进程间同步,下面讨论到某一种机制的时候,再具体详述其适用场景。

二、临界区(Critical Section)
(本节内容适用于WinCE 1.0及以上版本)

WinCE实现了操作系统理论里的临界区管理。临界区内含有对临街资源的访问。通过对临界区进行有效管理,使得某一时刻最多只能有一个线程进入临界区,实现对临界资源的保护。

考虑下面用临界区实现两个线程对临界资源互斥访问的情形。The 1st Thread和The 2nd Thread都要调用Func_CriticalSection()函数,而Func_CriticalSection()内部会对某一临界资源进行操作,为了保护这一临界资源,我们用一个WinCE的CriticalSection来实现。

图一是该解决方案的一个场景。The 1st Thread和The 2nd Thread进入临界区之前已经创建(new)并初始化(InitializeCriticalSection())了一个临界区。试图进入该临界区的线程首先必须获得进入该临界区(通过EnterCriticalSection() / TryEnterCriticalSection())的资格,如果临界区内没有线程,它就能进入,否则必须被挂起等待。进入临界区的线程可以对临界资源进行操作(OpOnSharedResources())。操作完成之后退出临界区(LeaveCriticalSection()),以允许其它线程进入。图一中第一个线程进入临界区还未退出之前,第二个线程因执行EnterCriticalSection()而一直在被挂起等待,第一个线程退出临界区之后,第二个线程从等待中被唤醒,按照相应的调度机制重新竞争获得CPU,从而继续执行,完成临界区内的操作。



图一、应用临界区(CriticalSection)实现同步

利用临界区可以实现对临界资源的互斥操作,WinCE的临界区应用在同一进程内,亦即实现的是同一进程内的线程间同步,不能应用在进程之间。

三、互斥体(Mutex)
(本节内容适用于WinCE 1.01及以上版本)

互斥体(Mutex)顾名思义就是实现对共享资源实现互斥访问的。WinCE中的互斥体的使用规则如下(按线程之间的同步为例):

◇ 互斥体可以是匿名互斥体也可以是命名互斥体;
◇ 线程创建互斥体的时候可以指定创建完毕它是否就立即拥有该互斥体;
◇ 某一时刻最多只有一个线程拥有给定的互斥体;
◇ 拥有互斥体的线程可多次获得该互斥体;
◇ 线程可用CreateMutex或wait函数来获得互斥体。

看下面应用互斥体的情景。Thread1创建并拥有了一个互斥体g_hMutex[序列1&2]。互斥体g_hMutex是定义的全局量,thread2可访问到,Thread2用WaitForSingleObject()试图获得该互斥体,因为此时g_hMutex是被Thread1拥有的,所以Thread2被挂起[序列3]。Thread1执行了一些操作之后,又用wait函数试图再次获得了该互斥体,因为此时g_hMutex的拥有者还是Thread1,所以Thread1立即再次获得了该互斥体[序列4-6]。Thread1对互斥体g_hMutex保护的共享资源操作完毕,释放该互斥体[序列7],但是因为Thread1两次获得了g_hMutex,所以g_hMutex的拥有权并没有交出。等到Thread1再次释放互斥体g_hMutex[序列8]之后,Thread1才失去了g_hMutex的拥有权,Thread2可竞争g_hMutex的拥有权,如能成功拥有,就可从等待状态被唤醒,完成对共享资源的访问操作。



图二、应用互斥体(Mutex)实现同步

不知道从上面的描述,读者有又没有看出互斥体与临界区之间的区别。使用上,它们都实现的对共享资源的互斥访问,但是临界区是多个线程对同一段程序的执行,这段程序会访问到临界资源,所以它们是同一个进程内的多个线程;而互斥体的应用情景是在线程之间独立执行,可以不是程序上的重叠,只是一个线程执行到共享资源的时候,有可能别的线程也要访问该共享资源,所以要用互斥体来保护该共享资源。

由于互斥体上述的应用范围,它不但能应用在同一进程内的线程之间,也能应用在进程之间。进程之间可以通过命名互斥体来实现。一个进程通过为CreateMutex()指定一个名字做参数来获得已经存在的互斥体的句柄,处理过程如下面程序所示。

HANDLE hMutex;

hMutex = CreateMutex (
NULL, //
FALSE, // Mutex object NOT initially owned
TEXT("NameOfMutexObject")); // Muetx Name

if (NULL == hMutex)
{
// Something wrong, deal with it here.
}
else
{
if ( ERROR_ALREADY_EXISTS == GetLastError () )
{
// CreateMutex() opened existing mutex."
// ...
}
else
{
// CreateMutex() created new mutex."
// ...
}
}

进程获得已经存在的互斥体的句柄之后,就可以如线程之间同步规则那样来实现进程之间的互斥体使用。

四、信号量(Semaphore)
(本节内容适用于WinCE 3.0及以上版本)

信号量实体有一个数值指示当前该信号量使用情况,当前值的大小处于零和最大值之间。用下列操作原语实现信号量的同步操作(用线程间同步来说明):

◇ P(S, num):如果信号量当前值减去num大于零,执行该操作的线程获得信号量,可继续执行,同时信号量的当前值减小num;否则访问线程被挂起等待
◇ V(S, num):信号量的当前值增加num(增加之后仍不大于最大值),如果有等待该信号量的线程被挂起,唤醒等待线程并按照相应的调度机制参与调度。

信号量一般用来控制某类共享资源,最大值标识该类资源的数目,执行P操作是申请一定数目这类资源,V操作是释放一定数目的这类资源。在WinCE的信号量实现中,并未实现OpenSemaphore,P操作是用wait函数来实现的,而V操作由ReleaseSemaphore来实现。

看下面用信号量来控制数量为2的某类共享资源的使用情景。



图三、用信号量(Semaphore)实现同步

Thread1创建一个控制2个共享资源的信号量[序列1&2],并且自己用WaitForSingleObject()来申请一个资源,因为当前可用的这类资源有2个,所以它就获得了其中的一个[序列3&4]。同样地,Thread2获得了另外一个资源[序列5&6]。但是当Thread3也申请这类资源的时候,因为此时已经没有这类资源,信号量的值为零,它就被挂起[序列7]。拥有这类资源的线程释放掉一个资源[序列8&9],并且满足能满足Thread3申请资源数目的要求,Thread3竞争获得了该资源[序列10]。

信号量是实现同步的基本方法,在几乎所有的多任务操作系统里面都做了信号量的实现,其它一些同步机制其实可以通过信号量来实现。如果把信号量的最大值和初始值均设置为1,那么它就可实现互斥体,即保证对共享资源互斥访问的保护。如果把信号量的初始值设置为0,等待别的线程ReleaseSemaphore来唤醒它,那么它就可实现事件(Event)机制。

信号量机制可以用在同一进程内的线程之间同步,也可以用在进程之间的同步。进程间同步的实现方法如同互斥体的此类实现。

五、事件(Event)
(本节内容适用于WinCE 1.0及以上版本)

WinCE系统中广泛用到事件(Event)机制来实现线程之间的协调工作,具体表现在:

◇ 通知一个线程什么时候去执行它的特定的任务
◇ 标识事件的发生

WinCE中的线程操作原语有CreateEvent(),SetEvent()/PulseEvent(),ResetEvent()等。创建Event的时候在CreateEvent()的参数中指定Event的初始状态(触发的/未触发的),还要指定事件是否手动复位(手动复位是只有用ResetEvent()才能把事件状态显式地设置为未触发的,自动复位是等待该事件的线程等待事件到来之后,系统自动把该事件的状态复位为未触发的)。线程等待事件仍然用wait函数。

下面是使用Event同步的简单情况:



图四、用事件(Event)实现同步

线程Thread1执行过程中,要等到某个条件满足(事件触发),所以它创建了一个事件Event(参数设置为:手动复位,初始条件为未触发的),用WaitForSingleObject()来等待这个事件。线程Thread2执行了一些操作之后,满足了Thread1的条件,用SetEvent来触发该事件。

除了可以用SetEvent()来触发事件之外,也可以用PulseEvent()来触发,区别是PulseEvent()触发该事件之后把它又复位。

另外,也可以把命名事件用于进程之间的同步。实现方法同互斥体中的描述。

六、消息队列(MsgQueue P2P)
(本节内容适用于WinCE.net 4.0及以上版本)

消息队列通信机制如同建立了一个管道,管道的双方通过分别建立到管道的两端,与管道的读端口建立连接的进程可以从该端口读取消息(Message),与管道的写端口建立连接的进程可以写入消息(Message)到管道。管道内消息组成了一个FIFO(First In First Out)的队列,从读端口读取消息是读取队列的头,写入消息到写端口是在队列尾部追加一个消息。

WinCE中关于MsgQueue的操作函数主要有:

◇ CreateMsgQueue()创建一个消息队列。在该函数的参数中指定消息队列的名字,消息队列的最大数目,每个消息的最大长度,对该消息队列可进行读还是写操作等。因为调用一次CreateMsgQueue函数,只能指定读或者写这样的二选一的消息队列,所以一般需要用相同的消息队列名字做参数两次调用该函数,分别创建读消息队列和写消息队列,它们的返回值分别被读进程和写进程用OpenMsgQueue()打开用于读取消息和写入消息。
◇ OpenMsgQueue()打开消息队列并建立与相应端口的连接。进程与读端口建立连接之后,可用返回的句柄从消息队列中读取消息;进程与写端口建立连接之后,可用返回的句柄写入消息到消息队列中。
◇ CloseMsgQueue()断开与消息队列相应的端口之间的连接,并关闭由CreateMsgQueue()或OpenMsgQueue()创建或打开的消息队列。
◇ ReadMsgQueue()如同从普通文件中读取数据一样,用于从消息队列中读取消息。可以指定读取消息时,如果消息队列为空,读进程是被挂起还是直接返回。
◇ WriteMsgQueue()如同写数据到普通文件中一样,用于写消息到消息队列中。可以指定写入消息时,如果消息队列已满,写进程是被挂起还是直接返回。

下图是MsgQueue应用的典型场景。



图五、用消息队列(MsgQueue)实现同步

这种场景下的执行过程为:

◇ 主进程MainProcess创建了名为“Reader/Writer MsgQueue”的读和写的消息队列,并分别返回hMsgQ_r_m和hMsgQ_w_m[序列1-4]。
◇读进程ReaderProcess以主进程的ProcessId和hMsgQ_r_m为参数,通过OpenMsgQueue()与MainProcess消息队列的读端口建立连接[序列5&6]。
◇ ReaderProcess与消息队列建立连接之后,用WaitForSingleOnject(hMsg_r)看消息队列中是否有消息,因为此时消息队列为空,所以ReaderProcess被挂起[序列7]。
◇写进程WriterProcess以主进程的ProcessId和hMsgQ_w_m为参数,通过OpenMsgQueue()与MainProcess消息队列的写端口建立连接[序列8&9]。
◇ WriterProcess与消息队列建立连接之后,用WaitForSingleOnject(hMsg_w)看消息队列中消息是否满,因为此时消息队列为空,未满,所以WriterProcess不会被挂起[序列10&11]。
◇ WriterProcess写消息到消息队列中[序列12&13]。
◇ 因为消息队列中已经有了消息,ReaderProcess从挂起状态被唤醒[序列14]。
◇ ReaderProcess继续执行,从消息队列中读取WriterProcess刚才写入的消息。

消息队列除可用于同步之外,主要用于进程之间的数据传递,另外消息队列也可以用于同一进程中的线程之间同步,但是既然线程之间能直接传递数据,又何必那么麻烦呢。

七、互锁函数(Interlocked Function)
(本节内容适用于WinCE 1.0及以上版本)

除了上面各节的同步方法之外,WinCE还提供了一些用于原子操作的互锁函数,这些函数在执行过程中,不会因为线程的调度引起的当前线程被抢占而打断函数内的操作。

这些函数主要有:

InterlockedIncrement
InterlockedDecrement
InterlockedExchange
InterlockedTestExchange
InterlockedCompareExchange
InterlockedCompareExchangePointer
InterlockedExchangePointer
InterlockedExchangeAdd

八、Wait函数
(本节内容适用于WinCE 1.0及以上版本)

Wait函数不是特指的某一个函数,而是指wait的系列函数。wait函数并不是WinCE同步机制中的一种,但是WinCE的很多同步机制要用到wait函数,这些在前面讲述各个同步方法的时候也已有论述。

一般地,执行wait函数时,如果等待的同步对象条件不满足,那么执行wait函数的进程/线程会被挂起,当然也可以给它们设置等待的超时时间,超过给定时间,不管条件是否满足,它们会自动从等待状态苏醒。等待既可以等待某一个条件,也可以等待多个条件中的一个,WinCE不支持等待多个条件同时满足,如果有这种需要,要自己实现。

Wait函数原型如下:

DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds );

DWORD WaitForMultipleObjects(
DWORD nCount, // No. of object handles in the array.
CONST HANDLE* lpHandles, // Pointer to an array of object handles.
BOOL fWaitAll, // MUST be FALSE in WinCE
DWORD dwMilliseconds // Timeout (0, mills, or INFINITE)
);

DWORD MsgWaitForMultipleObjects(
DWORD nCount, // No. of object handles in the array.
LPHANDLE pHandles, // Pointer to an array of object handles.
BOOL fWaitAll, // MUST be FALSE in WinCE
DWORD dwMilliseconds, // Timeout (0, mills, or INFINITE)
DWORD dwWakeMask // Input types for which an input event object handle
);

前面讲述各种同步机制的时候都是以WaitForSingleObject()来说明的,这里就不再赘述它了。

WaitForMultipleObjects()和MsgWaitForMultipleObjects()可以用来等多个同步对象,它们之间的区别就是MsgWaitForMultipleObjects()还等待dwWakeMask参数中指定的输入事件,即这些事件发生时,等待的进程/线程也能被唤醒。

用WaitForMultipleObjects()等待的多个同步对象的句柄放在参数lpHandles数组中,同步对象的句柄的数目放在参数nCount中。dwMilliseconds指定了等待的超时参数:如果指定为0,该函数等待每个同步对象之后,不管触发与否都直接返回;如果指定为INFINITE ,该函数等待每个同步对象,直到有一个同步对象被触发,否则执行该函数的运行实体将一直被挂起;如果指定为非0,非INFINITE的一个数值,那么不管等待的同步对象是否被触发,到了指定的时间,执行该函数而被挂起的运行实体也会被唤醒。因哪个同步对象被触发而返回还是因超时而返回,可以从返回值中来判定,返回值为WAIT_TIMEOUT,是因为超时;返回值为WAIT_OBJECT_0到WAIT_OBJECT_0 + nCount -1之间的数时,可以按顺序找到具体那个同步对象被触发。

下面是WaitForMultipleObjects的典型应用。

HANDLE hSynchObjects[EVENT_COUNT];
DWORD dwEvent;

/* Put event handles in hEvents */
// ...

dwEvent = WaitForMultipleObjects (
EVENT_COUNT, // Number of objects in an array
hSynchObjects, // Array of objects
FALSE, // MUST be FALSE
500); // timeout, 0.5s

switch (dwEvent)
{
case WAIT_TIMEOUT:
// Handle for timeout
break;

case WAIT_OBJECT_0 + 0:
// Handle the 1st event
break;

case WAIT_OBJECT_0 + 1:
// Handle the 2nd one
break;

...

case WAIT_OBJECT_0 + EVENT_COUNT -1:
// Handle the final one
break;

default:
// Error: Not an anticipant one, handle it.
break;
}

总结
本文探讨了WinCE中的各种同步机制的用法,并给出了它们的典型应用场景。关于它们进一步的高级话题,将在后续文章中探讨。

参考资料以及进一步阅读
1) MSDN
2) UML Reference Manual, 2nd Edition
3) Abraham Silberschatz, Peter Baer Galvin, Greg Gagne. Operating System Concepts, 6th Edition. John Wiley & Sons, Inc/高等教育出版社影印, 2002.5
4) David R. Butenhof/于磊,曾刚. Programming with POSIX Threads. Addison Wesley/中国电力出版社, 2003

关于作者
田海立,硕士,国家系统分析师,中国系统分析员协会顾问团专业顾问。您可以通过 haili.tian@csai.cn 或 tianhaili@nju.org.cn 与他联系,到 http://blog.csdn.net/thl789/ 看他最新的文章。



版权声明:

◇ 本文为作者原创作品,版权归作者所有。
◇ 为了学习和研究,可转载本文,但必须与原文的内容和格式保持一致,并给出原文的链接!http://blog.csdn.net/thl789/archive/2006/01/17/582246.aspx

Thursday, November 15, 2007

uCOS研究]在MC68HC908GP32上移植μC/OS-II

在前几讲中,介绍了μC/OS-II的概念、工作机制,还介绍了μC/OS-II在Intel
80X86CPU上的移植,相信读者通信学习,已经对移植的过程和步骤有了一定的了解。ΜC/OS-II最初是为摩托罗拉68HC11系列单片机设计的。68HC11系列单片机有外部总线,可以外接RAM和ROM;而没有外部总线8位MCU。由于RAM容量的限制,移植就存在一定的困难;但对于有些8位的MCU,将μC/OS-II移植到MOTOROLA
MC68H908GP32(以下简称GP32)上。
一、在GP32上移植μC/OS-11的主要问题
在第(4)讲中,介绍过要移植μC/OS-11,目标处理必须满足以下要求:
(1)处理器的C编译器能产生可重入代码;
(2)用C语言就可以打开和关闭中断;
(3)处理器支持中断,并且能产生定时中断(通常在10~100Hz之间);
(4)处理器支持足够的RAM,保存全局变量和作为多任务环境下的任务堆栈。
(5)处理器有将堆栈指针和其他CPU寄存器读出和存储到堆栈或内存中的指令。
编译后的μC/OS-II的内核大约有6~10KB;如果只保留最核心的代码,则最小可压缩到2KB。RAM的占用与系统中的任务数有关,任务堆栈要占用大量的RAM空间,堆栈的大小取决于任务的局部变量、缓冲区大小及可能的中断嵌套的层数。所以,所要移植的系统中必须有足够的RAM资源。而像MOTOROLA6805系列的8位MCU,由于RAM资源太小且堆栈指针是固定的,不能满足上面的第(4)条和第(5)条要求,所以μC/OS-II不能在这类处理器上运行。
GP32是68HC08家庭的成员,具有512字节的片内RAM,32K字节的片内Flash,8MHz总线时钟。内部寄存器包括1个8位累加器A,1个16位索引寄存器X,1个16位堆栈指针寄存器SP,1个16位程序指针寄存器PC及1个8位标志寄存器CCR。与6805系列MCU相比,68HC08系列MCU的堆栈指针为16位,可以自由寻址。这就满足了移植条件(5),且可以使用C编译器生成代码。GP32的用户手册可以在摩托罗拉公司的主页上下载,http://www.motorola.com。
在GP32上移植μC/OS-II的主要困难还是RAM资源太少。为了移植成功,必须采取措施减少RAM的用量,包括限制系统中的任务数量、仔细修改μC/OS-II内核、去掉不使用的部分、限制任务的断嵌套层数、在任务中尽量减少使用局部变量等等。在采取了上述措施后,可将RAM用量减少到最低。但需要提醒的是,为了节省堆栈空间而采用的限制中断嵌套层数的方法将影响系统的实时性能。所以,在GP32上移植μC/OS-II更多的是一种演示,能更好地说明μC/OS-II内核的可裁剪性和灵活性。
在本讲中将介绍一个移植实例。为了减少RAM用量,在本例中只运行了2个用户任务。尽量减池一内核中不必要的模块(包括由箱、消息队列、内存管理等),去掉了任务挂起、唤醒和删除等扩展功能,但仍然支持任务的创建和管理,也保留了信号量模块来用来任务间的通讯。
二、工具和运行环境
要实现μC/OS-II向GP32的移植,需要一个面向MC68HC08的C编译器。笔者使用的是HIWARE公司的C编译器。移植过程同样适用于MC68HC08家庭的其他成员。
三、移植中所需修改的文件
首先是编写整个项目的公共头文件include.h,这个文件定义使用内核中的哪些模块。Include.h会被所有的C源程序引用。还要修改和CPU相关的三个文件,分别是头文件OS_CPU08.H、汇编代码文件OS_CPU08.ASM和C代码文件OS_CPU08.C。
1.include.h文件
include.h是主头文件,在所有后缀名为.C的文件的开始都包含include.h文件。文件中可以内核进行裁剪。在本例中定义如下:
#define OS_MAX_EVENTS 2 /*共用了2个信号量*/
#define OS_MAX_MEM_PART 0 /*不使用内存块功能*/
#define OS_MAX_QS 0 /*不使用消息队列功能*/
#define OS_MAX_TASKS 3 /*共有3个任务(包括空闲任务)*/
#define OS_LOWEST_PRIO 20 /*定义最低优先级20*/
#define OS_TASK_IDLE_STK_SIZE 60 /*定义空闲任务堆栈60字节*/
#define OS_TASK_STAT_EN 0 /*不使用统计任务*/
#define OS_MBOX_EN 0 /*不使用消息邮箱功能*/
#define OS_MEM_EN 0 /*不包括内存管理部分代码*/
#define OS_Q_EN 0 /*不包括消息队列部分代码*/
#define OS_SEM_EN 1 /*定义包括信息量相关代码*/
#define OS_TASK_CHANGE_PRIO_EN 0 /*不包括任务优先级动态改变代码*/
#define OS_TASK_CREATE_EN1 /*包括任务创建函数代码*/
#define OS_TASK_CREATE_EXT_EN 0 /*不包括带扩展功能的任务创建函数*/
#define OS_TASK_DEL_EN 0 /*不包括删除任务函数代码*/
#define OS_TASK_SUSPEND_EN 0 /*不包括任务挂起和唤醒函数代码*/
#define OS_TICKS_PER_SEC 10 /*定义每秒的时钟节拍数*/
对于不同类型的处理器,还需要改写include.h文件,增加自己的头文件,但必须加在文件末尾。在安装μC/OS-II的时候,附带了几个移植实例,例如,针对Intel
80X86的代码安装到IIL目录下。我们为GP32编写的移植实例可放在IIHC08下,这样,为GP32改写的include.h文件中应该加入下列语句:
#include "iiHC08_CPU08.ASM"
#include "iiHC08_CPU08.C"
#include "iiHC08_CPU08.H"
2.OS_CPU08.H文件
OS_CPU08.H文件中定义了硬件相关的基本信息:
/*数据类型*/
typedef unsigned char BOOLEAN;
typedef unsigned char INT8U;
typedef signed char INT8S;
typedef unsigned short INT16U;
typedef signed short INT16S;
typedef unsigned long INT32U;
typedef signed long INT32S;
/*定义堆栈增长方向*/
#define OS_STK_GROWTH1 /*堆栈由高地址向低地址增长*/
/*定义堆栈单位*/
#define OS_STK INT8U
/*定义进入临界代码区开关中断宏*/
#define OS_ENTER_CRITICAL() asm sei
#define OS_EXIT_CRITICAL() asm cli
#define OS_TASK_SW() asm swi
(1)数据类型
由于不同的处理器有不同的字长,μC/OS-II的移植需要重新定义一系列的数据结构。具体字长还和使用的C编译器有关。在GP32中堆栈是按字节操作的,堆栈数据类型OS_STK声明为8位。μC/OS-II中所有任务的堆栈都必须用OS_STK声明。
(2)代码临界区
μC/OS-II在进入系统临界代码区之前要关闭中断,等到退出临界区后再打开,从而保护核心数据不被多任务环境下的其他任务或中断破坏。在GP32中,开关中断可以通过汇编指令CLI和SEI来实现。所以μC/OS-II中的宏OS_ENTER_CRITICAL()定义为指令SEI,OS_EXIT_CRITICAL()定义为指令CLI。
(3)堆栈增长方向
GP32的堆栈是由高地址向低地址方向增长的,所以常量OS_STK_GPOWTH必须设置为1。
(4)OS_TASK_SW()函数的定义
在μC/OS-II中,OS_TASK_SW()用来实现任务切换。就绪任务的堆栈初始化应该模拟一次中断发生后的样子,堆栈中应该按进栈次序设置好各个寄存器的内容。OS_TASK_SW()函数模拟一次中断过程,在中断返回的时候进行任务切换。GP32中可采用软中断指令SWI实现任务切换。中断服务程序的入口点必须指向汇编函数OSCtxSw()。
OS_TASK_SW()的定义:
#define OS_TASK_SW() asm swi
3.OS_CPU08.ASM文件
μC/OS-II的移植需要改写OS_CPU08.ASM中的4个函数:OSStartHighRdy()、OSCtxSw()、OSIntCtxSw()和OSTickISR()。
(1)OSStartHighRdy()函数
该函数由SStart()函数调用,功能是运行优先级最高的就绪任务。在调用OSStart()之前,必须先调用OSInit(),并且已经至少创建了一个任务。为了启动任务,OSStartHighRdy()首先找到当前就绪的优先级最高的任务(OSTCBHighRdy中保存有优先级最高任务的任务控制块-TCB的地址),并从任务的任务控制块(OS_TCB)中找到指向堆栈的指针,然后从堆栈中弹出全部寄存器的内容,运行RTE中断返回。由于任务创建时堆栈的结构就是按中断后的堆栈结构初始化的,执行RET指令后就切换到新任务(有关μC/OS-II的任务切换机制,请参考系列讲座的第2讲)。对于OSStartHighRdy的代码,我们采用在C中嵌入汇编的方法编写。需要说明的是,由于GP32中有512字节RAM,所以地址指针必须是16位的;而GP32中累加寄存器A为8位,所以用累加器A传递地址必须进行两次读入、输出操作。
Void OSStartHighRdy(void)
{asm
{
jsr OSTaskSwHook //调用用户定义接口函数
lda OSRunning //设置OSRunning变量,标志进入多任务模式
inca
sta OSRunning
ldx OSTCBHighRdy //取得最高优先级就绪任务TCB地址
stx OSTCBCur //保存到OSTCBCur中
pshx
ldx OSTCBHighRdy:1//保存地址的第二个字节
stx OSTCBCur:1
pulh
lda 0,X //载放就绪任务堆栈指针
psha
ldx 1,X //载入就绪任务堆栈指针第二个字节
pulh
txs
pulh //恢复索引寄存器内容
rti //中断返回,运行新任务
}}
(2)OSCtxSw()函数
OSCtxSw()是一个任务级的任务切换函数(在任务中调用,区别于在中断程序中调用的OSIntCtxSw())。在GP32上实现,可通过执行一条软中断指令SWI来实现任务切换。软中断向量指向OSCtxSw()。在μC/OS-II中,如果任务调用了某个函数,而该函数的执行结果可能造成系统任务新调度(例如试图唤醒一个优先级更高的任务),则在函数的末尾会调用OSSched();如果OSSched()将查找当前就绪的优先级最高的任务,若不是当前任务,则判断是否需要进行任务调度,并找到该任务控制块OS_TCB的地址,将该地址拷贝到变量OSTCBHighRdy中,然后通过宏OS_TASK_SW()执行软中断进行任务切换。在此过程中,变量OSTCBCur始终包含一个指向当前运行任务OS_TCB的指针。OSCtxSw()的汇编代码如下:
Void OSCtxSw(void)
{asm
{pshh //保存X寄存器
tsx
pshx
pshh
dx OSTCBCur //载入当前任务的TCB指针
pshx
ldx OSTCBCur:1 //载入TCB的第二个字节
pulh
pula
sta 0,x //保存当前堆栈指针
pula
sta 1,x
jsr OSTaskSwHook //调用用户定义的接口函数
lda OSPrioHighRdy //设置OSPrioCur=OSPrioHighRdy
sta OSPrioCur
pshx
ldx OSTCBHighRdy:1
stx OSTCBCur:1
pulh
lda 0,x //载入堆栈指针
psha
ldx,1,x
pulh
txs
pulh //恢复索引寄存器内容
rti //中断返回,切换任务
}}
(4)OSTickISR()函数
在μC/OS-II中,当调用OSStart()启动多任务环境后,时钟中断的使用是非常重要的。在时钟中断程序中负责处理所有与定时相关的工作,如任务的延时、等待操作等等。在时钟中断中将查询处于等待状态的任务,判断是否延时结束,否则将重新进行任务调度。
为GP32编写的函数OSTickISR()的代码如下:
void OSTickISR()void{
asm{
pshh
LDA T1SC
BCLR 7,T1SC //允许中断嵌套
}
OsintEnter(); /*标志进入中断*/
OSTimeTick(); /*调用时钟节拍函数*/
OSlntExit(); /*标志退出中断*/
Asm{
Pulh
Rti
}}
和μC/OS-II中的其他中断服务程序一样,OSTickISR()首先在被中断任务堆栈中保存CPU寄存器的值,然后调用OSIntEnter()。μC/OS-II要求在中断服务程序开头调用OSIntEnter(),其作用是将记录中断嵌套层数的全局变量OSIntNesting加1。如果不调用OSIntEnter(),直接将OSIntNesting加1也是允许的。随后,OSTickISR()调用OSTimeTick(),检查所有处于延时等待状态的任务,判断是否有延时结束就绪的任务。在OSTickISR()的最后调用OSIntExit(),如果在中断中(或其他嵌套的中断)有更高优先级的任务就绪,并且当前中断为中断嵌套的最后一层,OSIntExit()将进行任务调度。注意:如果进行了任务调度,OSIntExit()将不同志返回调用者,而是用新任务的堆栈中的寄存器数值恢复CPU现场,然后用IRET实现任务切换。如果当有中断不是中断嵌套的最后一层,或中断中没有改变任务的就绪状态,OSIntExit()将返回调用者OSTickISR(),最后OSTickISR()返回被中断的任务。
4.OS_CPU08.C文件
μC/OS-II的移植需要用户在OS_CPU08.C中定义6个函数:
OSTaskStkInit()
OSTaskCreateHook()
OSTaskDelHook()
OSTaskSwHook()
OSTaskStatHook()
OSTimeTickHook()
实际需要定义的只有OSTaskStkInit()函数,其他5个函数需要声明,但不一定有实际内容。这5个函数都是用户定义,所以OS_CPU08.C中只有定义,没有给出代码。如果用户需要使用这些函数,请将文件OS_CFG.H中的#define
constant OS_CPU_HOOKS_EN设为1,设为0表示不使用这些函数。
OSTaskStkInit()函数由任务创建函数OSTaskCreate()或OSTaskCreateExt()调用,用来初始化任务的堆栈。初始状态的堆栈模拟发生一次中断后的堆栈结构,按照中断后的进栈次序预留各个寄存器存储空间;而中断返回地址指向任务代码的起始地址。当调用OSTaskCreate()或OSTaskCreateExt()创建一个新任务时,需要传递的参数是:任务代码的起始地址、参数指针(pdata)、任务堆栈顶端的地址、任务的优先级。OSTaskCreateExt()还需要一些其他参数,但与OSTaskStkinit()没有关系。OSTaskStkInit()只需要以上提到的3个参数(task、pdata和ptos)。堆栈初始化工作结束后,OSTaskStkInit()返回新的堆栈栈顶指针,OSTaskCreate()OSTaskCreateExt()将指针保存在任务的OS_TCB中。
Void*OSTaskStklint(void(*task)(void*pd),void*pdata,void*ptos,INT16U
opt)
{
INT16U *stk;
stk=(INT16U*)ptos; /*保存堆栈指针*/
*--stk=(INT16U)(task); /保存程序计数器内容*/
*--stk=(INT16U)(0x00); /初始化X和A寄存器内容*/
--stk=(INT16U)(0x00); /*初始化CCR和H寄存器*/
return((void*)stk);
}
其余的几个函数:OSTaskCreateHook()、OSTaskDelHook()、OSTaskSwHook()、OSTaskStatHook和OSTimeTickHook()均由用户自定义。
四、制作用户自己的项目
在为内核编写了上述与硬件相关的代码以后,用户就可以为自己的项目编写实际的代码了。在本例中,用户任务共有两个。任务1在初始化时钟中断以后,就进入了一人死循环。在这个循环里,任务1一方面以1s(秒)为周期改变并行I/O口PORTA第0个引脚的输出电压,另一方面每隔4s便向任务2发送1个信号。而任务2则始终等待任务1发来的信号,一旦收到信号,便改变并行I/O口PORTA第1个引脚的输出电压。具体的代码如下:
/*****************************************
* EXE2.C
*******************************************/
#include
#include "includes.h"
Byte PORTA @0x0000; /*并口A地址$0000*/
Byte DDRA @0x0004; /*并口A方向寄存器地址$0004*/
Byte T1SC @0x0020; /*定时器控制寄存器地址$0020*/
Byte T1MODH@0x0023; /*定时器模式寄存器地址$0023*/
OS_EVENT *Semaphore;
#define TASK_STK_SIZE 64 /*任务堆栈大小64字节*/
INT8U Task1Stk[TASK_STK_SIZE]; /*定义任务1堆栈*/
INT8U Task2Stk[TASK_STK_SIZE]; /*定义任务2堆栈*/
Void Hardwareinit(void);
Void Task1(void*pdata)
{int count=0;
/*int count=0;
/*初始化定时器*/
asm{
LDA #0x50
STA T1SC
LDHX #0x0333 //设定定时器间隔100ms
STHX T1MODH
CLI
}
for(;;){
PORTA&=0xFE;
OSTimeDly(5); /*延时0.5s*/
PORTA|=0x01;
/*延时0.5s*/
DSTimeDly(5);
Count++;
If(count= =4){
OSSemPost(Semaphore);
Count=0;
}
}}
void Task2(void *pdata)
{
Byte err;
For(;;){
OSSemPend(Semaphore,0,&err);
PORTA&=0xFD:
OSSemPend(Semaphore,0,&err);
PORTA|=0x02;
}
}
void main(void){
Hardwarelnit(); /*完成硬件的初始化工作*/
Oslint(); /*初始化多任务环境*/
Semaphore=OSSemCreate(0);
OSTaskCreate(Task1,(void*)0,(void*)&Task1Stk
[TASK_STK_SIZE],10);
OSTaskCreate(Task2,void*)0,(void*)&Task2Stk
[TASK_STK_SIZE],9);
OSStart();
}
在主程序main()中,用户必须先调用OSInit(),然后创建各个任务和信号量等,最后调用OSStart(),以启动内核运行,开始正常的任务调度。
本例中尽量减小了对RAM的需求:假如中断嵌套层数不超过三层,所需事件只有一个,即只需要一个事件控制块;应用中对μC/OS-II提供的功能进行最大限度的裁剪,能不用的尽量不用。采用了上述措施后,μC/OS-II的RAM使用情况大致如下:μC/OC-II所使用的全局变量占用22字节,事件控制块占用12字节。此外,当系统初始化时,还需要最小30字节的系统堆栈用于初始化TCB,并传递参数。以上为μC/OS-II中系统所必需的RAM,计64字节。
假设给每个任务分配64个字节的堆栈空间,其中用于任务控制块17字节,允许三层中断嵌套要用18字节,任务切换时的栈结构要用8字节。由于程序是用C语言编写的,确切的子函数嵌套调用层数是不知道的。如果不使用本身需要缓冲区的C语言函数,如printf()等,任务中程序调用所产生的嵌套层数不超过10层,则64字节中还能分析给任务局部变量的空间只剩下1字节了。这是能分配任务的最小RAM空间了。
综上所述,GP32的512字节RAM可分为8个64字节的RAM块。如果运行4个任务,能留给应用程序的RAM也只剩下128字节了。如果在GP32上运行μC/OS-II,且不多于8个任务,则任务调度表可以再简化,不需要调度64个任务,只调度8个任务就可以了。

μC/OS-II 实时操作系统

μC/OS是一个特殊风格的嵌入式操作系统,它有多个版本,可以适应从x86到8051的各种不同类型不同规模的嵌入式系统,原先代码开放,但某些改进版本,代码不开放。

1、μC/OS-II 的特点

可移植性:绝大部分μC/OS的源码是用移植性很强的ANSI C写的,和微处理器硬件相关的那部分是用汇编语言写的,汇编语言写的部分已经压到最低限度。


可固化:μC/OS是为嵌入式应用而设计的,用户可以通过固化手段将μC/OS嵌入到产品中成为产品的一部分。


可裁减:μC/OS系统由多个相对独立的、短小精炼的目标模块组成,用户可根据需要选择适当模块来裁剪和配置系统,这样,通过目标模块之间的按需组合,可以减少产品中的μC/OS所需的存储空间,这种裁减性是靠条件编译实现的。


占先式:μC/OS完全是占先式的实时内核,即μC/OS总是运行就绪条件下优先级最高的任务。


多任务:μC/OS可以管理64个任务,每个任务的优先级必须是不同的,其中系统占用8个,应用程序最多可以有56个任务。


可确定性:全部μC/OS的函数调用与服务的执行时间是可知的,即μC/OS系统服务的执行时间不依赖于应用程序任务的多少。


任务栈:μC/OS允许每个任务有不同的堆栈空间,以便压低应用程序对RAM的需求。


系统服务:μC/OS有多个相对独立的、短小精炼的目标模块组成,这些模块有:任务管理、时间管理、任务间的通信与同步、内存管理。其中:任务管理提供建立任务、删除任务、请求删除任务、任务的堆栈检查、改变任务的优先级、挂起任务、恢复任务和任务信息查询的系统调用;时间管理提供任务延时、取消任务延时和查询系统时间的系统调用;任务间通信与同步提供基于信号量、邮箱和消息队列机制的系统调用;内存管理提供内存分区的建立、分配、释放和查询的系统调用。


中断管理:中断可以使正在执行的任务暂时挂起,如果优先级更高的任务被该中断唤醒,则高优先级的任务在中断嵌套全部退出后立即执行,中断嵌套层数可达255层。


稳定性和可靠性:μC/OS自1992年以来已经有好几百个商业应用。

2、μC/OS-II 内核

实时操作系统对系统资源进行管理。主要包括任务调度、时间管理、内存管理、资源管理(信号灯、邮箱、消息队列)四大部分。μC/OS所有系统服务均由内核提供。内核将应用系统和底层硬件结合成一个完整的实时系统。


⑴ 任务调度

任务可以是一个无限的循环,也可以是在一次执行完毕后被删除掉。这里要注意的是,任务代码并不是被真正的删除了,而只是μC/OS-Ⅱ不再理会该任务代码,所以该任务代码不会再运行。

任务看起来与任何C函数一样,具有一个返回类型和一个参数,只是它从不返回。任务的返回类型必须被定义成void型。

任务的调度包括如何在用户的应用程序中建立任务、删除任务、改变任务的优先级、挂起和恢复任务,以及获得有关任务的信息。它主要由下列函数组成:

◇ 建立任务OSTaskCreate()

◇ 建立任务OSTaskCreateExt()

◇ 堆栈检验OSTaskStkChk()

◇ 删除任务OSTaskDel()

◇ 请求删除任务OSTaskDelReq()

◇ 改变任务的优先级OSTaskChangePrio()

◇ 挂起任务OSTaskSuspend()

◇ 恢复任务OSTaskResume()

◇ 获得有关任务的信息OSTaskQuery()

以上函数可以在OS_TASK文件中找到。


① 建立任务函数OSTaskCreate()或OSTaskCreateExt()

想让μC/OS-Ⅱ管理用户的任务,用户必须要先建立任务。用户可以通过传递任务地址和其它参数到以下两个函数之一来建立任务:OSTaskCreate() 或OSTaskCreateExt()。OSTaskCreate()与μC/OS是向下兼容的,OSTaskCreateExt()是OSTaskCreate()的扩展版本,提供了一些附加的功能。用两个函数中的任何一个都可以建立任务。

任务可以在多任务调度开始前建立,也可以在其它任务的执行过程中被建立。在开始多任务调度(即调用OSStart())前,用户必须建立至少一个任务。任务不能由中断服务程序(ISR)来建立。


OSTaskCreate() 格式为:

INT8U OSTaskCreate (void (*task)(void *pd), void *pdata, OS_STK *ptos, INT8U prio)

OSTaskCreate()需要四个参数:

task 是任务代码的指针,

pdata 是当任务开始执行时传递给任务的参数的指针,

ptos 是分配给任务的堆栈的栈顶指针,

prio 是分配给任务的优先级。

返回值:OS_NO_ERR 任务成功建立

OS_PRIO_EXIST 此优先级上已有一个任务

OS_PRIO_INVALID 此优先级上已有一个休眠的任务

 


OSTaskCreateExt() 格式为:

INT8U OSTaskCreateExt (void (*task)(void *pd),

void *pdata,

OS_STK *ptos,

INT8U prio,

INT16U id,

OS_STK *pbos,

INT32U stk_size,

void *pext,

INT16U opt)


OSTaskCreateExt()需要九个参数!前四个参数(task,pdata,ptos和prio)与OSTaskCreate()的四个参数完全相同,连先后顺序都一样。

id 参数为要建立的任务创建一个特殊的标识符。该参数在μC/OS以后的升级版本中可能会用到,但在μC/OS-Ⅱ中还未使用。这个标识符可以扩展μC/OS-Ⅱ功能,使它可以执行的任务数超过目前的64个。但在这里,用户只要简单地将任务的id设置成与任务的优先级一样的值就可以了。

pbos 是指向任务的堆栈栈底的指针,用于堆栈的检验。

stk_size 用于指定堆栈成员数目的容量。也就是说,如果堆栈的入口宽度为4字节宽,那么stk_size为10000是指堆栈有40000个字节。该参数与pbos一样,也用于堆栈的检验。

pext 是指向用户附加的数据域的指针,用来扩展任务的OS_TCB。

opt 用于设定OSTaskCreateExt()的选项,指定是否允许堆栈检验,是否将堆栈清零,任务是否要进行浮点操作等等。μCOS_Ⅱ.H文件中有一个所有可能选项(OS_TASK_OPT_STK_CHK,OS_TASK_OPT_STK_CLR和OS_TASK_OPT_SAVE_FP)的常数表。每个选项占有opt的一位,并通过该位的置位来选定(用户在使用时只需要将以上OS_TASK_OPT_???选项常数进行位或(OR)操作就可以了)。

 


② 任务堆栈和堆栈检验函数OSTaskStkChk()

每个任务都有自己的堆栈空间。堆栈必须声明为OS_STK类型,并且由连续的内存空间组成。用户可以静态分配堆栈空间(在编译的时候分配)也可以动态地分配堆栈空间(在运行的时候分配)。

堆栈检验函数OSTaskStkChk()可以用来统计从堆栈栈底开始的堆栈空闲空间。这样用户就可以避免为任务分配过多的堆栈空间,从而减少自己的应用程序代码所需的RAM(内存)数量。


OSTaskStkChk() 格式为:

INT8U OSTaskStkChk (INT8U prio, OS_STK_DATA *pdata)

prio 是想检验的任务的优先级。

0S_STK_DATA数据结构用来保存有关任务堆栈的信息。

 


③ 删除任务OSTaskDel()

通过调用OSTaskDel()就可以完成删除任务的功能。OSTaskDel()一开始应确保用户所要删除的任务并非是空闲任务,因为删除空闲任务是不允许的。


OSTaskDel() 格式为:

INT8U OSTaskDel (INT8U prio)

prio是想删除的任务的优先级。

 


④ 请求删除任务OSTaskDelReq()

有时候,如果任务A拥有内存缓冲区或信号量之类的资源,而任务B想删除该任务,这些资源就可能由于没被释放而丢失。在这种情况下,用户可以想法子让拥有这些资源的任务在使用完资源后,先释放资源,再删除自己。用户可以通过OSTaskDelReq()函数来完成该功能。

发出删除任务请求的任务(任务B)和要删除的任务(任务A)都需要调用OSTaskDelReq()函数。

任务B在需要请求删除任务的情况下调用OSTaskDelReq()函数。如果要被删除的任务不存在(即任务已被删除或是还没被建立),OSTaskDelReq()返回OS_TASK_NOT_EXIST。如果OSTaskDelReq()的返回值为OS_NO_ERR,则表明请求已被接受但任务还没被删除。

任务A通过调用OSTaskDelReq(OS_PRIO_SELF)来确认自己是否需要被删除。

 


⑤ 改变任务的优先级OSTaskChangePrio()

在用户建立任务的时候会分配给任务一个优先级。在程序运行期间,用户可以通过调用OSTaskChangePrio()来改变任务的优先级。μC/OS-Ⅱ允许用户动态的改变任务的优先级。


OSTaskChangePrio() 格式为:

INT8U OSTaskChangePrio (INT8U oldprio, INT8U newprio)

oldprio 旧的优先级

newprio 新的优先级


μC/OS-Ⅱ不允许多个任务具有相同的优先级,所以OSTaskChangePrio()需要检验新优先级是否是合法的(即不存在具有新优先级的任务)。如果新优先级是合法的,则保留这个优先级。

 


⑥ 挂起任务OSTaskSuspend()

通过调用OSTaskSuspend()函数可以挂起任务。被挂起的任务只能通过调用OSTaskResume()函数来恢复。


OSTaskSuspend() 格式为:

INT8U OSTaskSuspend (INT8U prio)

prio是想挂起的任务的优先级。

返回值:OS_NO_ERR 任务被成功挂起

OS_SUSPEND_IDLE 任务已休眠,不能被挂起

OS_PRIO_INVALID 此优先级大于OS_MAX_TASK

OS_TASK_SUSP_PRIO 此优先级没有被注册的任务

 


⑦ 恢复任务OSTaskResume()

通过调用OSTaskResume()函数来恢复被挂起的任务。


OSTaskResume() 格式为:

INT8U OSTaskResume (INT8U prio)

prio是想恢复的任务的优先级。

返回值:OS_NO_ERR 任务被成功激活

OS_TASK_NOT_SUSP 任务并没有被挂起

OS_PRIO_INVALID 此优先级大于OS_MAX_TASK

OS_TASK_NOT_EXIST 此优先级没有被注册的任务

 


⑧ 获得有关任务的信息OSTaskQuery()

用户的应用程序可以通过调用OSTaskQuery()来获得自身或其它应用任务的信息。


OSTaskQuery() 格式为:

INT8U OSTaskQuery (INT8U prio, OS_TCB *pdata)

prio是想获得信息的任务的优先级。

OS_TCB是想获得信息的任务控制块

 


⑵ 时间管理

μC/OS-Ⅱ(其它内核也一样)要求用户提供定时中断来实现延时与超时控制等功能。这个定时中断叫做时钟节拍,它应该每秒发生10至100次。时钟节拍的实际频率是由用户的应用程序决定的。时钟节拍的频率越高,系统的负荷就越重。

与时钟节拍有关的系统服务主要有:

◇ 任务延时函数 OSTimeDly()

◇ 按时分秒延时函数OSTimeDlyHMSM()

◇ 结束延时函数 OSTimeDlyResume()

◇ 设置系统时间 OSTimeGet()

◇ 返回系统时间 OSTimeSet()

它们都包含在OS_TIME.C文件中。

 


① 任务延时函数 OSTimeDly()

OSTimeDly()函数可以使用户按时钟节拍数来定义延时时间。该函数的参数是延时的时钟节拍数_____一个1 到65535之间的数。使用该函数,用户的应用程序需要知道延时时间对应的时钟节拍的数目。

任务调用OSTimeDly()后,一旦规定的时间期满或者有其它的任务通过调用OSTimeDlyResume()取消了延时,它就会马上进入就绪状态。注意,只有当该任务在所有就绪任务中具有最高的优先级时,它才会立即运行。


OSTimeDly() 格式为:

void OSTimeDly (INT16U ticks)

ticks 延时的时钟节拍数(1~65535)

 


② 按时分秒延时函数 OSTimeDlyHMSM()

OSTimeDlyHMSM()函数可以使用户按小时(H)、分(M)、秒(S)和毫秒(m)来定义延时时间。

任务调用OSTimeDlyHMSM()后,一旦规定的时间期满或者有其它的任务通过调用OSTimeDlyResume()取消了延时,它就会马上处于就绪态。同样,只有当该任务在所有就绪态任务中具有最高的优先级时,它才会立即运行。


OSTimeDlyHMSM() 格式为:

INT8U OSTimeDlyHMSM (INT8U hours, INT8U minutes, INT8U seconds, INT16U milli)

hours 小时

minutes 分

seconds 秒

milli 毫秒

 


③ 结束延时函数 OSTimeDlyResume()

μC/OS-Ⅱ允许用户结束延时正处于延时期的任务。通过调用OSTimeDlyResume()可以取消指定任务的延时,不等待延时期满,就使指定的任务处于就绪态。实际上,OSTimeDlyResume()也可以唤醒正在等待事件的任务,虽然这一点并没有提到过。在这种情况下,等待事件发生的任务会考虑是否终止等待事件。

该函数的参数是要恢复的任务的优先级。


OSTimeDlyResume() 格式为:

INT8U OSTimeDlyResume (INT8U prio)

prio 是想唤醒的任务的优先级

返回值:OS_NO_ERR 任务被成功唤醒

OS_TIME_DLY 任务并没有休眠

OS_PRIO_INVALID 此优先级大于OS_MAX_TASK

OS_TASK_NOT_EXIST 此优先级没有被注册的任务

 


④ 设置系统时间 OSTimeSet()

通过调用OSTimeSet()可以改变时钟节拍计数器的值。

该函数的参数是时钟节拍计数器的新值。


OSTimeSet() 格式为:

void OSTimeSet (INT32U ticks)

ticks 是时钟节拍计数器的新值

 


⑤ 返回系统时间 OSTimeGet()

通过调用OSTimeGet()可以获得时钟节拍计数器的当前值。


OSTimeGet() 格式为:

INT32U OSTimeGet (void)

返回值:ticks 时钟节拍计数器的当前值

 


⑶ 内存管理

内存管理模块用来对需要管理的内存块进行简单的管理:分配(动态分配)和释放(动态回收)。它主要由一个数据结构体和五个函数组成:

◇ 内存控制块数据结构OS_MEM

◇ 内存分区建立函数OSMemCreate()

◇ 内存块分配函数OSMemGet()

◇ 内存块释放函数OSMemPut()

◇ 内存分区状态查询函数OSMemQuery()

◇ 内存控制块链表初始化函数OSMemInit()

 


① 内存控制块数据结构OS_MEM

为了便于内存的管理,在μC/OS-II中使用内存控制块(memory control blocks)的数据结构来跟踪每一个内存分区。


其定义如下:

typedef struct {

void *OSMemAddr;

void *OSMemFreeList;

INT32U OSMemBlkSize;

INT32U OSMemNBlks;

INT32U OSMemNFree;

} OS_MEM;


系统中每个内存分区必须有一个属于自己的内存控制块,只有这样,内存管理模块中的五个函数才能对这个内存分区进行管理和操作。


.OSMemAddr是指向内存分区起始地址的指针。它在建立内存分区时被初始化,在此之后就不能更改了。

.OSMemFreeList是指向下一个空闲内存控制块或者下一个空闲的内存块的指针,具体含义要根据该内存分区是否已经建立来决定。

.OSMemBlkSize是内存分区中内存块的大小,是用户建立该内存分区时指定的。

.OSMemNBlks是内存分区中总的内存块数量,也是用户建立该内存分区时指定的。

.OSMemNFree是内存分区中当前可以得空闲内存块数量。


如果要在μC/OS-II中使用内存管理,需要:

Ⅰ.打开配置文件OS_CFG.H,将开关量OS_MEM_EN设置为1:#define OS_MEM_EN 0

Ⅱ.打开配置文件OS_CFG.H,设置系统要建立的任务分区的数量:#define OS_MAX_MEM_PART,该常数值至少应为2。

 


② 内存分区建立函数OSMemCreate()

在使用一个内存分区之前,必须先建立该内存分区。这个操作可以通过调用OSMemCreate()函数来完成。


OSMemCreate() 格式为:

OSMemCreate(OSMemAddr, OSMemNBlks, OSMemBlkSize, &err)

该函数共有4个参数:内存分区的起始地址、分区内的内存块总块数、每个内存块的字节数和一个指向错误信息代码的指针。

 


③ 内存块分配函数OSMemGet()

应用程序调用OSMemGet()函数可以从已经建立的内存分区中申请一个内存块。该函数的唯一参数是指向特定内存分区的指针,该指针在建立内存分区时,由OSMemCreate()函数返回。显然,应用程序必须知道内存块的大小,并且在使用时不能超过该容量。


OSMemGet() 格式为:

void *OSMemGet (OS_MEM *pmem, INT8U *err)

*pmem 是指向特定内存分区的指针

*err 是指向错误信息代码的指针

 


④ 内存块释放函数OSMemPut()

用户创建的任务不再使用申请来的内存块的时候,必须及时的调用OSMemPut()来把内存块释放到相应的内存分区中去。需要注意的是,这个内存块从那个内存分区中申请来的就必须释放到那个内存分区中去,否则会造成系统崩溃;这个用户在编写任务的时候注意就可以避免了。

OSMemGet()和OSMemPut()应该成对使用;


OSMemPut() 格式为:

INT8U OSMemPut (OS_MEM *pmem, void *pblk)

*pmem 是指向特定内存分区的指针

*pblk 空闲内存块链表

 


⑤ 内存分区状态查询函数OSMemQuery()

在μC/OS-II 中,可以使用OSMemQuery()函数来查询一个特定内存分区的相关信息。通过该函数可以知道特定内存分区中内存块的大小、可用内存块数和正在使用的内存块数等信息。所有这些信息都放在一个叫OS_MEM_DATA的数据结构中。


OSMemQuery() 格式为:

INT8U OSMemQuery (OS_MEM *pmem, OS_MEM_DATA *pdata)

*pmem 是指向特定内存分区的指针

*pdata OS_MEM_DATA的数据结构

 


⑷ 资源管理

μC/OS-II 中的资源管理主要包括:信号灯、邮箱、消息队列等。

① 信号灯

信号灯管理模块主要由五个函数组成:

◇ 信号灯初始化函数OSSemInit()

◇ 信号灯获取函数OSSemPend()

◇ 信号灯计数器函数OSSemAccept()

◇信号灯释放函数OSSemPost()

◇ 信号灯删除函数OSSemClear()


Ⅰ、信号灯初始化函数OSSemInit()

OSSemInit()函数初始化一个信号灯,可用来同步对公共资源的存取。


OSSemInit() 格式为:

UBYTE OSSemInit (OS_SEM *psem, UWORD cnt)

*psem 是信号灯指针

cnt 可同时支持的最大进程数

返回值:OS_NO_ERR 信号灯被初始化

 


Ⅱ、信号灯获取函数OSSemPend()

OSSemPend()函数用来获取一个信号灯以及对其保护资源的存取。


OSSemPend() 格式为:

UBYTE OSSemPend(OS_SEM *psem, UWORD timeout)

*psem 是信号灯指针

timeout 以内核时钟节拍为单位的等待时间(1~65534)

返回值:OS_NO_ERR 获得信号灯

OS_SEM_NODATA 信号灯被占用( timeout =OS_NO_SUSP时)

OS_TIMEOUE 信号灯被占用(经过等待timeout 后)

 


Ⅲ、信号灯计数器函数OSSemAccept()

OSSemAccept()函数可用来利用信号灯实现事件计数器的功能。


OSSemAccept() 格式为:

UBYTE OSSemAccept(OS_SEM *psem, UWORD *cnt, UWORD timeout)

*psem 是信号灯指针

*cnt 变量指针,该变量获得计数值

timeout 以内核时钟节拍为单位的等待时间(1~65534)

返回值:OS_NO_ERR 至少有一次事件发生

OS_SEM_NODATA 计时器为0( timeout =OS_NO_SUSP时)

OS_TIMEOUE 计时器为0(经过等待timeout 后)

 


Ⅳ、信号灯释放函数OSSemPost()

OSSemPost()函数用来释放使用完的信号灯,释放被保护资源。


OSSemPost() 格式为:

UBYTE OSSemPost(OS_SEM *psem)

*psem 是信号灯指针

返回值:OS_NO_ERR 信号灯被释放

OS_SEM_OVF 信号灯操作出错(计数值太大)

 


Ⅴ、信号灯删除函数OSSemClear()

OSSemClear()函数用来删除信号灯的计数值。


OSSemClear() 格式为:

UBYTE OSSemClear(OS_SEM *psem)

*psem 是信号灯指针

 


② 邮箱

邮箱管理模块主要由三个函数组成:

◇ 邮箱初始化函数OSMboxInit()

◇ 邮箱获取函数OSMboxPend()

◇邮箱释放函数OSMboxPost()


Ⅰ、邮箱初始化函数OSMboxInit()

OSMboxInit()函数用来初始化一个信箱。


OSMboxInit() 格式为:

UBYTE OSMboxInit(OS_MBOX*pmbox)

*pmbox 是信箱指针

返回值:OS_NO_ERR 信箱被初始化

 


Ⅱ、邮箱获取函数OSMboxPend()

OSMboxPend()函数用来从信箱获取信息。


OSMboxPend()格式为:

UBYTE OSMboxPend(OS_MBOX*pmbox, void OS_FAR *msg, UWORD timeout)

*pmbox 是信箱指针

*msg 接收缓冲区指针

timeout 以内核时钟节拍为单位的等待时间(1~65534)

返回值:OS_NO_ERR 成功获取信息

OS_MBOX_NODATA 信箱中没有信息( timeout =OS_NO_SUSP时)

OS_TIMEOUE 信箱中没有信息(经过等待timeout 后)

 


Ⅲ、邮箱释放函数OSMboxPost()

OSMboxPost()函数用来向信箱发送一个信息。


OSMboxPost()格式为:

UBYTE OSMboxPost(OS_MBOX*pmbox, void OS_FAR *msg, UWORD timeout)

*pmbox 是信箱指针

*msg 发送缓冲区指针

timeout 以内核时钟节拍为单位的等待时间(1~65534)

返回值:OS_NO_ERR 成功发送信息

OS_MBOX_FULL 信箱已满( timeout =OS_NO_SUSP时)

OS_TIMEOUE 信箱已满(经过等待timeout 后)

 


③ 消息队列

消息队列管理模块主要由六个函数组成:

◇ 队列初始化函数OSQueueInit()

◇ 队列状态获取函数OSQueueInfo()

◇ 队列获取函数OSQueuePend()

◇ 队列发送函数OSQueuePost()

◇ 队列头发送函数OSQueueFrontPost()

◇ 队列删除函数OSQueueClear()


Ⅰ、队列初始化函数OSQueueInit()

OSQueueInit()函数用来初始化一个队列。


OSQueueInit() 格式为:

UBYTE OSQueueInit(OS_Q*pq, void OS_HUGE*buffer, UWORD size)

*pq 是队列指针

*buffer 是内核缓冲区指针

size 以字节为单位的内核缓冲区大小

返回值:OS_NO_ERR 队列被初始化

 


Ⅱ、队列状态获取函数OSQueueInfo()

OSQueueInfo()函数用来获取一个队列的状态。


OSQueueInfo() 格式为:

UBYTE OSQueueInfo(OS_Q*pq, UWORD*size, UWORD*used, UBYTE *prio)

*pq 是队列指针

*size 指向变量的指针,将获得队列大小

*used 指向变量的指针,将获得队列中已用字节数

*prio 指向变量的指针,将获得处于等待状态的任务的优先级

返回值:OS_NO_ERR 无错误

 


Ⅲ、队列获取函数OSQueuePend()

OSQueuePend()函数用来从队列中获取一个字节。


OSQueuePend() 格式为:

UBYTE OSQueuePend(OS_Q*pq, UBYTE OS_FAR *msg, UWORD timeout)

*pq 是队列指针

*msg 接收字节指针

timeout 以内核时钟节拍为单位的等待时间(1~65534)

若使 timeout=OS_NO_SUSP ,没有可用字节,函数也会立即返回;若使 timeout=OS_SUSPEND,没有可用字节,函数将一直等到有可用字节才返回。

返回值:OS_NO_ERR 成功获取信息

OS_Q_NODATA 队列中没有信息( timeout =OS_NO_SUSP时)

OS_TIMEOUE 队列中没有信息(经过等待timeout 后)

 


Ⅳ、队列发送函数OSQueuePost()

OSQueuePost()函数用来发送一个字节到队列中。


OSQueuePost() 格式为:

UBYTE OSQueuePost(OS_Q*pq, UBYTE OS_FAR *msg, UWORD timeout)

*pq 是队列指针

*msg 接收字节指针

timeout 以内核时钟节拍为单位的等待时间(1~65534)

若使 timeout=OS_NO_SUSP ,队列没有可用空间,函数也会立即返回;若使 timeout=OS_SUSPEND,队列没有可用空间,函数将一直等到有可用空间才返回。

返回值:OS_NO_ERR 成功发送

OS_Q_FULL 队列满( timeout =OS_NO_SUSP时)

OS_TIMEOUE 队列满(经过等待timeout 后)

 


Ⅴ、队列头发送函数OSQueueFrontPost()

OSQueueFrontPost()函数用来发送一个字节到队列头部。


OSQueueFrontPost() 格式为:

UBYTE OSQueueFrontPost(OS_Q*pq, UBYTE OS_FAR *msg, UWORD timeout)

*pq 是队列指针

*msg 接收字节指针

timeout 以内核时钟节拍为单位的等待时间(1~65534)

若使 timeout=OS_NO_SUSP ,队列没有可用空间,函数也会立即返回;若使 timeout=OS_SUSPEND,队列没有可用空间,函数将一直等到有可用空间才返回。

返回值:OS_NO_ERR 成功发送

OS_Q_FULL 队列满( timeout =OS_NO_SUSP时)

OS_TIMEOUE 队列满(经过等待timeout 后)

 


Ⅵ、队列删除函数OSQueueClear()

OSQueueClear()函数用来删除一个队列中的内容。


OSQueueClear() 格式为:

UBYTE OSQueueClear(OS_Q*pq)

*pq 是队列指针

返回值:OS_NO_ERR 队列中的内容被删除

 

3、μC/OS 51移植

移植的时候内核是不变的,开发者根据自己应用系统的需要来选择实时操作系统内核,开发者不能对内核随意访问,只能使用内核提供的功能服务来开发自己的应用系统。内核确定,那么所提供的系统管理能力,系统服务也就得到了限定。开发者只能在规定的范围内对系统作些改动。

μC/OS 51移植涉及到两个方面:与处理器相关的代码和与应用相关的代码。

⑴ 与处理器相关的代码

这是移植中最关键的部分。内核将应用系统和底层硬件有机的结合成一个实时系统,要使同一个内核能适用于不同的硬件体系,就需要在内核和硬件之间有一个中间层,这就是与处理器相关的代码。处理器不同,这部分代码也不同。

我们在移植时需要自己处理这部分代码,可以自己编写,也可以直接使用已经成功移植的代码。

在μC/OS中这一部分代码包括三个文件:OS_CPU.H, OS_CPU_A.ASM,OS_CPU_C.C。

① OS_CPU.H

包括了用#define定义的与处理器相关的常量,宏和类型定义。

具体来讲有系统数据类型定义,栈增长方向定义,关中断和开中断定义,系统软中断的定义等等。

② OS_CPU_A.ASM

这部分需要对处理器的寄存器进行操作,所以必须用汇编语言来编写。包括四个子函数:OSStartHighRdy(),OSCtxSw(),OSIntCtxSw(),OSTickISR()。

OSStartHighRdy()

在多任务系统启动函数OSStart()中调用。完成的功能是:设置系统运行标志位OSRunning = TRUE;将就绪表中最高优先级任务的栈指针Load到SP中,并强制中断返回。这样就绪的最高优先级任务就如同从中断里返回到运行态一样,使得整个系统得以运转。

OSCtxSw()

在任务级任务切换函数中调用的。任务级切换是通过SWI或者TRAP人为制造的中断来实现的ISR的向量地址必须指向OSCtxSw()。这一中断完成的功能:保存任务的环境变量(主要是寄存器的值,通过入栈来实现),将当前SP存入任务TCB中,载入就绪最高优先级任务的SP,恢复就绪最高优先级任务的环境变量,中断返回。这样就完成了任务级的切换。

OSIntCtxSw()

在退出中断服务函数OSIntExit()中调用,实现中断级任务切换。由于是在中断里调用,所以处理器的寄存器入栈工作已经做完,就不用作这部分工作了。具体完成的任务:调整栈指针(因为调用函数会使任务栈结构与系统任务切换时堆栈标准结构不一致),保存当前任务SP,载入就绪最高优先级任务的SP,恢复就绪最高优先级任务的环境变量,中断返回。这样就完成了中断级任务切换。


OSTickISR()

系统时钟节拍中断服务函数,这是一个周期性中断,为内核提供时钟节拍。频率越高系统负荷越重。其周期的大小决定了内核所能给应用系统提供的最小时间间隔服务。一般只限于ms级(跟MCU有关),对于要求更加苛刻的任务需要用户自己建立中断来解决。该函数具体内容:保存寄存器(如果硬件自动完成就可以省略),调用OSIntEnter(),调用OSTimeTick(),调用OSIntExit(),恢复寄存器,中断返回。


③ OS_CPU_C.C

μC/OS中共定义了6个函数在该文件中。但是最重要的是OSTaskStkInit(),其他都是对系统内核的扩展时用的。

OSTaskStkInit()

是在用户建立任务时系统内部自己调用的,对用户任务的堆栈进行初始化。使建立好的进入就绪态任务的堆栈与系统发生中断并且将环境变量保存完毕时的栈结构一致。这样就可以用中断返回指令使就绪的任务运行起来。

具体的入栈方式要根据不同mcu而定。需要参考用户使用的mcu说明书。同时还要考虑mcu的栈生成方式。这需要根据具体问题来分析,在此不做过多论述。

⑵ 与应用相关的代码

这一部分是用户根据自己的应用系统来定制合适的内核服务功能。包括两个文件:OS_CFG.H,INCLUDES.H。

OS_CFG.H

配置内核,用户根据需要对内核进行定制,留下需要的部分,去掉不需要的部分,设置系统的基本情况。比如系统可提供的最大任务数量,是否定制邮箱服务,是否需要系统提供任务挂起功能,是否提供任务优先级动态改变功能等等。


INCLUDES.H

系统头文件,整个实时系统程序所需要的文件,包括了内核和用户的头文件。

4、用户应用系统编写的模式

用户应用系统是整个实时系统的最高层,用户通过利用实时操作系统提供的服务来开发自己的具体程序。

kernel提供给用户一些功能函数,使得用户的系统建立更加方便,但是kernel内部不会处理用户的工作,对于整个系统的具体应用工作还得需要用户自己去考虑,如何利用好这些功能服务函数就成为一个比较重要的问题。

⑴ main函数的结构

void main (void)

{

初始化系统的硬件;

OSInit();

任务的建立,消息机制的建立;

OSStart();

}

这里需要的是在OSStart()执行之前不得启动中断,硬件系统还不能工作,必须先让软件系统进入工作状态后才行。


⑵ 中断的结构

ISR:

{

保存处理器寄存器的值;

调用OSIntEnter();

执行用户的工作;

调用OSIntExit();

恢复处理器寄存器的值;

RTI;

}

用户的中断形式和以前一样,没有什么大的变化,仅仅是在原来用户ISR的基础上在固定的位置加了两个函数:OSIntEnter(), OSIntExit()。

⑶ 各个任务的结构

void YourTask (void)

{

for(;;)

{

用户代码

调用的系统服务

}

}

在任务启动函数执行完后,系统会切换到最高优先级的任务去执行,此时,可以将系统硬件部分的启动放在该任务的最前边,仅仅是启动时执行一次,主要是启动系统的节拍中断,或者一些必须在多任务系统调度后才能初始化的部分,使系统的真正开始工作,达到软件硬件的基本同步。

Void HighestPrioTask(void)

{

OSStartHardware();

For (;;)

{

用户代码

调用的系统服务

}

}

用户可以按照这些格式去编写自己的任务,建立自己的应用系统。

 

习题六

1、什么是实时操作系统?

2、实时多任务操作系统与分时多任务操作系统有什么区别?

3、实时操作系统应具有哪些基本功能?

4、实时操作系统中的任务(Task)有哪几种基本状态?

5、请列举几种针对51CPU的实时操作系统。

6、RTX51实时操作系统有哪两种不同的版本,它们之间有什么区别?

7、RTX51是怎样在多个任务之间切换的?

8、μC/OS是一种什么样的操作系统?

9、μC/OS-II有哪些基本特点?

10、μC/OS-II的内核主要包括哪些组成部分?

11、μC/OS 51的移植主要涉及到哪几个代码文件?

系统中断与时钟节拍

作 者:■ 武汉理工大学 王原丽 吕永江

引 言:

1 系统中断与时钟节拍

1.1 系统中断

  中断是一种硬件机制,用于通知CPU有个异步事件发生了。中断一旦被系统识别,CPU则保存部分(或全部)现场(context),即部分(或全部)寄存器的值,跳转到专门的子程序,称为中断服务子程序(ISR)。中断服务子程序做事件处理,处理完成后执行任务调度,程序回到就绪态优先级最高的任务开始运行(对于可剥夺型内核)。

  中断使得CPU可以在事件发生时才予以处理,而不必让微处理器连续不断地查询(polling)是否有事件发生。通过两条特殊指令:关中断(disable interrupt)和开中断(enable interrupt)可以让微处理器不响应或响应中断。在实时环境中,关中断的时间应尽量的短,关中断影响中断响应时间,关中断时间太长可能会引起中断丢失。中断服务的处理时间应该尽可能的短,中断服务所做的事情应该尽可能的少,应把大部分工作留给任务去做。

1.2 系统时钟节拍

  时钟节拍是特定的周期性中断(时钟中断),这个中断可以看作是系统心脏的脉动。操作系统通过时钟中断来确定时间间隔,实现时间的延时及确定任务超时。中断之间的时间间隔取决于不同的应用,一般在10~200 ms之间。时钟的节拍式中断使得内核可以将任务延时若干个整数时钟节拍,以及当任务等待事件发生时提供等待超时的依据。时钟节拍频率越快,系统的额外开销就越大。系统定义了32位无符号整数OSTime来记录系统启动后时钟滴答的数目。用户必须在多任务系统启动以后再开启时钟节拍器,也就是在调用OSStart()之后。μC/OSII中的时钟节拍服务是通过在中断服务子程序中调用OSTimeTick()实现的。时钟节拍中断服务子程序的示意代码如下:

void OSTickISR(void) {
  保存处理器寄存器的值;
  调用OSIntEnter ()或是将OSIntNesting加1;
  调用OSTimeTick ();
  调用OSIntExit ();
  恢复处理器寄存器的值;
  执行中断返回指令;
}

2 时钟管理系统

2.1 μC/OSII时钟管理系统

  μC/OSII原有的时钟管理系统类似于Linux,但是比Linux简单得多。它仅向用户提供一个周期性的信号OSTime,时钟频率可以设置在10~100 Hz,时钟硬件周期性地向CPU发出时钟中断,系统周期性响应时钟中断,每次时钟中断到来时,中断处理程序更新一个全局变量OSTime。μC/OSII时钟中断服务程序的核心是调用OSTimeTick ()函数。OSTimeTick ()函数用来判断延时任务是否延时结束从而将其置于就绪态。其程序伪代码如下:

void OSTimeTick(void) {
 OSTimeTickHook();// 调用用户定义的时钟节拍外连函数
 while { (除空闲任务外的所有任务)
  OS_ENTER_CRITICAL();//关中断
  对所有任务的延时时间递减;
  扫描时间到期的任务,并且唤醒该任务;
  OS_EXIT_CRITICAL();//开中断
  指针指向下一个任务;
  }
  OSTime++;//累计从开机以来的时间
}

  在μC/OSII的时钟节拍函数中,需要执行用户定义的时钟节拍外连函数OSTimeTickHook (),以及对任务链表进行扫描并且递减任务的延时。这样就造成了时钟节拍函数OSTimeTick ()有两点不
足:

  ① 在时钟中断中处理额外的任务OSTimeIickHook (),这样增加了中断处理的负担,影响了定时服务的准确性;

  ② 在关中断情况下扫描任务链表,任务越多所需要时间越长,而长时间关中断对中断响应有不利影响,是中断处理应当避免的。

2.2 改进的时钟管理系统

  针对上述OSTimeTick ()的不足之处,需加以改进来优化时钟节拍函数。在Linux中一般对中断的响应分为两部分:立即中断服务和底半中断处理(bottom half)。立即中断服务仅仅做重要的并且能快速完成的工作,而把不太重要的需要较长时间完成的工作放在底半处理部分来完成,这样就可以提高中断响应速度。

  μC/OSII不支持底半处理,为了减轻时钟中断处理程序的工作量来提高μC/OSII的时钟精确度,可以将一部分在每次时钟中断需处理的工作内容放在任务级来完成。这样就可以减少每次时钟中断处理的CPU消耗,从而提高中断响应速度和μC/OSII的时钟精确度。为此,定义任务OSTimeTask (),由它来处理原来在OSTimeTick()中需要处理的操作。因为μC/OSII采用基于优先级的抢占式调度策略,而每次时钟中断处理程序结束后需要首先调度该任务执行,因此让任务OSTimeTask()具有系统内最高优先级。由它执行用户定义的时钟节拍外连函数OSTimeTickHook (),以及对所有任务的延时时间进行递减,并把到期的任务链入到链表OSTCBRList中,OSTCBRList管理所有到期任务。OSTimeTask()函数伪代码如下:

void OSTimeTask() {
 OSTimeTickHook()//用户定义的时间处理函数
 while { (除空闲任务外的所有任务)
  对所有任务的延时时间进行递减;
  把所有要到期的任务链入到OSTCBRList链表中;
}
  任务状态改为睡眠,调用OSSched ()进行任务调度;
}

   在任务OSTimeTask()中,执行原来在时钟中断处理的用户函数OSTimeIickHook (),并实现将延时到期的任务链入到OSTCBRList链表中,这样在时钟中断程序中就只需要扫描任务到期的链表而不需要扫描整个链表,减少了关中断的时间。OSTCBRList为新建链表,它管理所有到期的任务。

  同时,需要减少OSTimeTick ()的执行工作量,只对OSTCBRList链表扫描,这样也减少了关中断时间。OSTimeTick ()伪代码如下:

void OSTimeTick(void) {
OSTime++;
OS_TCB* ptcb=OSTCBList;// OSTCBRList指向所有到期任务的链表
while(ptchb!=null){
  关中断;
  唤醒任务;
  开中断;
  指针指向下一个任务;
  }
}

3 小结

  本文以开源的嵌入式操作系统μC/OSII为例,分析了操作系统的中断机制和中断应满足的条件。介绍了μC/OSII系统时钟节拍,探讨了时钟中断函数中存在的不足,并且给出了解决方案,从而有效提高了中断响应速度和μC/OSII的时钟精确度。

                  参考文献

1 Labrosse Jean J. 嵌入式实时操作系统μC/OSII.邵贝贝译. 北京:北京航空航天大学出版社,2003
2 吴君钦.ARM嵌入式系统中断向量表的动态配置.单片机与嵌入式系统应用,2004(12)
3 刘岚,张凯.ARM7嵌入式系统的中断设计与中断处理优化.武汉理工大学学报,2004(4)
4 沈绪榜.2001嵌入式系统及单片机国际学术交流会论文集.北京:北京航空航天大学出版社,2001
5 Stanly B. Lippman, Josee Lajoie. C++ Primer. Third Edition. Addison Wesley
6 Liunx Kernel Archives. http://www.kernel.org

                            (收稿日期:2005-04-25)

Wednesday, November 14, 2007

《如何大談你沒讀過的書?》 教授教你放膽說




【聯合報╱李維國】 2007.11.11 02:56 am



《如何大談你沒讀過的書?》書影。
圖/聯合報提供
時尚派對中,你左近的名媛手拿勃艮地名酒,侃侃說道普魯斯特《追憶逝水年華》最長的句子用標準字體印刷有四公尺,可繞葡萄酒瓶底十七圈。你要沒法搭一句:普魯斯特失眠時最愛讀火車時刻表,只剩下張口結舌傻笑的份?

巴黎大學法國文學教授皮耶‧巴亞(Pierre Bayard)年初出書傳授祕訣,教讀者掩飾未讀過名著的尷尬窘態,意外爆紅全法國爭讀。熱浪席捲全歐撲向英美,英譯本《如何大談你沒讀過的書?》(How to Talk About Books You Haven't Read)《紐約時報》譽為「闖蕩藝文界求生指南」。

巴亞的家人很少讀書,他自己也對書冷感。偏偏教書為業,必須經常在課堂上、咖啡館賞析名著,他不見得本本都讀過。高談闊論沒讀過的書不會臉紅?「沒什麼好丟臉的,博覽群籍的書癡也不可能本本俱到。」

別以為文學教授有多專業,不是每個都讀過《追憶逝水年華》,「說文學教授只翻過普魯斯特,你覺得難以置信,但確實不少人如此。」更別提公認難以卒讀的喬伊斯名著《尤利西斯》。

他舉電影《今天暫時停止》(Groundhog Day)為例,質疑讀書的必要;格雷安‧葛林的《黑獄亡魂》消遣書中角色對聽眾暢談自己沒資格發言的喬伊斯。大衛‧洛吉(David Lodge)小說描述英國學者玩遊戲,說出沒讀過的名著,結果《哈姆雷特》奪冠。王爾德認為不一定要讀完書才能評書,「讀六分鐘最佳」。詩人梵樂希到處讚揚作家,根本沒讀過他們的書。

巴亞認為讀書人面臨三種壓力:要讀書、要把書讀完以及要讀完書才敢和人談論。壓力來自家庭或教育體系,不願承認沒讀過某些書,擔心被當成蠢材。其實「別人怎麼想不如對自己誠實重要,沒必要表現出一副有學養的樣子。」也是心理分析家的巴亞沒想到不讀書的人竟有強烈罪惡感,「這本書讓他們甩開罪惡感,免看心理醫生,省錢多多」。

巴亞把書分為四類:一無所知的、聽人討論過的、翻過的、讀過又忘記的。所幸不是每本都必須讀,只有少數經典和暢銷書,是文化養成和社交的必備,「沒有重要性的書就『不存在』」。他強調領悟書在書海中的位置比知道書中細節重要,就算沒讀過也可以有精采的討論,即使對方也沒讀過。

「學校摧毀對文學的愛,因為不讓學生瀏覽。」事實上,讀書方法許多:「瀏覽、讀頭不讀尾、看索引。」巴亞倡議瀏覽而非細讀,「閱讀應像在花園中漫步」。他認為好讀者有能力由第一行讀到最後一行,但有些書這樣讀,其他書就必須瀏覽。「說是瀏覽,不如說和書共同生活。譬如有時我會從書中找生命的答案,或隨意翻閱幾頁。」

「談沒讀過的書,首要膽子大。」要瞭解書的基本情狀,如封面、書評、讀者反應和作家的八卦,還要掌握談話時機與交談對象。否則,就設法改變話題。碰到作者,只要極力吹捧,免談細節。根據巴亞觀察,學生最知道胡說八道沒讀過的書,也最懂得將課堂聽來的枝節應用於社交。

「所有的意見即使再沒基礎,都有存在價值。」巴亞最大膽的建議是教人把書當開場,接下來大談自己,書的內容丟在一邊。這必須發揮想像力,也等於創作自己的書,「能靈活談論你不知道的事,比讀過世上全部的書還有價值」。

「我喜歡寫有趣的書,用幽默處理複雜議題。」巴亞稱寫這本書期望改造一般學生和歐洲大眾的線性閱讀方式,不要被堂皇大書嚇倒,克服文化恐懼症。「他們視文化為高牆,『知識』是嚇人的妖魔鬼怪。」他終極目的是希冀更多人讀更多書,有更大的自由。

『情境‧繪意』 陳國珍攝影展

在這金黃秋熟之際,壢新藝術生活館非常榮幸邀請榮獲2002年全省美展攝影獎得主陳國珍先生,展出『情境‧繪意』 陳國珍攝影展。
陳國珍1948年出生於苗栗,從年輕時一直到五十知天命的歲月,一直服務於工業界,在看似與藝術沒有交集的前半生中,因為內心對藝術的喜愛和家人的支持下,於2001年毅然辭去優渥的工作,投身於旅遊攝影的行列。『因為旅行,愛上攝影』,陳國珍這樣訴說他與攝影的最初緣份。從年輕到現在,從東方到西方,在不同的國度中旅行,遇見不同的人,走過千山萬水,每一次交集的火花,喜悅的、哀傷的都透過攝影來捕捉,以影像來訴說著不同時空下的故事。
「旅行是一輩子的一次相遇」,相遇瞬間的美麗,成為所有旅行攝影師的最深的依戀。此次展出共分成「情境山水」和「繪意映像」兩大部分,情境山水主要以中國山水為景,其中以黃山和黃龍最吸引人,黃山之壯美,千古傳頌,但在陳國珍的鏡頭下,黃山除了有著歷史山河的人文沉勁之外,還有一份穿越時間、因人才有的細膩之美,如在「自得」這件作品中,雄偉黃山隱沒在波瀾的雲海間,山巔上的小小剪影人物則細膩地勾勒出這人世的互動,直率又單純的情感以及天人交融的遼闊天地,構成一幅動人的小世界。
「繪意映像」這個系列則展現了完全不同於傳統的攝影性格,在原有的攝影作品上,透過影像的處理,讓畫面有了嶄新的風貌,藉此讓創作概念更為具體和清晰。畫面中滿溢的飽和色彩代表生命的豐厚,極簡的現代設計則突顯生命的純淨,種種都反映了其藝術的再創造性。另外,此次展覽中有作者精心構思的賞析小語,大家可透過這些片片小語來更加了解創作的意向。
能夠在人生的中站上,轉換跑道,走上攝影藝術這一條路,不知是多少人的夢想,陳國珍先生不但圓了這個夢,而且還以他個人獨特的角度,引領大家走進他的攝影世界,這個在美麗中帶著真誠,在燦爛中透著深沉的情境繪意世界,竭誠邀請你的蒞臨欣賞。
11月9日下午二時舉辦開幕茶會,誠摯的邀請您,暫時拋開忙亂的俗務,一同感受藝術帶給您的平靜與喜樂。



展覽期間:2007年11年09日~2007年12月24日
採訪時間:2007年11月09日 PM 1:00於本館
開放時間:週一至週五10:00~12:00 15:30~17:30 19:00~21:00
週六10:00~12:00 週日及國定假日休館
展出地點:壢新醫院門診大樓3樓 壢新藝術生活館
主辦單位:壢新醫院・聯新文教基金會
協辦單位:智邦藝術基金會
團體預約專線:(03)494-1234轉2150

『情境‧繪意』 陳國珍攝影展

在這金黃秋熟之際,壢新藝術生活館非常榮幸邀請榮獲2002年全省美展攝影獎得主陳國珍先生,展出『情境‧繪意』 陳國珍攝影展。

陳國珍1948年出生於苗栗,從年輕時一直到五十知天命的歲月,一直服務於工業界,在看似與藝術沒有交集的前半生中,因為內心對藝術的喜愛和家人的支持下,於2001年毅然辭去優渥的工作,投身於旅遊攝影的行列。『因為旅行,愛上攝影』,陳國珍這樣訴說他與攝影的最初緣份。從年輕到現在,從東方到西方,在不同的國度中旅行,遇見不同的人,走過千山萬水,每一次交集的火花,喜悅的、哀傷的都透過攝影來捕捉,以影像來訴說著不同時空下的故事。

11月9日下午二時舉辦開幕茶會,誠摯的邀請您,暫時拋開忙亂的俗務,一同感受藝術帶給您的平靜與喜樂。

展覽期間:2007年11年09日~2007年12月24日
開幕茶會:2007年11月09日 PM2:00於本館
開放時間:週一至週五10:00~12:00 15:30~17:30 19:00~21:00
週六10:00~12:00 週日及國定假日休館
展出地點:壢新醫院門診大樓3樓 壢新藝術生活館
主辦單位:壢新醫院・聯新文教基金會
協辦單位:智邦藝術基金會
團體預約專線:(03)494-1234轉2150

工程師心聲:不當工程師要做什麼?

我是個工程師,喜歡冒險和解決各種創造性問題的挑戰,喜歡尋找使事情能順利運作的方法、並說服他人讓我這麼做。如果我不是工程師,我想我會是一個建築師,又可能是個律師、精神科醫師、企業家、小說家或演員。

身為一個工程師,我發現其他大多數職業只能帶給我對於工程的熱愛的部分;事實上,我發現其他大多數職業在多樣性和挑戰性上有太多侷限。

建築吸引我的地方在於其融合了靈感和實用性。有一次我負責一項房屋自動化的遙控器設計案;該裝置可控制所有屋內系統,其控制介面能讓來訪的賓客易於使用。此外該遙控器必須讓使用者握起來舒適、看起來美觀,甚至能搭配屋內的漂亮瓷器並引起賓客們的讚嘆。

律師這個職業則提供了進行競爭性說服(competitive persuasion)的機會。事實和證據是很有力量的,但如何去研究、理解、展現這些事實和證據,對事情結果關係重大。

工程領域也充滿了競爭性的念頭,需要權衡和折衷去得到最終的解決方式;這些元素必須進行研究、策略性地考慮問題和議定結果。從各種點子到產生最終產品的過程,全賴工程師理解需求和說服說服他們自己和其他人、並得到最佳結果的能力。

精神科醫師幫助人們明白從生活中得到的,大部分是得自人們所付出的;他們需要應付緊張、精神異常的病人以及各種突發事件,需要尋求各種平衡。精神科醫師協助人們妥善處理生活中的事件,幫助人們應付各種變化和找到優勢,克服缺點。

而工程師則需要和物理定律打交道:你不能從系統輸出中得到多於你對系統所輸入的;他們需要根據現實平衡目標需求,比如時間和成果之間,工程師不斷地面對各種變化,特徵變異,性能失效、資源、國際設計成果和技術,以及更多的不確定性。新的設計必須在克服功耗方面的缺陷,尺寸和噪音靈敏性的問題的同時,加強功率的輸出、擴大範圍、提高吞吐量,更友善的使用者介面和更有效。

企業家面對的則是推展務創造性策略、資源配置和行動的總規劃,他們是為其投資想法與潛能帶來回報的福音傳播者;他們把想法付諸行動,執行商業計畫。

我也當過企業家、創辦過公司,募集了幾百萬美元、創造了產品並投入市場,銷售給終端顧客,同時進行了其他的投資。所有的工程師都需要策略規劃的必要手段--即完成計畫的行動過程。工程需要投入預算、工具、人才、資源和計畫表。各種點子必須出售給需要投資並能使之成為現實的人。

小說家是傳達的藝術家,他們透過文字取得讀者內心的理解。書籍具體化了人們的想法,所以人們可以在故事中展望和追隨這些想法;作者給人物性格下了定義並且描述了人物之間如何互動,書籍創造了可複製的標準,它可以在多種語言中翻譯並傳播。

我曾撰寫過上千頁的架構說明、產品特性需求、設計審查、作業原理、研究結果、專案計畫和技術規格。為了團隊的成員可以更有效地進行合作,當這些文件涉及專案說明、元件規格、介面規格、性能需求和預定作用的時候,它們必須保持相同的進度。能寫出自己能理解的文章還不夠好,必須寫出讓其他人不會誤解的文章。

演員是表演各種看起來簡單但是很複雜的動作的藝術家;一場成功且娛樂性強的表演需要準備、時機、協調、正確的元素,以及大量的技巧和一點運氣。

工程就像一首好歌和一支舞,它在得到最終結果的時候需要各種因素的協調,和經歷實踐過程中的失敗;最終產品的品質與公司的集體智慧直接關聯。工程也需要好的頻率控制、降低雜訊和各種應急的計畫,需要在事情不能如預期進行時緊急應變。

我記得年輕時拆解老唱機和小電晶體收音機,以製造一個立體聲系統的事情;我記得我在我老爸對四路開關頭痛的時候,試圖在屋子裏進行佈線的事情;我記得我的第一個電子套件,並將所有零件組裝起來的事情;還記得把我的第一個萬用表插進120V的插座卻爆出火花,原因是我輸入電壓錯誤。

我想我注定當一個工程師…或者說,選擇成為一名工程師使我獲得了更多。而當我已經成為工程師並且獲得了許多樂趣,我還可能選擇其他任何的職業嗎?

(參考原文: State of the Engineer: What would I be, if not an engineer?)

在南村落做南法菜

韓良憶  (20071113)




 從前,我總以為巴黎就是法國,直到我去了南法。

 巴黎,當然是一個無與倫比、令人敬仰又無法忽視的存在,可是南法卻改變了我對法國的觀感。如果只去過巴黎,你或會覺得,法國人優雅、注重美感,根本上有點抑鬱甚或神經質,同時因為自視高,多少令人難以親近;到了南法,才發覺法國文化中也有比較快活、可親的一面。


 我喜歡閱讀,對不同的文化充滿好奇,我往往藉由閱讀來設法理解不同的文化,卻發覺知性的理解到底不是感性的體會,理解再深刻,也像是隔了一層保鮮膜,形、樣、色皆熟悉,就是觸不到真正的質地,於是我動身前往斯地,改用我的五感官能來直接感知文化,短時間內實在到不了的話,套用前兩年一本台灣書的書名,那就「吃」吧。飲食畢竟是文化的一部分,是文化的載體,我在享受一道又一道的佳餚時,吃下的不只是美味,還有文化。
 鄉村菜色尤其能凸顯一個地方的風土民情和歷史文化,南法菜即是如此。一般常認為,法國菜著重醬汁的調製,作法繁複,菜色華美豐潤,然而那是早期宮廷菜和後來都會布爾喬亞菜的特色,包括南法菜在內的鄉村菜,卻比較講究「原汁原味」,作法相對簡單,家常菜更是樸實,根據時令就近取材,菜色容或不比布爾喬亞菜精緻,卻比較吃不膩,而且說不定更對華人的胃口。

 以南法鄉村菜為例,南法的平原和矮丘盛產蔬菜、水果,日常餐桌上遂出現大量的蔬果;綿延的山坡則種植著葡萄和橄欖,於是人們簡直把葡萄酒當水喝,烹調時用起橄欖油更毫不吝惜。至於沿海地區,菜餚自然以地中海的漁獲為主,於是有了馳名於世的尼斯沙拉、馬賽魚羹等;山區無海魚可食,卻盛產乳酪和肉,於是有了紅酒炆牛肉、燉羊腿和甜椒、節瓜鑲肉這樣的菜色。我們吃著這些菜,不必上地理課,就對當地風土有了基本的了解。

 有些菜色則提醒了我們南法地區自古以來的歷史,這裡曾是古羅馬帝國的屬地,羅馬人在這裡不只留下圓形競技場、水道橋,也留下了鵝肝、青醬等美味,尤其是普羅旺斯的青醬,它的法文名為pistou,不但與義大利青醬pesto音似,兩者根本出自出同源,都是羅馬帝國的遺風。

 前不久,我應「林語堂故居」和「南村落」的邀約,為「林語堂的美味飲宴」活動的第一場晚宴燒菜,烹調的菜色就以南法鄉村菜為主。為什麼是南法,又為什麼要燒鄉土菜色?原因說來簡單,只因為林先生的女兒林太乙在《林語堂傳》中提到,林先生生前喜愛南法的風光,他在人生走到低潮時,曾數度旅居南法。在那兒,他喜歡傍晚坐在露天咖啡室喝一杯濃咖啡,或在海邊看滿載而歸的漁船,更常常在早上和妻子手拉手,一起上街買菜,夫婦倆在南法的海光山色與田園風光中,重新嚐到簡樸生活的甘美滋味,重獲生命的力量。

 我猜想,燒得一手好菜的林夫人廖翠鳳在客居南法時,除了在廚房裡做她擅長的廈門菜外,應該也試著燒過南法的美味。她烹調的應該不會是那些只有餐館大廚才整治得出來的「大菜」,而是些樸素有味的家常菜,很可能就是鄉村菜,好比青醬、燉雞或燉牛肉以及基本作法和廈門菜差不多的炒煮鮮貝之類的簡單菜色。

 於是,在十月底的一個週日夜晚,我在離新店溪的南村落,端出橄欖醬、青醬、尼斯沙拉、白酒海瓜子和番茄橄欖燉雞等南法家常菜,我揮舞著鍋鏟,嗅聞著悠長的橄欖油香和直接而毫無矯飾的蒜香,好像更明白南法簡樸的美味與恬淡的生活,為何吸引了包括林先生在內的無數旅人,一再重返斯土。

《酸甜人生》 美90老翁出版小說處女作

【聯合報╱吳凌遠】 2007.11.11 02:56 am



九十歲老作家上報,多半是訃聞向讀者與書市告別。偏偏美國近日有一位九十歲人瑞級作家米拉德‧柯福曼(Millard Kaufman)頻頻在媒體亮相,替他的小說處女作《酸甜人生》(Bowl of Cherries)全力造勢。

柯福曼原是資深編劇,美國東岸知名大學畢業,二戰後到加州發展,以電影編劇為業,與米高梅長時合作。1950年代柯福曼兩度獲奧斯卡金像獎最佳編劇提名,參與知名卡通《脫線先生》(Mr. Magoo)幕後製作,也擔任過導演和執行製片。

八十六歲仍振筆創作,沒想到一個編劇約臨時告吹,柯福曼驚覺「這把年紀在電影圈混不下去」,決定改行寫小說。他雄心勃勃,想起聽小說名家毛姆演講時說:「寫小說有三大祕訣,可惜沒人知道是哪三項。」老驥伏櫪,這位寫作老手發豪語:「既然如此,我怎會失敗?」

耗時四年(幸好他心臟夠勇身體夠強)完工的《酸甜人生》像好萊塢電影,開場就是高潮:暗黑陰冷的監獄裡,十五歲的主角賈德等著上刑場,回望短短在世歲月遇見的奇人異事,發現他「五味雜陳的人生和打噴嚏一樣倉促、難預測、無足輕重」。

賈德聰明機靈,失意的教授父親拋妻棄子不知去向,他又與母親分開。為了尋母,小賈德獨闖科羅拉多州養馬場。天才過人的賈德更進了耶魯大學,跳級讀文學博士,論文寫英國早夭浪漫詩人查特頓(Chatterton)。查特頓的後人是埃及古物學者,賈德造訪時邂逅他的女兒薇樂莉,深受吸引。

賈德為愛輟學,投入他們瘋狂的計畫:重新架構人類社會,分派給他的任務是設計自殺,「想出有生產力的做法」。故事調性忽而由科技轉向動作冒險,他和女友薇樂莉到紐約發展,隨一群貪婪的美國人前往伊拉克阿薩瑪省,夢想一夜致富。當地土霸首領看上薇樂莉,讓賈德被捕下獄。往事一幕幕重現眼前,賈德悟出「我們身上發生的事沒有難以置信的,任何事都可能發生」。

《酸甜人生》故事場景橫跨美國東西岸和伊拉克,連小布希總統都出面軋一腳。引人入勝的奇想情節媒合俚俗趣味與歷史、政治、哲學,青春期的衝動迷惑,配上機智對話、幽默筆調,立時成為話題熱點。柯福曼不愧是影劇圈老手,書一問世就獲得三家電影公司青睞,電視談話節目也競相邀約。

談起一鳴驚人的小說,柯福曼老實說:「真正寫在紙上,才知道要寫什麼。」嫻熟戲劇布局,他指出主角如果是成人,就少了許多戲味,所以安排賈德只有十來歲。虛構的阿薩瑪省寫來栩栩如真,令讀者驚嘆,以為他在伊拉克待了許久。他覺得好笑,「說出來不好意思,其實全靠大英百科全書。」

替他出書的麥克史威尼公司(McSweeney's)由創作怪才戴夫‧艾格斯(Dave Eggers)成立。健康欠佳成天在家寫劇本的柯福曼從沒聽過,幸好他1999年出版編劇指南《情節與角色》(Plots and Characters)的編輯向他推薦。他告訴作家兒子出版公司要出他的小說,「我兒子差點昏倒」。

老作家對寫作飽富信心,他篤信「一個字接著一個字,時間夠久,就會有幾頁作品出來」。和時間賽跑,柯福曼埋首趕寫第二本小說,他不肯透露內容,「因為我還不確定會是什麼樣」。

胡蘿蔔燉飯

陳念萱  (20071114)




 如果吃過新疆農村的手抓羊肉飯,一定會被那微黃噴香的燜飯,刺激得遮遮掩掩口水直嚥,怕讓主人誤以為來客真的飢腸轆轆。而嚐過義大利燉飯和西班牙海鮮飯的人,也會永遠記著那色彩繽紛的海鮮與香草,不斷撩人的震驚與讚嘆,尤其是洋人堆砌食材的方式,總是入眼就讓人垂涎,彷彿沒吃到就一定會跳樓的。

 聽說手抓羊肉飯,是用胡蘿蔔、洋蔥與辛香料炒米過後,再把羊腿置放在炒米上一起蒸熟,原理跟義大利燉飯接近,但我只吃過卻沒試過,僅只是想像,就已經相當美味了。這裡面最複雜的其實是西班牙海鮮飯,不但食材多到很難找齊全,製作過程也頗繁複,就只是端到桌上的風光,便一目了然,連我這種好奇鬼,都不敢輕易嘗試。如果要用想像的自己動手做,最容易入門的就剩下義大利燉飯啦!


 由於被迫教學認識香料,卻偶而遇到盛裝赴宴者,心裡總不是滋味地暗罵:你是來做菜的還是準備包餐的?讓自己心理平衡的方式,就是把最痛苦的炒米工作交給穿西裝打領帶來上課的人。義大利燉飯,沒啥大學問,就是做苦工,必須把生米硬是炒成熟飯,期間不斷地加入自己喜歡的辛香料,待米入味慢慢加水燜熟,就是義大利燉飯了。口味變化多端,各憑喜好,是最好的廚房入門測試版。
 那回,被我懲罰炒米的學生,竟然成功地完成了我新發明的胡蘿蔔香料燉飯,其實,我自己從未動手做過,只在當下胡亂想像動口指揮罷了。

嗆聲事件的重點

【聯合報╱楊照】 2007.11.14 02:51 am


應該考慮游泳去大陸的,怎麼會是我們?應該是抱持恐怖權力心態,如狼似犬的總統、副總統吧!

《伊索寓言》裡有這麼一個故事,說村子裡一位眼盲的老先生,經常比明眼人還明白,任何東西任何事到他面前,雖然他看不見,摸摸、聽聽,他就能洞察真相。

有一天,村子裡的好事者,一定要考驗老先生不可思議的能力。他們找了好多難以辨識的東西給老先生,老先生一一都答對了。最後剩下一隻小狼犬,好事者竊笑地問:「這,是一隻狼,還是一隻狗?」

瞎眼老先生摸了又摸,想了又想,不能確定到底是狼還是狗,最後搖搖頭認輸了,好事者發出戲謔的笑聲,不過,瞎眼老先生隨即補了一句:「不管牠是什麼動物,我都不會讓這傢伙靠近我的羊群的!」

這個寓言的教訓是什麼?別小看瞎眼人,少了視覺幫忙,他們對現實的掌握,不見得會比五感齊全的正常人差;更進一步,有時候正因為少了視覺,一個瞎眼人可以察覺、「看到」我們看不到的重點。

老先生搞不清楚是狼是狗,很糗嗎?但他卻搞清楚了更重要的事──這動物不會對羊群友善的。把握了不讓牠靠近羊群的重點,那牠究竟是狼是犬,相對就沒什麼了不起的關係了。這種智慧,真值得我們學習。並不是所有的事都需要費時間花力氣去弄得明明白白,懂得如何節省精神精力對付重點,才是道理。

例如說,台北音響大展上對陳水扁總統嗆聲的「查理」,究竟是何許人?那個在高雄新興市場對呂秀蓮副總統嗆聲的「阿珠」又是何許人?最近幾天,媒體花了好多時間好多篇幅,東問西問東訪西訪,炒作「查理」、「阿珠」的背景,探討他們嗆聲的來龍去脈,還要讓他們跟兩位「大人物」你說我回,我說你回,來來往往,好像真有個是非議題可以從這樣追究中浮現似的。

不能說這種做法無聊或沒意義,然而其意義恐怕比較接近問瞎子老先生小狼犬是狼是犬吧!換作去問老先生:「『查理』是不是有錢買票看音響?『阿珠』是不是媒體安排的?」聰明老瞎子可能會回答:「我實在不曉得『查理』、『阿珠』何許人……但,我卻明白他們當然有對『大人物』嗆聲的權利,而且他們嗆聲的內容沒有胡扯啊!」

還有可能,聰明的老瞎子會回答:「我不曉得『查理』、『阿珠』何許人也,然而我卻知道,換作是我,我不會讓甚至沒有肚量面對『查理』、『阿珠』小小抱怨發洩的總統、副總統,靠近我,靠近我的親戚朋友,靠近我要賴以生活下去的社會。應該考慮游泳去大陸的,怎麼會是我們?應該是抱持恐怖權力心態,如狼似犬的總統、副總統吧!」

Tuesday, November 13, 2007

那個年代

【聯合報╱張典婉/文】

父親張漢文的歲月

父親在屏東林邊教書,因為不滿日本政權,一日脫下衣物假裝投海自盡,而後輾轉到了日本與中國,開始他的人生,只是那個時代的中國,已是千瘡百孔……

曾赴大陸求學,

考取中華民國外交官

第一屆特考

常想,自己是什麼樣的機緣,成為父親、母親的女兒。是要在生命中留下記憶呢?還是在這個世紀,為他們寫下未完的夢?我覺得上一代的人生,比我們精采壯烈!有時候要提筆都覺得好沉重,沒能來得及留住他們的時代,他們就消逝了。

父親出生於清朝末年,民前十年(1901),在苗栗頭份斗煥坪的農村,排行老二,出生時適逢日本殖民時代,農村也被迫學日語、改日本名,祖父卻仍偷偷為張家家族設私塾、教漢學,並為父親取名漢文,希望子孫不要忘記自己的語言與文字。

父親從小有著濃郁家國觀念,懷抱著對祖國的熱情,後來成為他到大陸念書的動機,考上中華民國外交官第一屆特考,並投入康有為門下,成為萬木草堂中唯一的台灣學生。

整理父親舊作時,父親在我小學時寫給我的詩與散文,再次浮現,在《因句集》中,父親詩說:「志難故不耽推敲,豈謂雕虫怕世嘲。藝游養性從所好,舍藏尤不亂將拋。」「清靜家居未閉關,祇因才薄事刨山。生平唯恨莊周誕,言是行非虛渺間。」看見他的題字:「書留日後紀念並解父意可也。」只怪自己時年太小,父親來不及分享我的寫作生涯,而他留給我的許多期許,竟是在近中年後才體會,也是我深深的遺憾。

他最常讓我背李白、杜甫、王維的作品,背詩的過程是:他先說詩作內容,再用客語、國語各念一回,念完後父親讓我背誦完,吃飯前就得考試,我得背完才得上飯桌,就這樣我不知不覺背完了許多詩詞,為日後靠文字工作,奠下了基礎。

家鄉斗煥坪,是中港溪開拓英雄黃祈英與原住民交換民生物資的台地,也是中港溪流域最早的交易重鎮,父親很喜歡分享黃祈英的傳說,農閒有空,他帶著我坐客運車到南庄、三灣、獅頭山、北埔……一路沿著小鎮,告訴我客家人屯墾的過程、地名的傳說,水流東、番婆石、內灣村、吊神牌……一站站走過,讓我早早就認識了家園。這些旅行經驗,也化為我日後從事報導文學、客家研究與紀錄片的最佳藍本。

童年記憶中,父親是沉默的,多半時間都是沉靜地在桌前沉思,短短的新樂園香菸,往往燒到了最後一刻,這才放下,父親很少說話,母親看他在沉思,就會把我帶開,讓我在花園裡澆花,或是到竹林裡採竹筍,給晚上的飯桌加道湯;有時候,大家都安靜無語,我也深怕大吵大鬧,會讓一切崩塌,事實上在童年記憶中,我一直深怕自己的吵鬧,會讓年邁的父母枯竭,一如冬日乾枯的葡萄藤,斷裂、殘破,所以年幼的我,在同學眼裡一直是很文靜沉寂的小孩,直到現在,我還是習慣躲在人群後,享受孤獨的滋味。

記憶中的夜晚,只有我們三個人與黑暗共存,窗外偶爾有幾聲貓頭鷹的低吟。

不滿日本政權,

前往中國投入康有為門下

據說父親在屏東林邊教書,因為不滿日本政權,一日脫下衣物假裝投海自盡,而後輾轉到了日本與中國,開始他的人生,只是那個時代的中國,已是千瘡百孔,在貪腐與戰亂中遊走,在歷史上演出了無數的悲劇,父親縱有一生理想與報國大志,卻是生錯了年代,走錯了時空。

例如他經恩師康有為介紹,去投靠吳佩楫漁伬唌A希望有機會到德國求學,哪知吳佩孚眼中的康有為,志不同、道不合,後來在野史書上,讀到康有為在吳佩孚五十大壽時相見一幕,康有為暗示:可否一同起戈復辟。這位直系大將卻難耐不同思維的人,畢竟是前朝人物,吳佩孚無法體會,還認為康有為有求於他,父親即使有康有為推薦,也不得吳佩孚賞識,不同的年代,不同時空,只能各自留下空白!

父親出生後,一路從殖民社會脫逃,懷抱熱情、理想,一心想為祖國做些什麼,但是他的熱情,卻目睹了衰敗的中國,即使在中壯年曾經周遊日本、新加坡等外交單位,擔任外交工作,八年抗戰卻又讓他隨著國民政府遷徙,四處流竄。戰後回到台灣,目睹二二八,父親選擇回故鄉終老一生,與他的老師一樣,空留無盡悲傷與失落。

長大後,母親才告訴我父親回到台灣,因為不是國民黨黨員,無法再回公務體系,而且他不願同流合汙,不齒政府的腐敗,像父親這樣有著自由思想,在大陸有如此豐富的閱歷,在二二八當口又曾救出不少菁英(詳見吳濁流著《台灣連翹》),之後他就在白色恐怖名單之列,失去所有公職機會,也不容於當權政府。無法在公務體系報效國家,連帶著時任台灣省糧食局副局長的四弟張子斌,一起告老返鄉,之後他隱居山林,過著平淡生活,其實是無奈的選擇,也是生命中嚴重的挫敗。

印象中,老是有一些穿黑西裝的男人到家中小坐,我也不知道他們是誰,直到台灣政治氛圍稍微開放,母親才告訴我,那些黑衣人是來查家中的收音機,是不是偷聽大陸匪區廣播,有沒有當發報機使用,或是來看看父親、母親又與誰通信了,看看家裡是不是有大陸版的讀物,翻翻家中的書報,告訴我們「保密防諜,人人有責」。記得他們來了,父親就在果園裡忙農事,不願進門,只有母親依然給他們倒茶水,一面踩著縫衣機為我做制服,直到天黑。

不過父親每隔一些時日,會帶母親與我全家北上,拜訪他的師母,即康有為第二任妻子康梁隨覺。這位廣東老太太,纏足梳包頭,是康家唯一帶著子孫在台灣的後人,父親對她極為恭敬,晚年多由孫子康保延照顧她。

父親跟隨康有為的晚年,康歷經變法失敗,民國成立,軍閥四起,父親與垂垂老矣的恩師,遊歷上海、杭州、青島,最後在青島為康有為送終,「康有為是被人毒死的!」父親曾告訴母親。後來父親也把早年他與康有為生活的經過,寫在〈游存盧哀思行狀三紀〉一文中。

與吳濁流、林海音、

沈櫻等文友往來

童年記憶中,有些朋友來找父親,一坐就是大半天,談著國事、政治、文學……來訪客人中,印象最深的是吳濁流先生,他是父親台北師範的同學,也有豐富的大陸經驗。他每回來都是拉開嗓門,用客語、日語及濃濃的客家國語與父親交談,那時他已經創辦了《台灣文藝》,父親偶爾也在雜誌上寫些四言絕句等漢詩。吳濁流先生常會送些他的新書,如《亞細亞的孤兒》、《瘡疤集》……也讓我早早觸動了台灣文學的根苗。早年父親文章多應邀發表在《醒獅雜誌》、《台灣文藝》等刊物,內容多圍繞在時事、《論語》、孔孟的學說,及心愛的古詩。

沈櫻女士與林海音女士,應該是父母親最熟悉的文友了,她們不僅在文學路上支持著父親與母親,也間接啟蒙了我對文學的興趣,我也在她們身上感受到文人的溫厚和煦。

林海音女士,是父親恩師林煥文先生的長女。林煥文先生年輕時曾在新埔與頭份公學校教書,在高壓日本統治下,是少數還偷偷用客語教漢詩的老師,在新埔公學校任教時教過吳濁流,在頭份公學校,父親深受林煥文先生的思想啟迪,因此離開被殖民的故鄉。

沈櫻女士早年在大成中學任教國文老師,在1950年代與父親、母親結為好友。在她任教北一女退休後,將斗煥坪當成第二故鄉,在我們家鄰地蓋了所小屋,成為早年文人中,最早有別墅的女作家,常常在假日帶著女作家、文友們到頭份來小住度假。也經由她們的往來,我有機會認識琦君、羅蘭、張秀亞、艾雯、劉枋、林海音等五十年代的前輩作家,至今我還是覺得自己很幸運,可以在她們身邊看見文人丰采。

從鎮上的中學完成學業,到台北念書,父親已經年紀大了,記憶與體力越來越差,全靠母親照料,果園無法耕種,果樹日漸荒廢,農業經營越來越困難,勞動力外流,台灣進入工業社會,頭份鎮上有了第一個工業區,同學們畢業都去工廠當作業員,家裡來幫忙做田事的長工,多半到工廠去輪班了,父親的手足相繼離開人世,在我念世新四年級暑假,一個酷暑午後,父親嚥下最後一口氣,安靜地到了天堂。同年十二月美麗島事件爆發。

攔轎喊冤

【聯合報╱馮翊綱】 2007.11.13 03:12 am


秦香蓮帶著一雙兒女來到京城,攔轎喊冤,能以一介草民,扳倒當朝駙馬、狀元陳世美,乃是因為遇到了以下幾個關鍵人物:

王元龍是客棧老闆,看香蓮母子可憐,提供一個免費的住處。現代的富貴人,配合高官需求,進獻禮品、提供報帳發票的不見少,免費布施窮人的不夠多,王元龍屬於少數。

狀元府的門官,用現代的官銜看待,就是官邸門口警衛隊的低階軍官。他問明了來由,大發惻隱,私放香蓮闖入官邸,使得苦命母子見到了丈夫、父親。現代警衛,在官邸門口自殺的倒有一個,私放陳情者的聞所未聞。

香蓮一如預期,被逐出官邸,於是攔轎喊冤,攔下宰相王延齡的官轎。王延齡最初表示自己身為宰相,不該僭越受理民詞,但聽了香蓮之苦,決定管了這樁閒事,安排在狀元壽宴中,香蓮唱上一曲悽愴的曲子,試著挽回丈夫最後的良知。陳狀元不領情,把宰相一塊兒轟了出去。現代宰相,不知菜價,且與當朝結為死黨,座車耗油與排氣量之大,絕非孱弱女子所能攔下。

陳狀元天良喪盡,派殺手韓琪了斷妻子,以絕後患。韓琪居然放下屠刀,細聽原由,進而全盤相信了秦香蓮,良心發現,自刎而死。現代殺手,以為自己的行動是為國鋤奸,進而淪落成法律人球,客死異鄉,親痛仇快,令人唏噓;韓琪這種可以用情感打動的殺手,是太不專業的少數。

那個額頭上長了一彎月牙的黑臉法官,這次居然展現出難得一見的人性。包拯最初接受了和解條件,代轉由後宮提供三百兩銀子的和解金,勸香蓮放手,回家度日,「教子南學把書念,千萬讀書莫做官」;卻熬不住香蓮下堂隨口說出的一句「官官相護有牽連」,「叫一聲殺了人的天」!說包青天正直,倒不如相信他當時羞愧難堪;包黑子摘下自己的烏紗帽,將陳狀元開鍘……

這五個人物不敢說在當世絕對不存在,但五個如此特別的人要齊聚一樁公案,太強求、也太傳奇。

當世這番傳奇,有人已攔轎喊冤,但聽到我們的「陳狀元」回敬喊冤的百姓,居然說是「我不會派人去暗殺他」……那麼我這個說書人,便要強力祝禱,秦香蓮的五個貴人:「善良的老闆」、「惻隱的警衛」、「老成的宰相」、「良知的殺手」以及「鐵面的法官」,請出列,該是站出來的時候了!

Monday, November 12, 2007

唐代的公主和駙馬

「鍘美案」看宋代駙馬

京劇裡有一齣戲叫做《秦香蓮》,戲的內容是說在宋朝時,湖廣荊州地區有一位窮書生,名叫陳世美,他娶妻名叫做秦香蓮,他們生有兩個兒子,年紀都很幼小,陳世美本人也上有父母。有一年,陳世美要上京趕考,秦香蓮帶著兩個兒子送陳世美上路,一路上,夫妻二人恩愛非常。陳世美到了京城開封,也考中了狀元。皇帝看到陳世美長得很英俊,就想招他為駙馬,皇帝問陳世美:你有沒有結過婚?陳世美一聽說皇帝想招他為駙馬,心裡就起了貪念,便向皇帝謊報自己沒有結過婚,於是皇帝把公主下嫁給陳世美,陳世美當然也成了駙馬。自從陳世美進京以後,就從未曾跟他的妻子通過任何音訊,所以秦香蓮在荊州,根本不知道陳世美生活得如何,也不知道陳世美在京城已高中狀元並做了駙馬的事。

秦香蓮在荊州過了兩、三年,她的公公、婆婆相繼去世,秦香蓮將公婆的喪事料理完後,便帶了兩個兒子到京城尋夫。秦香蓮母子三人,一路上就像乞丐一樣,沿途乞討生活盤纏,好不容易才到了京城,卻打聽到陳世美已經做了駙馬,於是秦香蓮帶著兩個小孩闖進陳世美的宅府裡,見到陳世美後,陳世美卻堅決不認秦香蓮與兩位小孩,反而叫人將他們趕了出去。不但如此,陳世美更深覺秦香蓮是個禍害,她的存在,會危及到自己的前途,於是陳世美找了家中的家將韓琪命其追殺秦香蓮。終於,到了一個深夜,在一個破廟裡,韓琪找到了秦香蓮母子三人,於是準備拔刀殺她們滅口,此時秦香蓮母子三人只能跪在地上苦苦哀求。韓琪此時心中也覺奇怪,心想:陳駙馬為何要殺這位弱女子跟兩位幼童呢?於是韓琪就問秦香蓮:「汝等何許人耶?」秦香蓮只好一面梨花帶淚,一面委婉將實情全盤托出。

韓琪聽到秦香蓮的說詞後,大為震驚,覺得陳駙馬實在是泯滅人性,禽獸不如,竟然為了自己私自前途,要殺妻殺子,於是韓琪心中為之一軟,實在不忍心下手。但是在宋代當時「愚忠」觀念下,韓琪也覺得未能完成駙馬爺交付的任務,就是不忠之人,也就再沒有顏面回去見駙馬。所以韓琪頓感左右為難,殺秦香蓮也不對,不殺也不對,最後,竟然拔刀自刎而死。秦香蓮看到韓琪為她們母子而死,不禁大為悲傷,也深覺陳世美實在是沒人性,於是心中頓生勇氣,決定返回開封府,向當時的開封府尹(也就是當時開封市市長)包拯告狀。

包拯,就是我們俗稱的包青天,他詳查了實情,知道陳世美所有的惡行作為後,便把陳世美騙到開封府衙門來審問,當然陳世美是堅決否認,可是包拯認為證據俱在,判定是陳世美謊報皇帝自己未婚才娶得公主,這在當時是欺君大罪也就是死罪;又,陳世美企圖殺妻殺子,這是不仁,不仁,在當時同樣也是死罪,所以包拯就在開封府的大廳中把陳世美當場處死。這便是京劇中「包公案」系列諸齣劇目之一,又稱為「鍘美案」,劇名便是取自包公不畏當朝權勢,「鍘」了陳世美人頭之意。

不喜歡和皇室結親家的唐朝人

陳世美的案子發生在宋代,他為了想得到駙馬這位置,冒了生命危險,也果真付出生命代價。但這種事情,在唐代是不可能發生的,因為唐朝人對於擔任駙馬這件事情,不但不羨慕,大多數還非常排斥。舉一個唐代跟陳世美境遇相似可是結果卻相反的例子。

唐宣宗大中十一年(公元857年),宣宗皇帝要宰相在當年的新科進士中,選拔一位當駙馬,於是便有人向宣宗推薦當年新科進士王徽。但是,王徽聽到了這消息後,便趕快跑到宰相劉瑑面前,哭泣哀求說:「我王徽今年已經年過四十,年老體衰,又體弱多病,實在不適合匹配公主,懇請相爺在皇上面前替我解說,千萬別招我為駙馬。」這件事情跟宋代的陳世美似乎剛好相反,陳世美是拼了命想當駙馬,而唐代的王徽是聽說自己會當上駙馬後,便趕快去向宰相哭泣,祈求宰相幫忙推託,就是不想當駙馬,可見唐代當時的進士大多數是不想當駙馬的。此外,在唐憲宗皇帝時,有一年,曾要求公卿大臣家中子弟來娶公主,結果公卿大臣紛紛托辭躲避,可見當時很多公卿大臣都很害怕娶公主,也害怕與皇室結為親家。

不僅新科進士不歡喜娶公主,早在唐玄宗時,玄宗想把她妹妹玉真公主許配給方士張果(也就是民間傳說中「八仙」之一張果老),張果便跟他兩位朋友王迥質和蕭華說:「娶婦得公主,平地生公府,可畏也。」這句話意思是說:娶個公主過門,等於平白生出個官府來管你,這是多可怕的事啊?張果在說這話時,剛好皇帝派的使者也來到,說要將玉真公主許配給張果,張果便大笑不肯接受。方士,其實在唐代時社會地位並不高,但連一個區區方士都不肯娶公主,可見當時人對公主的印象實在不好。

另外一件事是在更早些前的唐高宗時,當時高宗皇帝想把女兒太平公主許配給薛紹,但薛紹的哥哥薛顗感覺到太平公主平時就氣勢強盛,是位被寵壞的驕蠻公主,非常擔心弟弟的婚事。所以薛顗便問他的族祖薛克構的想法,然而薛克構也同樣憂心地說:「俗話說:『娶婦得公主,無事生官府』,實在令人感到可怕。」由張果跟薛克構的話都大同小異來看,這恰好是反映了當時社會上一般人的看法,認為娶公主是一件既可畏又可怕之事,才可能產生類似諺語不斷流傳。既然社會上一般人都認為娶公主不是件好事而不願跟公主結婚,所以唐朝的世家大族就更不願跟皇室結為親家。是故,在唐憲宗之前,沒有世家大族的子弟做過駙馬。所以早在唐太宗時就曾經講過:「我貴為天子,可是一般社會人士都寧願跟門閥世族聯姻,卻不願意跟我們皇室結為親家,我不知道為什麼會這樣?」唐太宗這點疑惑,一直延續了百餘年之久,直到唐憲宗以後,才出現極為少數的士大夫、世家大族願意與皇室聯姻之例子,但也是極其少數。

為保官位而聯姻

士大夫跟世家大族不願意尚公主,便造成唐代公主婚姻的困難。於是,公主可以下嫁的對象,便多半是集中在功勳大臣的家族,尤其是那些不是出身於世襲門第或世家大族的功勳大臣們,這些人因為出身沒有世家大族那麼名貴,也因為他們的地位,都是來自於朝廷給的官名,所以為了保持自己的高官爵位,功勳大臣們也就不得不接納公主為妻或媳婦。一旦某個這類家族接納了第一位公主以後,這個家族往往就會陸續接納第二個、第三個公主嫁給自己家族的其他成員,這就是我們中國人講的「親上加親」之觀念。所以我們看有幾個家族跟李唐皇室的婚姻關係是非常密切,以下我們舉數例:

一、楊貴妃家族:楊貴妃本人嫁給唐玄宗,而她堂哥楊錡也娶了太華公主,她另一位堂哥楊國忠,也接納萬春公主與延和郡主為自己的兒媳婦;楊貴妃又有一位堂兄弟楊鑑,也娶了承榮郡主。

二、中唐時幫助朝廷平定安史之亂的郭子儀家族亦復如此:郭子儀兒子郭曖娶了齊國昭懿公主(即昇平公主),郭曖的女兒也就是郭子儀的孫女又嫁回李唐皇室,即是唐憲宗的正妻郭皇后;又,郭曖的另兩位兒子郭鏦與郭銛,也分別娶了漢陽公主跟西河公主。

三、武則天皇后家族:武則天是唐高宗的正妻皇后,而武后的姪兒武攸暨也娶了她女兒太平公主;又,武攸暨的親弟弟武攸止的女兒又嫁回皇室,也就是唐玄宗前半生最心愛的武惠妃;又武后的另外三位姪兒武三思、武承嗣、武承業,也分別也納了安樂公主、永泰公主、新都公主為媳婦。

四、此外像盛唐時干預朝政的韋皇后家族也是一例:韋皇后是唐中宗的正妻,她妹妹也嫁給皇室的嗣虢王李邕,韋皇后堂兄弟韋濯,也娶了安定公主,韋濯的孫女又嫁回皇室,也就是唐德宗的韋賢妃;又,韋皇后的堂姪兒韋捷,則娶了成安公主,以上是韋家的例子。

像這類與李唐皇室數代聯姻的家族例子甚多,以上僅舉四個家族為例,這類家族的特點之一就是幾乎都不是出身很有社會名望的世襲門第家族,所以要藉官位保持權力來源,就必須不斷與皇室聯姻,來增加家族的政治地位。

唐朝公主為何不受歡迎?

然而,何以唐人都畏懼娶公主為妻?這是有幾個原因造成的:

一、唐朝的公主多半品德不佳:不知道是否是李唐皇室家教不良,多數公主表現出的品德往往不好。如唐宣宗想把永福公主嫁給于琮,後來宣宗發現永福公主品行不佳,於是婚事作罷,也就是宣宗自己把婚約收回了。公主出嫁後的敗德之事甚多,譬如高祖的女兒永嘉公主嫁給了竇奉節,卻跟有婦之夫楊豫之淫亂私通。唐太宗女兒合浦公主嫁給了房遺愛,房遺愛就是太宗親信重臣房玄齡之子,雙方家族都是當時有頭有臉的人物,但合浦公主竟偷偷和一位和尚僧辯機私通。唐中宗女兒安樂公主嫁給了前述武三思之子武崇訓,卻又跟武崇訓的堂兄弟武延秀淫亂,她還曾當著上官婉兒面前脫去武延秀的下裳高談闊論,荒唐行徑極其誇張。

除了公主結婚後與外人私通外,有些公主又有性情暴戾不法的,例如唐德宗的女兒義陽公主嫁給王士平,義陽公主平常就驕縱不可一世,駙馬王士平忍無可忍只好抵抗,當然就會發生爭吵,結果事情鬧到皇宮裡,被當時的皇帝唐憲宗知情了,憲宗大怒之下,先把公主關在宮中禁足,但也同樣命令王士平囚禁家中,不准出王家大門一步以示儆戒,可見駙馬真是難為。早在武則天時跟她女兒太平公主時,她們母女倆的態度更是囂張。話說武則天想把太平公主嫁給薛紹,卻嫌棄薛紹的嫂嫂蕭氏,還有薛紹的弟妹成氏兩人,都不是門第貴族出身,於是武則天竟開出條件,要薛家將他們這兩對夫妻先行離婚後才肯下嫁,武則天還說:「我女兒是什麼身分地位,怎麼可以跟那些鄉巴佬的女兒當妯娌啊!」還好當時有人在旁邊勸武則天說:「蕭家好歹也是開國功臣蕭瑀的後代,出身也不算壞」,武則天才打消先要人家離婚的念頭。所以薛紹娶太平公主,反而差點使自己哥哥與弟弟的婚姻破裂,可見娶公主還可能對家族造成極大威脅。

二、家庭禮儀的問題:公主是皇帝的女兒,身分尊貴,所以下嫁以後,常常不肯用當時家庭禮儀來跟公公婆婆行家禮,反而要公公婆婆跟她行君臣之禮,也就是要公公婆婆來拜見媳婦,這實在違反家庭倫理的禮儀。中國是一個非常重視禮節的民族,一個媳婦嫁到家裡來,卻完全違背家庭的禮儀,公公婆婆不敢把公主看成是媳婦晚輩,反而是把嫁進來的公主當成自己家老祖母一樣的孝敬,這在公公婆婆心中,其實又何嘗是滋味?唐太宗的女兒南平公主嫁給宰相王珪的兒子王敬直,王珪便說:「當今皇上(指唐太宗)是位有道明君,皇上向來遵守法度,所以我也要讓我家公主媳婦遵守家庭禮儀,我想皇上不但不會罪怪我,反而會認為我這樣做,是為了成全國家禮儀啊!」於是王珪要公主按媳婦禮儀拜見自己,當然也接受了公主的拜見。但這在當時,不過是件例外,因為在王珪以前,公主媳婦向來是不拜公公婆婆的,所以太宗聽到這件事情後也非常高興,下令說:以後公主都要拜見公公婆婆。

然而事實上,公主下嫁後,對公公婆婆仍舊不肯拜見。唐高宗顯慶二年(公元657年)時,曾下詔書說:公主下嫁後仍需要拜見公公婆婆。可見太宗以後到高宗此時為止,做人家媳婦的公主,仍照樣不拜見公公婆婆,所以高宗才必須下詔再特意強調一次。但這次詔書也未必發生功能,因為到唐德宗建中元年(公元780年)時又再下一次命令說:「按舊例都是公公婆婆拜見公主,而公主媳婦也不回禮,所以朕希望禮官們能制訂相關的拜見禮儀。」此後,還是有好幾位皇帝下過類似詔書。一直到唐宣宗大中三年(公元849年)萬壽公主出嫁,宣宗還特別告誡萬壽公主要守做媳婦的禮儀去拜見公公婆婆,可見當時公主媳婦不拜見公公婆婆實在是很普遍的現象。公主自恃是皇帝的女兒,身分尊貴,所以對公公婆婆常視之為「臣」,而自視為「君」的家族成員,這種心態常會讓公公婆婆跟駙馬造成一種委屈感。

前面已經說過,郭子儀的兒子郭曖娶了昇平公主,有一年,郭子儀生日,他的兒子、女兒、媳婦全都去拜壽,但唯獨昇平公主不肯去。郭曖見狀就很生氣,督促昇平公主快點動身,昇平公主就生氣說:「我是君,他是臣,哪有君跟臣拜壽的道理?」就硬是不去。郭曖就更生氣地說:「連皇太子都跟我父親去拜壽了,妳身為媳婦,為什麼不去?」然而昇平公主仍舊是不肯去,於是郭曖盛怒下,就打了昇平公主一巴掌。昇平公主平時驕蠻慣了,受不了這種氣,就哭哭啼啼地跑去跟皇帝父親告狀,此時郭子儀聽了這消息,嚇了一大跳,顧不得滿是前來拜壽的賀客臨門,就綁著郭曖,送進宮中求見唐代宗,要向唐代宗道歉。代宗笑著安慰郭子儀說:「我們做長輩的不裝聾作啞,怎麼互相當親家呢?」還安慰郭家父子,沒有對郭家父子追究。以郭子儀的例子看來,又有哪個家族願意娶公主為媳婦呢?這種媳婦若進了門,那真成了家中的老祖宗老佛爺了,所以誰家願意娶公主為媳婦呢?

三、公主下嫁後設有公主府,駙馬不過是府內的附庸:一般而言在唐代,普通的公主封邑是千戶,有時恩寵高的公主還可封到一千四百戶。所謂封邑的戶數,實際上就是中央政府將這些戶數的賦稅,都送給公主花費,所以公主等於是有薪俸的,她拿的是政府豐厚的津貼。此外,公主設有公主府,公主府裡面有邑臣,也就是她有下屬官吏歸她指揮。根據《唐六典》記載:「公主府有令一人,從七品下;丞一人,從八品下;錄事一人,從九品下。公主邑司官,各掌主家財貨出入、田園徵封之事,其制度皆隸宗正焉。」此外,皇帝在公主出嫁時還會賜給奴僕,這些奴僕人數數量不受限制,所以公主下嫁有很多陪嫁的僕人與財貨。此外,公主下嫁,皇帝必定會為公主蓋一間新宅第,這些新宅第有些是覓地新建,也些則根本就將駙馬原先舊家拆了重建,所以公主下嫁,嫁妝非常豐渥。也就是說,公主下嫁是帶了大量的財產與官吏、官署、僕人一起進門,所以做駙馬的人住的房子就住在公主府裡,公主府的一切財富,官吏、奴僕,都是屬於公主,由公主直接指揮,所以駙馬在公主府中的地位類似附庸般,完全沒有主權。

又,如果公主死亡,駙馬尚要為公主守三年喪,而唐代畢竟還是個以男性為中心的社會,駙馬在家中似乎缺少男性的尊嚴。所以做駙馬的人心中內心感受,多半不愉快,在這情形下,哪個男人願意很熱衷地做駙馬呢?

四、當了駙馬反而不容易升官:唐代,一個男人娶了公主以後,便有了「駙馬都尉」的官銜,於是簡稱為駙馬。一般來說,駙馬與公主結婚後,都會立刻加上一個「三品員外官」的官銜。三品,在唐代是很高的職階,唐代的宰相,一般都是三品官,因為按唐代慣例,除非真的如郭子儀般立過天大功勞,否則一、二品的高官官階,是不會輕易頒賜給官員的,所以可說三品官已是唐代的最高官階。但是「三品員外官」則不然,唐代所謂「員外官」,是指原缺以外的官,也就是正式編制以外的官,只是個編制外的虛銜,不是個正式的官。唐玄宗以後,員外官又改稱為「檢校官」,凡是任何一個官位,哪怕是上至宰相的大官,只要是加上「檢校」兩字銜頭,就根本只是一個虛銜空位而已,即令是檢校宰相,一樣沒實權、沒薪俸,也不能去任何單位上班。所以駙馬雖然有官銜,但也只是個虛的官銜而已,根本不能算正式官吏。

當然,如果皇帝對某位駙馬有恩賞或有意提拔的話,他仍可以當正式的占缺官員。但是翻一翻唐代眾多駙馬歷史來看,唐代一共有210位公主,結過婚的130位,這中間只嫁一次的有100人,有再婚也就是「二嫁」的有27人,至於「三嫁」的有3人,所以總共駙馬人數該有163人。但是,這163人最後能做到位極人臣的宰相,大概只有2人;其他能做到部長級的九卿級官員,不會超過10人;剩下的駙馬,則多半是沒有正式的一官半職。所以做為一位唐代的駙馬,在政治仕途上其實並不是很順利。皇帝跟駙馬的關係是岳父跟女婿,但是這種翁婿關係跟民間是不同的,在民間,這種翁婿關係是很密切的親戚關係,但是在皇宮中,皇帝跟駙馬的翁婿關係卻是非常淡薄的,因為這種關係,與其說是親戚關係,不如說是另一種的政治關係,所以皇帝不會特別照顧女婿當官以免平添政治是非。再加上這些肯來尚公主的駙馬,幾乎都是學識才能不高的人,所以他們的政治仕途通常不是很好。於是對於當時有才幹有能力的人來說,駙馬,並不是個能在政治上能飛黃騰達的升官捷徑,因此他們對於當駙馬也是興趣缺缺。

以上四個因素,都會使唐代人對駙馬身分毫無興趣,敬而遠之。

離婚怨偶

新加坡社會發展、青年及體育部的最新統計顯示,新加坡有愈來愈多夫婦以離婚收場。新加坡去年離婚或廢除婚約的怨偶數目達到七千零六十一對,這比二零零五年的六千九百零九對,要增加許多。二十年前,新加坡離婚怨偶數目只有二千六百零八對。

此外,根據統計,新加坡維持婚姻少於五年的夫婦,從一九八七年的百分之二點六,增加到二零零二年的百分之三點八。

統計也發現,新加坡結婚超過十五年,卻最後仍以離婚收場的夫婦比例,在一九九二年為百分之十二,也比一九八七年的百分之十,要增加兩個百分點。

不過,社會發展、青年及體育部表示,新加坡每一千名居民中,有兩人離婚;新加坡的離婚率,比香港的每千人有三人離婚、日本的每千人有四人離婚,和英國每千人有八人離婚,仍然要低。

對於離婚率有擴大趨勢,新加坡社會發展、青年及體育部政務部長符喜泉指出,當局對這個趨勢非常關注,正探討及制定政策,鼓勵新加坡人上婚前預備班,學習和伴侶溝通的技巧與學習維持婚姻,還有接受婚姻輔導等。

符喜泉說,政府也將與大學和專家合作,設立委員會,探討新加坡離婚率的趨勢,並找出解決辦法。

女性身材好 子女也聰明

【歐洲日報╱本報倫敦十一日電】

據BBC中文網報導,豐滿有曲線的女性受男性青睞,而且更長壽,這是廣為人知的事實。現在的研究還表明,身材如啞鈴(兩頭大,中間小,豐乳肥臀)的女性更聰明,她們的生育的子女也更聰明。

這項研究有美國匹茲堡大學和加利福尼亞大學的研究人員進行,研究本周將在《進化和人類行為》期刊上發表。研究人員研究了一萬六千名婦女,收集了她們身體測量的數據,認知測驗的得分。

研究發現,腰圍和臀圍差異大的婦女在測驗中得分明顯高於腰圍和臀圍差異不大的婦女。另外她們的子女也在測驗中分數比較高。

腰圍和臀圍的差異同肥瘦沒有關系,是指婦女的腰圍小於臀圍,最理想的比例應該在○點六和○點七之間。

豐滿有曲線的女性的子女更聰明。研究人員認為,女性豐滿的臀部和大腿蓄積更多脂肪,裏面含有Omega-3不飽和脂肪酸可能更多,這種不飽和脂肪酸對於懷孕期間大腦發育很重要。

腰部脂肪有高度Omega-6脂肪酸,但這種脂肪酸不適合大腦發育。腰部脂肪還可能導致糖尿病和心臟病。而消瘦的婦女缺乏上述兩種脂肪酸。

美國匹茲堡大學和加利福尼亞大學的研究人員認為這也從另一角度解釋了為什麼男人喜歡身體富有曲線的女性。研究為男人喜歡有曲線美的女性提供了生理依據。

青少年

上海,酷暑。德國駐上海的領事館這一年特別熱鬧。為了迎接到上海實習的德國大學生,每週開放交誼廳給學生。
文化評論家龍應台二十一歲的兒子安德烈、十八歲的菲力普也在裡面。龍應台幾乎無法相信,法國、美國和全球來的青少年大量到亞洲找實習機會。不少人的下一站是拉丁美洲,之後還要到印度。

西元前三五六年出生的亞歷山大、西元一一六二年出生的成吉思汗可能很難想像,今天十幾歲、二十幾歲的青少年,已大批大批跨疆界探險,絲亳不輸給當年他們二十多歲時縱橫東西的豪情。

只是,他們出發的目的不同,帝王和大汗為征服為國家興亡而冒險犯難。而這一代出走,有人為好奇、有人為競爭、有人自我探索、也有人是為了認識朋友與世界。

全球化把競爭平台加大

生長背景決定這世代青少年多元又有爆發力的面貌。

十五歲到二十四歲是聯合國定義的青少年,全球約十一億人,其中台灣有三三0萬。

這個年輕世代在八0年代後期,蘇聯、東德、波蘭「蘇東波」解體的浪潮下、在沒有政治的禁忌中長大,迎接他們的是一個個被拆除的國界與全球化世界。

他們也告別了以文字、書本為主導的世界。「在PDA上、在電腦上、在轉寄電子郵件時,都在閱讀,」大塊出版社董事長郝明義形容,網路的出現帶來不同的想像,青少年「連夢想空間都變大了」。

他們的價值也很立體與多元。

世界不再只有自由/共產、西方/東方、正義/邪惡二元對立。

但全球化的衝擊也讓他們像大海裡游的熱帶魚,選擇過剩、競爭過多,不知人生往哪裡游去。

便宜的科技 競爭加速

我們必須教導現在的學生,畢業後投入目前還不存在的工作,使用根本還沒發明的科技,解決我們從未想像過的問題。

這是YouTube上近來在台灣校園間廣為流傳的影片「你知道嗎」(Did you know),全球有百萬人次瀏覽。顯示學生對未來世界的變化感到恐慌。


這種恐慌是一種事實,從各國政府、企業家到教育工作者,都發現,這代青少年比前幾個世代浸淫在無情的競爭裡。

為累積人力資本,歐、美、亞洲的高等教育,急速擴充,中國每年釋出五百萬畢業生,印度更超過這數字。

台灣半導體之父張忠謀說這代青年面對「競爭的白熱化」:學生畢業出來,會是跟全世界差不多年紀的人才競爭。很快的,一個不小心的話,整個產業、整個企業,都會被世界的另一國、另一地搶過。

Me世代 既自信又懷疑

但這世代也是史上最勇於懷抱夢想、自我的一代。美國聖地牙哥州立大學教授珍.特吉(Jean M. Twenge)形容他們是「Me世代」(Me Generation)。

因為少子化,他們被教導成超高自尊、自我中心,珍.特吉觀察到,網路長大的Me世代,本質很反權威、重視自我表露,把網路世界發生的美好、可能性都搬到現實。

這一代的典型無疑是:
  學歷最高、知識最多元的一代
競爭最激烈的一代
  價值最多元而混亂的一代
  最受寵愛又孤獨的一代
  自信又帶著疑惑的一代

這些原因,讓過去五年,先進國家紛紛把焦點放在青少年。聯合國看到佔全球人口十八%的青少年,失業率攀升。失業人數達失業總數四0%。青年人比過去任何世代都更容易走向邊緣化。

歐盟則是預測二0二0年,十五到二四歲族群的人口將自十二.五%降為十一%,同時間,六五到九0歲人口比例則從十六%擴大到二一%。這代青少年長大後,無疑得面對一個「養不起的未來」。

各國看到外在環境對這代青年無情的衝擊,也起而行動。

去年芬蘭訂下青少年法案(Youth Act);中國上海市政府更在政府的十一五規劃裡,納入青少年發展。它們的目標都是促進青年的成長與獨立、協助他們成為積極的公民、避免被邊緣化。

新加坡則是喊出全人青少年政策,希望能訓練出對世界準備好的青少年(World Ready Youth),期待他們具有積極實踐的能力。其中一項,是要這一代永不說「死亡」,能在失望中堅持下去。


對外,挑戰無限;對內,青少年階段原本就是人一生中的關鍵。

十三到十五歲是一個人開始學習抽象思考的階段,接著,邁入青少年時,他們必須回答:我是誰、我的價值、我與世界的關係,才能順利過度為獨立的成人。

面對外在世界的波濤與內在自我的建立的雙重壓力,這一代回應世界的方式也變了。

時代雜誌(Time)兩年前就嗅到社會瀰漫的「大小孩」現象,以「長大,沒那麼快!」(Grow Up, Not So Fast)為題,預測這代青少年不知該如何向成年靠岸的現象。

未來 考驗台灣青少年

「年輕人的發展會決定國家未來的面貌,發展好的國家,就能面對全球化,」負責台灣青少年政策的青輔會主委鄭麗君語氣鏗鏘。

青少年的發展決定每個國家未來十到十五年的國力。台灣站在哪個位置,有什麼特殊的挑戰?

台灣的青少年肩負著把台灣人均所得從一萬五千提升至三萬美元的責任,也肩負台灣的經濟轉型。

中山大學經濟系副教授劉孟奇是經濟史專家。他分析,如果以未來台灣經濟成長率走勢二%到四%間(過去四百年間的平均成長率),需要花近二十年到三十年才能成長到三萬美元。

「二十年後,現在的青少年正是社會中堅,他們要月收入八萬到十萬新台幣才是平均水準,想想,那需要具備什麼樣的能力?」劉孟奇把眼光拉到二十年後提醒著。

那能力自然與戰後嬰兒潮世代的「勞力密集」、解嚴世代的資本與技術密集產業需要的「一技之長」不同。

不論是歐洲的波隆納宣言或澳洲、加拿大、新加坡等大英國協系統的國家都有共同體認,未來的人才要有「核心就業能力」:

是讓畢業生能夠在經濟上獨立,有自信,並能在生涯上成功發展。對許多人而言,這是追求完整自我實現的重要一環。

芬蘭教育體系是目前回應全球化潮流下做得最好的,兩千年至今,芬蘭學生在OECD等國際評量計劃裡,表現每年都在前三名。


包括日本、瑞典、英國、歐陸各國教育官員,都飛去芬蘭取經。

芬蘭的教改目標很明確:「培養國際工作的創造性技能」,而作法是穩紮穩打的從師資、有效的教學方法開始。芬蘭的國中小教師,至少要能說兩種外國語言、包班教師有碩士學位(具備專題研究的能力);師生透過專題討論,鼓勵學生閱讀、寫作、表達論點。

但台灣的教育體系在回應全球化能力的需求上,顯得緩慢。

今年底,將有兩份重要的全球教育調查出爐。

十一月底月公佈的PIRLS(國際閱讀理解素養調查,針對國小四年級)、十二月底的PISA(國際學生評量計畫,針對十五足歲中學生),台灣都是第一次參加。

雖然成績尚未公佈,但不少教授透露,台灣學生在數理成績表現好,但閱讀理解與素養的國際評比不理想,落在中後段。

幾位大學教授表示,台灣學生在各種國際數理比賽的表現優益,但因過度競爭與長期被分數衡量,而興趣與信心皆低;而閱讀素養的不足,背後隱涵台灣學生的表達、思考、問題解決能力並不理想。

教育仍然在許多青少年身上刮滿傷痕。台師大數學系教授林福來感嘆:「孩子對學習沒有熱情,無法終身學習,而那是適應未來最重要的能力。」

未與時俱進的家庭與社會文化,也是當今台灣青少年前進的反挫勢力。長不大的青少年
  培養不出獨立見解與人格

在德國,十五的青少年被稱為「先生」;在紐西蘭,十二歲的孩子就可以駕駛農場上的牽引機;美國十八歲投票;法國政府給大學生租屋貸款,鼓勵自立。

在北歐、紐西蘭等國家,培養視野卻是從兒童與青少年基本的生活獨立開始,逐漸發展到對世界的認知和理解。目的,就是在培養出能夠獨立探索知識、能發展自己見解的完整人格。

不同的社會對年齡有著不一樣的規訓,西方少年可以被當做成人,東方少年被視為小孩。

電影演員桂綸鎂的故事,也可說是七年級生,在中產階級家庭保護下,企圖活出自我的縮影。


桂綸鎂從小學各種才藝,是父母的小公主。但大三那年,她到法國遊學一年,文化衝擊讓她看到兩國青年的差異。二十四歲的她反省:「我們一直按社會期待走,好像從學習到生活都不為自己。」

家長以保護與管束,束縛年輕人,似乎他們只要把書唸好就行了。

景美女中校長林麗華今年迎接新生入學後,曾接到住宿生家長電話說:「女兒沒帶碗到學校,怎麼辦?」她很擔心家長的過度涉入,不利於於培養孩子解決問題能力。

長不大的孩子對個人、對國家的影響是什麼?

心理學博士、台大工管系教授陸洛認為,這種教養方式無法讓下一代用自己的眼睛看世界、欠缺獨立生活、獨立思考的能力。「一有問題就回到父母溫暖的懷抱(coming back to parents)。」

而對國家的影響?龍應台認為,如果個人是「晚成熟」,是「稚嫩」的,那麼國家的總競爭力也就是「稚嫩」的。

對存在感到焦慮

這一代青少年被豢養出的稚嫩,更讓他們感到失去存在感。

一位中正高中學生這麼說出他所接收的教育與文化:

所有的想法從表達起,就被壓抑,完全沒有發揮的空間。肚子餓了?會冷嗎?都由大人決定,與自己的真正想法無關。專長興趣甚至娛樂的方式,都是他們說了算……獨立?自主?連想法都不敢有。

在第一線教書十六年的政大教授陳文玲就感受這代學生的憂鬱。

「那種憂鬱在長輩看來太奇怪了,衣食無虞、資源豐富,幹嘛無病呻吟?」但她發現,這一代從小到大都被父母與社會決定,缺乏探索、心中有太多能量無法釋放。

政大中文系剛畢業,目前人氣最旺的獨立樂團「蘇打綠」主唱吳青峰,自承大二那年憂鬱半年,整天躺在床上。他一直思索,「在這生命裡,有什麼事讓我真心感到快樂,充滿熱情、完全不會倦怠?」

缺乏探索經驗、獨立的精神,這一代苦悶尋找生命的意義。

也許有人會問,獨立思考、獨立學習、獨立生活有這麼重要嗎?


台大工管系教授陸洛用「雙文化」的概念,分析獨立自主在未來世界的重要。陸洛解釋,華人講究關係、依賴取向,強調順應;而西方走個體、獨立取向,強調行動;兩個文化極度互斥。

「未來是雙文化社會,如果在成年前太保護,進入社會就有適應不良的問題,」陸洛分析。

無疑,東方的雙文化走得慢。

日本尼特族與飛特族是最明顯的例子。日本野村總合證券資深研究員日戶浩之表示,許多青少年中小學拼升學,大學後才自我探索,探索期拉長至畢業後,茫茫終日找不到熱情,工作一個換一個。

這些「工作貧民/窮忙族」(working poor),已變成企業燙手山芋。學生變得負面思考,連日本官員還上電視呼籲「不要自殺!」

台灣青少年的人生狀態是否正向積極?

中山大學教授劉孟奇這兩年積極研究與訪談七年級生,也出版《我七年級,我不草莓》。他認為,多數七年級生很熱血、敢追夢,只是求學階段探索不足,比較不容易找不到人生方向。

幾個數據顯示,青少年欠缺未來感的趨勢。

台灣二十歲到二十四歲勞參率約五三%,與美國的七四%、加拿大的七九%、新加坡及香港的七二%相距極遠,與鄰國南韓五七%接近。台灣人較晚加入就業市場。

目前,每六.六位大學生裡,就有一位延畢,是兩千年的一.三六倍(兩千年是九位有一位)。

劉孟奇分析延畢理由,有學生為讀雙學位、考研究所、到國外當志工,但的確也有兩成左右的學生逃避現實,不知興趣何在。

由於這一代對工作的想像不只是糊口,而是理想的實踐。當工作變成糊口的工具,就不免成了企業主眼中的「掃瞄族」(scanner),對工作沒承諾,只想跳槽。

青輔會最新的「大專畢業生就業力調查報告」顯示,畢業生就業第五年是向上或往下發展的關鍵年。學歷與經歷接近的兩人,薪水在五年後差距卻有兩萬元以上,差異就在熱情與持久力。

哪些人的職涯發展能具備持久力(staying power)?


「能把興趣、求學、職涯三位一體的人,決定職涯發展是否順遂,」劉孟奇分析,當企業與現實對工作的要求更高,只有對工作熱情的人能適應改變、持續學習。

探索自己的精神價值

台灣過去的世代,經歷經濟快速起飛、強調競爭與超越,總到人生後半段才意識自我的追求。

這世代又如何看自己?

明道綜合高中高二學生張珈瑋今年參加美國國務院全球網頁競爭活動獲得白金獎。對自己的世代有很多觀察,她說很多人以嬌嫩的草莓形容年輕人不耐壓,但她認為草莓的柔軟最適合融入各種料理充當調味。

「我們的柔軟就是最大的抗壓本錢,而我們的香味也會為社會帶來清新的氣息,」她自信說。

面對無公式可套的競爭,各系統除了要快速回應全球化,更要讓這世代建立屬於自己的精神價值。

開啟與新世代對話,欣賞彼此的差異、聆聽彼此,是避免青少年徬徨是第一步。

《十個與孩子的重要對話》的作者是擔任牛津大學十一年拉比(猶太教傳教士)的施慕禮,他在教養自己的八位子女,及擔任親子諮詢專家的過程中,強烈鼓勵世代對話:

到底我希望成為什麼樣的人?我希望成為好人或壞人?我希望自己自私或無私?我希望為世界帶來光明或黑暗?我要隨波逐流的人生,還是挖掘人生的奧秘與深義?

除了為他們找生存的內在動能,社會也要幫他們找到舞台、看到模範並結交志同道合的朋友。

中原大學服務學習辦公室主任吳肇銘是鼓勵大學生學習服務的推手。他這些年教學最大的獲益是發現大學生有驚人的本事──源源不絕的創意。

「青少年有一股氣在亂竄,就像武俠小說裡只要打通任督二脈,就可變高手,」吳肇銘打通筋脈的關鍵是幫孩子搭梯造橋,幫他們尋找資源,讓學生成功、累積信心。

他近期的計劃是讓學生透過web2.0協助遙遠的非洲馬拉威醫師、老師改善當地環境。

在天下採訪的眾多故事都讓人相信,當社會停止說他們小,他們會比你想像的,有改變自己與社會的能量。


青春不只是一段時光,更是人生的一種狀態。這社會應鼓勵這世代有自己的獨立宣言:

我們要敢於懷抱夢想、勇於冒險我們要為自己負責,享有自由同時盡義務。
我們要找到適合自己發揮的位置、成就自己、貢獻社會。
我們要能在失望中堅持下去。

詩人紀伯倫曾如此描述父母與子女的關係:父母像一把弓,孩子為箭,但父母不是強迫把箭射向何方,而是儘可能把弓拉滿,給一個好的環境。

唯有青少年活出積極人生,教他們不要害怕偉大、國家才能世代交替,未來才有希望。

CATCH PLAY

「Timothy,有人闖進我們公司了,」十月二十四日,一通關鍵電話打進威望國際董事長陳主望的手機,總經理張心望急切的報告,昨天還在談合作的得利影視,這一刻已經向檢調檢舉威望,大舉搜索公司。

「這是一個商業版偷襲珍珠港事件,」張心望說。全球第二大光碟大廠暨台灣影視龍頭──中環集團董事長翁明顯,十月二十四日突然向檢調聲請搜索威盛電子董事長王雪紅轉投資的威望國際公司,原因是:「涉嫌侵害其好萊塢八大影業對其在台授權」,與翁明顯對上的是王雪紅的外甥,三十三歲的陳主望。

他,談合作變被告 自認合法,王雪紅等長輩也力挺

翁明顯旗下的得利影視,指控威盛轉投資的威望國際,侵害他們辛苦取得的專有出租權,為了爭奪DVD出租權,雙方引發一場線上智慧財產權大戰。

被控的陳主望,來頭不小,外公是台塑創辦人王永慶,威盛董事長王雪紅則是他的阿姨。他是王雪紅大姊王貴雲之子,從小就受到王雪紅及夫婿陳文琦的栽培,三十二歲就被賦予重任,擔任手機大廠多普達的董事長。

這個被王家重點培植的第三代,十月三十一日坐在敦化北路底,可以俯視信義計畫區的高級辦公室裡,接受《商業周刊》獨家專訪時,對訴訟案原本只淡淡的說,「合作的對象這樣對我們,感覺很hurt(受傷),」但隨即話鋒一百八十度大逆轉,他臉上馬上露出迎接挑戰的笑容,他說,「越是攻不下來的市場,越讓我興奮!」

今年初開始,陳主望一直希望爭取翁明顯支持新推出的線上租片商業模式,他也直接拜訪翁明顯,「他要我們直接跟底下的人談,」陳主望說,「一路談氣氛都很好,一點都看不出問題。」沒想到二十四日翁明顯一出招,竟是突襲搜索,為了預防對手再次突襲,這幾天威望國際門口多了一群穿黑西裝的保鑣。 陳主望不是不知道他挑戰的是國內影視租片大亨,他可能正踩在灰色的法律線上,但他知道,如果一戰成名,他可以一口吃下一年營業額新台幣三十億到四十億元的影視租片市場,而且立即握有內容產業的影響力,可為威盛的手機等隨身裝置平台加分。

當年威盛王雪紅對上全球晶片大廠英特爾(Intel),大打智慧財產權專利戰爭,一度被視為小蝦米對抗大鯨魚;如今,陳主望對上翁明顯,顯然也不輸阿姨當年的氣勢。當被問到王雪紅對訴訟侵權的反應,沒想到陳主望說,「長輩們確定我們做的事合法後,對被告反而感覺很exciting(興奮),」他興奮的說,「這表示這真的是個值得做的市場。」

爭議點何在? 踩在法律灰色地帶,取得出租權

翁明顯和陳主望之間的這場戰爭,起因在於過去著作權法沒有考慮到網路時代來臨帶來的影響。原本著作權法僅規定除了代理商授權之外,如果能合法取得正版DVD的所有權,也就同時得到了出租的權利。這條規定在只能靠實體店面出租的年代影響不大,但在網路時代,新的線上出租模式卻能把影片租到全台灣,透過網路放大影響力,這個過去被忽視的法條,卻變成陳主望顛覆傳統、開拓線上租片產業的武器。

得利影視總經理張國強拿出智慧財產局二○○三年的公文指出,這只限於賣給一般人的銷售版,威望若是向出租店購買出租版DVD,就涉嫌侵害得利的專屬出租權。這是因為得利會跟出租店簽訂契約,保留影片的所有權,如果威望是向出租店買出租版影片,就不是合法取得影片,在網路上租出租版影片,很有可能違反著作權法。

戰鼓隆隆的這起商戰,雖然雙方已進入訴訟程序,但也打響了威望的名聲。二○○六年,威盛電子從硬體跨足軟體,成立威望國際,資本額新台幣一億六千萬元,威盛持股超過七成。今年初威望國際推出CatchPlay線上娛樂平台,主要營業項目是提供線上遊戲、影片付費下載、出租服務。 陳主望講話帶著標準美式英文口音,他從美國加州大學柏克萊分校畢業後,就進入威盛擔任總經理特助,「我在威盛已經十一年了,」他解釋。不管是擔任多普達董事長,創立威望國際,都是因為威盛要開發新事業。今年八月他更與全家等便利商店合作,仿效美國Netflix網路租片的經營模式,在威望的CatchPlay線上娛樂平台,提供網路「長租」影片服務。

破壞力何在? 價格不到市價一半、通路更便利

這個在美國一炮而紅的Netflix模式,衝擊最大的就是傳統影視出租店,以往租還片都需到實體出租店,而且必須按「次」租;但Netflix把它變成可在便利商店進行的「月」租服務,並可無限次更換電影,消費者買的是隨時在手邊保留三到五支DVD的權利,就像上網購物,超商取貨的模式,消費者改成在網路上選片,網站就會透過郵局送片,影片看完後再寄回網站,這種模式比傳統出租店更彈性,選擇也更多。Netflix也因為這個顛覆性的新商業模式,二○○二年就在美國掛牌,Netflix的出現,也讓百視達在美國的營收快速下滑。

這個網路線上破壞式創新,也在台灣業界引起極大震撼,「如果威望贏了,整個既存的影片出租業很可能會重創,」一位資深電影發行商觀察。

威望的威力究竟在哪裡?

第一是價格破壞。今年五月,威望國際以十九元線上下載電影打出名號,但僅限於非美國八大電影公司的「獨立製片」;八月開始,打出三十九元的線上租片服務第二炮,不到市場價格的一半,可租八大影業的影片。

威望的價格破壞力,讓得利影視總經理張國強大聲批評,「為取得美國八大影業的代理權,我們連DVD盒上的圖檔、影帶的行銷方式都要送給八大影業公司審查,每支影片付出去的權利金至少五○%以上,威望憑什麼能不跟我們取得授權,就以超低價出租八大影業的電影,這是侵害我們的出租權。」 第二是通路破壞。CatchPlay推出的線上租片服務,包括單片出租與包月暢租方式,會員可由線上租片,並透過全家、萊爾富及OK便利商店取片與還片。目前傳統通路百視達全省共一百二十八家分店,亞藝一百零一家店,但三家便利商店的通路就有四千三百六十六家,是傳統出租店的十八倍以上,而且未來如果與統一超商接洽成功,租片點總共將達八千家以上。

美國線上租片公司Netflix,與百視達市占率已達到五比五,影響力足以撼動整個市場;威望也估計,未來CatchPlay要攻下國內租片市場的三成市場。

也因為網路的強大殺傷力,讓翁明顯急得跳腳,一狀告上法院。

翁明顯近年積極跨足影視娛樂事業,包括投資拍攝電影、買片、代理影片、出租影片通路、投資威秀影城,全心投資上中下游影視產業,就是要打造台灣影視王國。

誰的訴訟占優勢? 中環財力厚,威盛纏訟經驗豐

翁明顯的大手筆,不但從上游投資拍攝「魔戒」,最近又投資數億元拍攝「赤壁之戰」。中環為了布建娛樂影音市場,之前也繳了不少學費,旗下的中藝,專門買國外影片進口播映;而中環持股超過五○%的得利影視,每年投資超過億元。中環砸大錢,就是為整合產業鏈,現在中藝好不容易開始賺錢,得利也好不容易成為台灣唯一的影片代理商,辛苦打下的江山,怎可拱手讓人?

張國強接受訪問時,指責陳主望侵害得利影音代理的電影DVD版權,「侵犯版權的行為,一般人都不會做,為什麼像他(指陳主望)這樣的名門之後,創業要這樣游走在灰色地帶,」他質疑。

三十三歲的王家第三代小王子,為什麼敢正式挑戰翁明顯的影視帝國?

「以前我也參與過威盛和英特爾的訴訟,我那時候在美國,involve(參與)得非常深,」陳主望說。他一直跟在威盛總經理陳文琦身邊,早就見識過國際訴訟的大陣仗,威盛與英特爾纏訟多年,陳主望早就熟悉智財權的戰爭,也精於攻防。 陳主望在威盛負責過美國、中國、南韓、歐洲的銷售業務,是王雪紅重點扶植的接班人之一。平日十分低調的陳主望,這次碰上了翁明顯的鐵板,反而戰鬥意志十足,「當業務談不下來的時候,我最興奮,」他笑著說,「因為當你覺得這幾乎不可能達成的時候,就是你快成功的時候,」臉上充滿叫戰的興奮。

王雪紅怎麼看呢?「Shieh(王雪紅英文名)經營CatchPlay的指示是:『一切合法、堅持到底』,」陳主望說。

威望一切有備而來,甚至已跟母公司威盛的法務人員切磋,「如果有需要,威盛的人隨時可以過來支援,」他說。

叫王雪紅敢放手讓陳主望一搏的,還有一名關鍵戰將,就是比陳主望小一歲的好友張心望,張心望是台大電機系畢業,他從史丹佛大學畢業後,還在美國自己創立一家軟體公司,創立威望之前,陳主望還帶他去拜訪王雪紅,「Shieh跟我說,你要好好抓住這個人,」威盛集團很多公司,就是從對的一個人、兩個人開始的,陳主望說。

這宗掀起國內影視租片大戰的豪門之戰,究竟誰的勝算大?

誰的勝算大? 學者依法論法,威望未必站不住腳

有業者形容,威望的模式就像開二手租書店,目前二手租書店合法,但是否意味著線上的影音出租店也合法,值得推敲。台大法律系教授謝銘洋認為,「按照著作權法第六十條,如果消費者買的是正版,又是用合法方式取得DVD,這麼做是合法的,」他說,許多國家也都同意,除了電腦程式、音樂等容易複製的產品之外,消費者只要買斷所有權,就有權利出租自己擁有的DVD等內容產品。換句話說,威望如果是合法取得出租版DVD所有權,線上出租的行為在法界未必站不住腳。

但是張國強懷疑,威望國際對外出租的影片雖超過半數都註明是得利影視授權出租版,但卻查不出條碼,不排除是從得利授權的影片出租店流出,再拿到網路轉租。 智慧財產局著作權組組長張玉英表示,得利租給出租店的DVD分兩種,其中雖然有不得轉租的片子,另一種是賣斷所有權的。

換句話說,如果威望證明自己的影片是正版,同時出示能證明合法取得的發票,得利影視就不能說威望侵害了他們的出租權。

面對大老叫戰,陳主望接受訪問時顯得非常樂觀,「我是那種一躺下去就會睡著的人,」「我覺得人生不可能一直都好,過去越辛苦,過了反而更快樂。」

就像當年英特爾和威盛的法律大戰一樣,威望和得利各自出招,背後卻有深意,「被告,其實證明這個模式有影響力,值得被注意,」張心望說。對剛起步的網站來說,訴訟案一鬧,反而可以增加知名度,累積網站人氣其實更重要。

對陣的張國強也不示弱,他表示「暫時不考慮和解」。其實,在訴訟同時中環已開始布局IPVT(網路電視),最快今年底提供網路化新服務,屆時究竟是誰勝出,這場舊商業模式和新商業模式的大戰,好戲才剛上場。


陳主望小檔案
出生:1974年
學歷:加州大學柏克萊分校工程學系
經歷:威盛電子總經理特助、多普達董事長
現職:威望國際董事長

《如果你愛上一家書店》 令人神往的書店風景




一到書店我就不能自持。從貨源最足的巴黎書城到購物中心的無名書店,任何書店都各有驚喜。從書店三十年前第一次打動我的心後,身為顧客、雇員、銷售代表或遊客,我造訪過數千家書店。每一家都無拘束地散發特有的喜悅。

傍晚時分,女兒麥蒂和我有時會散步到街角的黑橡樹書店(Black Oak Books),店裡很簡樸,幾排整齊的淺色書架,沒有特別布置,就書店而言有些別出心裁。書店有一些新書,但主要還是銷售舊書。兒童區不大,麥蒂所以喜歡來是因為她可以隨意翻書。

我們向正在門前閒聊的卡爾和瑪麗亞打聲招呼,就鑽進狹窄的走道。我們會認真翻閱兩、三本書,最後選上一本。這種時刻讓我們心儀的是書店的安適、平靜、人少,我們似乎是唯一的顧客。離開時,橙色的晚霞在西方天際燃燒, 彷彿有一道綠光倏忽而過。我們知道上床休息的時間快到了,今天將有一本新書或兩本「舊好」相伴。

城市之光詩歌室:有各種奇遇的安靜洞窟

每個人都有自己心儀的書店名單:我喜歡這些書店確是因為我偶然撞見了它們。書店給人的樂趣之一就是為數眾多、各有妙趣。

舊金山的招牌──城市之光書店,因「垮掉的一代」出沒而理所當然聲名遠揚,開業五十年依然是不可或缺的城市地景。書店臨近哥倫布和百老匯大道交叉口,西方被中國城包圍,東面是一排脫衣舞俱樂部,南邊是舊金山商業區光怪陸離的天際線。

這個地點因與眾多不太體面的咖啡館和三家大酒吧(隔壁是維蘇威Vesuvio,街對面則是斯倍克Spec's 和托斯卡Tosca)比鄰更加有名。天晴的日子,你甚至可以碰見費林蓋提本人站在街角,靠著他的自行車,凝視他參與建立的這塊美麗所在。

這地方本身就值得一遊,書店裡的圖書更是讓你目瞪口呆。前門的收銀台夾在破舊狹窄的樓梯間,大量新書就塞在角落或縫隙裡。最讓我驚訝的是世界上沒有任何一家別的書店會像那樣,用如此另類的書籍吸引顧客:從冰島小說到五百頁厚的購物拱廊史。

正廳擺滿了小說,按「城市之光」獨有的方式排列:美國、英國、歐洲、亞洲、拉美等等。那裡還有「城市之光」自己出版的全部圖書,以及品類繁多的文學雜誌和期刊;最引人入勝的是一架他們出版的詩集,有許多是訂書機裝訂、模糊不清的影印本。這家書店最最精華部分,是沿著後樓梯上去的詩歌室,那是個可以有各種奇遇的安靜洞窟,城市的喧囂淡去,由詞彙組成白色的靜謐世界將你包裹。

紀伊國屋書店: 書籍成為無涉意義的物件

「城市之光」在當地以顧客服務著名,但也許不是你期望的那種。北灘附近有一家老派義大利餐廳Caffe Sport,侍者教育你可以或不可以吃什麼、只可以吃多少,去吃飯隨時聽到他們的喝斥。但食客趨之若鶩,為了享受這種待遇要排幾個小時的隊。

「城市之光」的客服不會這麼粗魯,照我們的說法是太心不在焉了。顧客的詢問經常換來一聲厭世的嘆氣,然後隨手一指,嘟囔出一個方向。如果你是常客,碰過幾回釘子就再也不會去尋求指引了,但可以坐在一旁看外來客吃癟。我不抱怨「城市之光」的客服,一點兒也不;少了它,書店的格調就不一樣了。所以「城市之光」近乎完美。

舊金山另一家近乎完美的書店,是我對那裡的文字一竅不通的紀伊國屋書店,位於日本城的這家書店大又繁忙,銷售期刊、圖書、CD和文具。儘管書店銷售少部分關於亞洲的英文書籍,但最吸引我的卻是日文書,一種我既不會說也看不懂的語言,書的封面封底正與我們的書相反。

紀伊國屋書店屬於夢中的書店,那裡的景色似曾相識,每個細節又讓我摸不著頭腦,但這不是噩夢。在一個我看不懂書的地方,書籍成為一個無涉內容的物件,別有一番妙處。我凝視著這些外國文字,拿起華美的平裝書,試圖猜出它們的意思,但最終只是滿足於徜徉其間。

夢中書店:除非窮盡所能,否則找不到你要的書

義大利作家卡爾維諾死於1985年,留下列出十五種計畫動筆的著作清單。我願意開一家卡爾維諾書店,最好是在巴黎,因為他在那裡生活多年。在卡爾維諾書店裡,你可以找到各種類型、卡爾維諾沒有機會寫的書,有標準版、袖珍版、對開本和插圖版。你也許還會發現一批你喜歡的已故作家沒能完成的書。每周都有新書上架。

巴黎現在有家書店叫「找不到」(Introuvable),真是一個絕妙的名字。在我完美的書店之城裡,我想像的終極書店也許就是絕對找不到的,這家書店有你想找的任何一本書:從孩提時看過的紫衣小鬼頭的書,到你在裡面寫滿筆記的報稅指南。除非你窮盡所有可能,否則找不到那本你要的書。只有當你不再心存僥倖,你也許就能找到那本找不到的書。

怪怪書店大蒐奇

【聯合報╱摘自《如果你愛上一家書店》)】 2007.11.11 02:56 am


●惡魔島書店

舊金山灣的惡魔島上曾有知名的重刑犯監獄,1963年監獄廢止後,變成觀光景點。島上書店最熱賣的書是從前關過的囚犯、監獄管理員及犯人家屬的回憶錄,書中回顧了這些重囚送到島上服刑的生活點滴。書店裡書種不多,但銷售業績比起周邊旅遊景點的書店一枝獨秀。即使你沒買書,書店醒目的石牆本身就為你講述一個好故事。

●菸草書店

舊金山以南二十英里的海邊小鎮半月灣有一家奇想書店:灣區書店暨菸草店(Bay Books & Tobacco)。書店有世界級的科幻小說專區,還有一個面向大街的空調室,銷售各種牌子的香菸和菸絲。我始終想不透這種搭配怎麼會成功,但確實成功了。抽菸斗或香菸和讀書一樣需要慢慢品味,也和讀書一樣容易陷入沉思和綻放激情。即使你不抽菸斗,書店裡飄溢著附近林木和櫻桃的氣味也使你心曠神怡。

●紋身書店

加州更北一些的紅杉林鎮加伯維爾(Garberville),有一家書店兼營紋身。我在造訪前很多年就聽說過這家書店。沒錯,這裡有一排排的圖書;不同的是書店後面不時發出尖利的叫聲,你沒聽錯。我走近紋身間時,那個在一位婦女肩部輕柔地描繪一朵玫瑰的居然是熟人;這位紋身師和我同在帕洛阿爾托的普林特斯書店共事過,這書店是她最新的投資。我們愉快地聊了很久,但我放棄了首次紋身的機會。我已經記不起這家書店的店名,也許是「墨與墨」(Ink & Ink )?

九歌原型

【聯合報╱蔣勳】 2007.11.12 02:30 am



湘夫人是千年來中國文人寄託愛情的對象。圖為《九歌》中的「湘夫人」,楊美蓉飾。
攝影/謝安,雲門舞集提供
近代神話學的新看法,幫助林懷民可以以更自由活潑的想像空間來看待〈九歌〉。林懷民因此擺脫了長期以來中國文學章句註解學者逐字逐句的引經據典,得以把〈九歌〉還原到初民的生活之中,還原到祭神儀式的歌舞中,還原到人與自然的對話關係之中,給予〈九歌〉全新復活的現代生命…

林懷民重新詮釋〈九歌〉

《九歌》是雲門屆滿二十五周年的重要作品,在林懷民一系列創作中具有劃時代的意義。

從一開始,林懷民的創作與中國傳統的經典就有密切關係,包括從民間傳說、戲曲取材的《奇冤報》、《白蛇傳》,從美術造型出發的《星宿》、《夢土》,從文學經典編作的《紅樓夢》、《九歌》等,都可以看到創作者身上切不斷的廣義中國傳統的脈絡影響。

林懷民取材於中國經典,但也從來不囿限於古老經典的束縛。

他總是重新詮釋經典,使經典可以有存在於現代世界的可能。

在《雲門‧白蛇傳》裡,林懷民重新組織了青蛇與白蛇的關係,使原來產生於封建奴僕關係的青蛇有了獨立發展自我存在個性的可能,青蛇不再附屬於白蛇,甚至敢於去追求許仙,成為有自主人格的角色。

《紅樓夢》本身是極具現代意義的文學經典,林懷民著重在曹雪芹創造的賈寶玉本身,使這個充滿了對封建傳統背叛的角色以赤裸裸的肉體解放形式出現在舞台上。他也重新用春、夏、秋、冬四季的結構布局,使《雲門‧紅樓夢》的繁華到荒涼有了視覺上的意象。

賈寶玉是慾望、背叛、深情而又贖罪的多重意象,他以愛慾為修行,從眷戀糾纏走向巨大深沉的贖罪意識領悟。林懷民的《雲門‧紅樓夢》是全新版本的現代詮釋,但可能也最貼近曹雪芹原作的深沉意涵。

在雲門所改編的經典中,〈九歌〉時代最早,早過清代的《紅樓夢》,早過宋元成形的《白蛇傳》,它是兩千年前楚地的祭神篇章,至少在漢代已成為文體的典範,是經典中的經典,對中國知識分子的影響也早已根深蒂固,其實比其他經典更難動搖或突破。

但是〈九歌〉有一個好處,它徹頭徹尾是一篇神話,神話不管再古老,永遠都具備著最現代也最新的解讀可能。

兩千年來,希臘神話也在西方的世界一再被重新詮釋,賦予完全現代的意義。

民國初年的學者如郭沫若、聞一多,在1930年前後便以神話學的角度指出〈九歌〉新的研究方向。

〈九歌〉原來是楚地祭神頌辭,可能被屈原修飾過,漢代以後就被學者與〈離騷〉、〈天問〉、〈九辯〉這些屈原的作品搜集在一起,由於都是楚地的文學資料,因此就被合稱為《楚辭》,《楚辭》影響了漢代主流文學「賦」的文體,成為文學經典。

最初,學者未必認為《楚辭》一定等於「屈原」,但隨著時代發展,《楚辭》逐漸與「屈原」劃上等號,並且,以屈原的生平特色、歷史事件來註解〈九歌〉,使〈九歌〉越來越失去原有神話的超然性。

近代神話學者重要的指證是:〈九歌〉應該是早於屈原的作品,〈九歌〉至多只是被屈原修飾過,〈九歌〉保留了楚地初民原始祭神的儀式歌舞讚頌,〈九歌〉是巫覡與神的對話。

這些近代神話學的新看法,幫助林懷民可以以更自由活潑的想像空間來看待〈九歌〉。

林懷民因此擺脫了長期以來中國文學章句註解學者逐字逐句的引經據典,得以把〈九歌〉還原到初民的生活之中,還原到祭神儀式的歌舞中,還原到人與自然的對話關係之中,給予〈九歌〉全新復活的現代生命。

林懷民擺脫了〈九歌〉學者章句註解的束縛,把〈九歌〉還原成初民的神話。

神話是初民生存中對一切神祕力量的敬與畏,他們嚮往創造力,嚮往原始生命的永恆壯大力量,他們向著天空或初昇的太陽唱出了「東皇太一」、「東君」;他們尊敬崇拜天空的雲,雲帶來雨水,雨水豐饒大地,他們因此歌詠出了自由活潑的「雲中君」;他們在河流邊行走,在美麗的湘江上行船,歌唱對岸美麗的男子或女子,那歌聲便流傳成了「湘君」、「湘夫人」;他們走向山林,在幽暗隱蔽的角落感覺到「若有人兮」的孤獨憂愁,低聲嘆息的聲音和吟詠變成了山林水湄精靈的「山鬼」;他們也懼畏死亡,不知道何時將至的生命的結束,使他們在冥冥中探索著不可知的主宰生死的力量,他們因此悲歌出沉重的「大司命」與「少司命」;他們也懼畏戰爭,不可知的屠殺,不可知的肢體的分離與斷裂,他們相信每一次戰爭之後,空中便瀰漫著無家可歸的飄零的魂魄,他們要引領那些魂魄回家,因此唱出了「國殤」。最後甚至以「禮魂」來召喚遍散在天地空中的諸神,山林荒原的鬼魅,以及人間無主的魂魄。

〈九歌〉的原始信仰裡有初民的崇敬,感謝,懷念與愛戀。

林懷民在舞台上還原的也正是這些動人的人類初民的歌詠,使原來楚地的神話、中國的文學經典擴大成為世界性共通的聲音。

我寫《舞動九歌》

「看雲門讀經典」系列的緣起,是五部雲門以「經典」出發的舞劇的介紹,一方面重讀「經典」,另一方面也藉著雲門舞台上的表演使「經典」可以更活潑,更具象,也更貼近生活。

《舞動白蛇傳》和《舞動紅樓夢》完成之後,原本計畫推出《舞動薪傳》,但因雲門2007年決定在秋季推出《九歌》,也許正是向大眾介紹《九歌》這部不朽經典的最好時機,《舞動九歌》的寫作計畫就此提前了。

為了《舞動九歌》的寫作,2007年初春,我帶了少部分資料離開台灣,當時台灣正是潮濕又寒冷的季節,瑟縮著脖子,諸神彷彿都很遙遠。〈九歌〉的神話使我想起南方,河流、陽光、鮮花、濃郁的香氣、嫵媚明亮的女子與男子、戴著獰厲華麗面具的巫鬼神靈的歌舞、如慕如怨的婉轉旋律曲調……

這些都這麼像我飛往的國度———東南亞的泰國。

在毗鄰曼谷湄南河的東岸,一間樸素的小小民宿,在六樓面河的房間,陽台上有一張書桌,清晨被附近巨大如傘蓋般的菩提樹上的鳥聲喚醒,坐在書桌前,看河面上流蕩的霧氣,朝日淡粉色的光,閃爍的河流細細的波紋,慢慢多了起來的船隻來往穿梭,載著鮮花和香料的船在岸邊交易,幾座廟宇從遠方傳來誦經的聲音,嘹亮的鐘聲,屋角大樹下有人祭神焚燒起的濃煙的香味,冉冉上升,彷彿升成一朵一朵天空的雲……

這一切都這麼像兩千年前楚地的〈九歌〉,我寫著寫著,覺得〈九歌〉諸神都近在四周,天空中偉大初始的「東皇太一」,明亮陽光燦爛的「東君」,自由自在來去飄飛的「雲中君」,河流上蜿蜒著詠唱愛慕的「湘君」與「湘夫人」……

〈九歌〉似乎從兩千年前的楚地沿河一路南來,到了我書寫〈九歌〉的流域。

許多人去了長沙,去了湘江,卻覺得沒有〈九歌〉諸神的蹤跡。

〈九歌〉諸神當然渴望南方、陽光,渴望悠閒自在的生活,渴望真實的愛與恨,渴望沒有虛度的生與死,渴望浩浩蕩蕩的大河與嫵媚美麗的人民,渴望悠揚的歌聲與翩翩婉轉的舞姿……

我是在諸神的國度寫完《舞動九歌》,諸神一一復活,我覺得自己像一名古老的巫覡,諸神降臨,在我魂魄最深處騷動起來,使我不停筆地書寫,我只是記下了諸神要我說的話語。

應台談三代情 千人捧場

「這不是親子教育書,而是我的人生之書!」甫出版新書「親愛的安德烈」的龍應台,昨天在師大體育館舉行讀書會,暢談她與父母、孩子之間的相處經驗。可容納千人的會場座無虛席,許多聽眾還是兩代攜手聽講。

「比起批評陳水扁、胡錦濤,對我來說,這本書的寫作更是步步危機!」一向予人自信、從容印象的龍應台,在書裡變成「慌張失措的母親」,對於孩子的長大與獨立措手不及。兒子安德烈形容她的出書舉動是「自曝其短的傻子」,可得「勇氣媽媽獎」。

龍應台出身南部鄉下,國中畢業時,父親認為她應該當老師,然後嫁一個鄉下人,希望她考師專;幸好有母親勸說,她得以報考高中。填大學志願時,她想填新聞系,父親又說:「做記者不如做妓女!」這次她騉韝F理想,改填外文系。

「儒家的傳統價值是服從!」然而當龍應台遠嫁德國,當起兩個德國人的媽媽後,面對的卻是兒子的獨立與直言無忌。

龍應台說,她和安德烈的書信在台灣和香港發表時,許多父母寫信給她,而許多孩子則寫信給安德烈,抱怨他們的親子關係有多冰凍。然而親子之道無法複製,她相信只要有一方肯主動,「總可以找到一把鑰匙交到對方手中,讓他開啟你的心門!」聽眾問龍應台,如何培養孩子的國際觀?她回答:「國家不能有鎖國政策」。龍應台認為,台灣有許多外籍新娘、勞工,「為什麼越南、泰國不能成為台灣國際化的一部份?」

「我自己是外籍新娘!」龍應台說,她從孩子呱呱落地起,堅持跟他們說中文,德國也從未禁止外籍新娘跟小孩說母語。

露天湯

泡湯不見得要花大錢,大台北地區有不少免費的露天湯。

金山:金山溫泉屬大屯山地熱帶,是中性碳酸溫泉,水質清澈透明,也無異味,主要集中在金包里老街北方、磺港及水尾一帶。

水尾豐漁村公園對面的金山溫泉泡腳池,每逢假日總有遊客攜家帶眷,坐在溫泉溝旁,一邊享受泡腳的樂趣,一邊欣賞水尾漁港景致。

金山萬里建浴室 八煙野溪燙

陽明山八煙野溪溫泉水溫自3、40度,到最高達80度,愈靠近溫泉露頭的水溫愈高,萬一誤闖露頭區,恐怕會被燙得一身傷,雖然陽管處三令五申,民眾仍不畏危險入內泡湯。

此外,金山鄉公所特別在金包里老街,新蓋一間金包里公共浴室,內部為日式裝潢,設施新穎雅緻,門票只需50元。

萬里:萬里有一間免費的加投公共浴室,屬酸性硫磺,位在大鵬村加投路橋頭邊,1樓分為男女浴池,2樓則以木頭搭蓋一涼亭,供人休閒賞景,但知道的人不多,因此加投公共浴室多半是社區居民使用。

烏來碳酸泉 熱度很適合人體

烏來:烏來溫泉屬碳酸泉,水質無色無味,但是熱度很符合人體需求,所以泡起來非常舒服。

烏來兩處野溪溫泉都位在市區,民眾開車前往非常方便,雖無人管理,可是下水泡湯之前,一定要先清洗身體,否則會被罵。

桂山發電廠出水口處野溪溫泉因位在南勢溪底,所以泡湯更衣沒有遮蔽處;至於熱力溫泉比較有規模,更衣、清洗身體都沒問題。

如果民眾泡完湯,想來個三溫暖,還可以直接跳進南勢溪游泳,但要注意安全。

清水地熱區 高溫煮食攤販多

宜蘭:清水地熱區位在羅東三星鄉,雪山隧道通車後,從台北驅車前往尤其方便,走國道5號自羅東交流道下後,循指標往三星、天送埤至井場,見到清水橋後沿溪旁產業道路前行約4公里,便可見到。清水地熱區的假日比一般野溪熱鬧!因為主坑泉水溫高達攝氏95度,足夠煮食,久而久之,賣雞蛋、鹹鴨蛋、玉米、番薯及飲料、網子的小販便常聞風而來。

台北市磺溪 硫酸鹽泉白如奶

北市:台北市磺溪溫泉應該是比較值得推荐的。由陽明山往金山方向,經馬槽到大油坑站,可看見右側有一封閉的產業道路,這條破舊荒蕪的道路就是曾繁榮一時的「挑硫古道」,循此路即可通往磺溪溫泉。

磺溪溫泉泉質為乳白色的硫酸鹽泉,泉源自岩壁流下,蓄成一處如牛奶匯成的溫泉池,又有「牛奶湯」美譽。