问答题
阅读以下关于利用信号量机制解决进程同步与互斥方面的应用实例,回答问题1至问题3。
[说明]
在多道程序系统中,进程是并发执行的。这些进程间存在着不同的相互制约关系,主要表现为同步和互斥两个方面。信号量机制是解决进程间同步与互斥的有效方法。下面是信号量应用实例。
下图所示代码是在μC/OS-II操作系统上运行的一个应用的主函数。该函数创建了任务Task1和Task2,其中Task1实现从键盘读入一个字符的功能,Task2将该字符输出到屏幕,它们使用信号量和一个公共变量buffer来传递该字符。
主函数、Task1和Task2中所调用的函数原型说明如下:
a.创建一个信号量:OS EVENT*#OSSemCreate(INT16Uvalue);
b.创建一个任务:INT8U OSTaskCreate(void(*#task)(void*#pd),void*#pdata,OS_STK *#ptos,INT8U prio);
c.开始执行多任务:void OSStart(void);
d.从键盘读入一个字符:char scanc();
e.输出一个字符至屏幕:void printc(char ch);
f.发出一个信号量:INT8U OSSemPost(OS EVENT*#pevent);
g.等待一个信号量:void OSSemPend(OS_EVENT*#pevent,INT16U timeout,INT8U *#err)
【正确答案】
【答案解析】[问题1]
临界资源:一次只能给一个进程访问的资源。
临界区:进程中访问临界资源的那段代码。
访问临界资源时应遵循如下原则:
(1)空闲让进(或有空即进)。
(2)忙则等待(或无空则等)。
(3)有限等待。
(4)让权等待。
[解析] 本题考查进程同步与互斥的基本概念和应用。
[问题1]
在多道程序系统中,进程是并发执行的,这些进程之间存在着不同的相互制约关系。进程之间的这种制约关系来源于并发进程的合作以及对资源的共享。
进程在运行过程中,一般会与其他进程共享资源,而有些资源的使用具有排他性。系统中的多个进程可以共享系统的各种资源,然而其中许多资源一次只能为一个进程所使用,通常把一次仅允许一个进程使用的资源称为临界资源。许多物理设备都属于临界资源,如打印机、绘图机等。除物理设备外,还有许多变量、数据等都可由若干进程所共享,它们也属于临界资源。
进程中访问临界资源的那段代码称为临界区,也称为临界段。
访问临界资源应遵循如下原则:
(1)空闲让进(或有空即进)。当没有进程处于临界区时,可以允许一个请求进出临界区的进程立即进入自己的临界区:
(2)忙则等待(或无空则等)。当已有进程进入其临界区时,其他试图进入临界区的进程必须等待:
(3)有限等待。对要求访问临界资源的进程,应保证能在有限时间内进入自己的临界区;
(4)让权等待。当进程不能进入自己的临界区时,应释放处理机。
【正确答案】
【答案解析】[问题2]
(1)S--
(2)S<O
(3)S++
(4)S<=0
[解析]
[问题2]
信号量是荷兰著名的计算机科学家Dijkstra于1965年提出的一个同步机制,其基本思想是在多个相互合作的进程之间使用简单的信号宋同步。
在操作系统中,信号量是表示资源的实体,除信号量的初值外,信号量的值仅能由 P操作(又称Wait操作)和V操作(又称Signal操作)改变。
设S为一个信号量,P(S)执行时主要完成的功能为:先执行S=S-1,若S≥0,则进程继续运行;若S<O,则阻塞该进程,并将它插入该信号量的等待队列中。
V(S)执行时主要完成的功能为:先执行S=S+1,若S>O,则进程继续执行;若 S≤0,则从该信号量等待队列中移出第一个进程,使其变为就绪状态并插入就绪队列,然后再返回原进程继续执行。
P、V操作的形式化描述如下:
P(S)
{
S --;
IF(s<o ) {
阻塞该进程;
将该进程插入信号量s的等待队列;
}
}
V(S)
{
S++;
IF(S<= 0) {
从信号量s的等待队列中取出队首进程;
将其插入就绪队列;
}
}
【正确答案】
【答案解析】[问题3]
(1)OSSemPend(emptySem,O,&err)
(2)Buffer=readc
(3)ret=OSSemPost(fullSem)
(4)OSSemPend(fullSem,O,&err)
(5)ret=OSSemPost(emptySem)
(6)Printc(wntec)
[解析]
[问题3]
本问题是信号量应用实例。
μC/OS-II操作系统提供了操作信号量的若干系统调用,任务Task1为了实现从键盘读入一个字符并写到buffer的功能,就必须在读完字符后调用OSSemPend()和OSSemPost()对写buffer缓冲的动作加锁和解锁。
Task2为了完成将该字符输出到屏幕,也必须在读buffer缓冲的动作时加锁和解锁。这样就避免任务Task1和Task2同时操作buffer缓冲的资源冲突。
因此,任务Task1的代码如下:
void Task1(void*#pdata)
{
INT8U readc;
INT8U err;
INT8U ret;
while(1){
readc=scanc();
OSSemPend(emptySem,0,&err);
Buffer=readc;
ret=OSSemPost(fullSem);
}
}
任务Task2的代码如下:
void Task2(void*#pdata)
{
INT8U writec;
INT8U err;
INT8U ret;
while(1){
OSSemPend(fullSem,0,&err);
writec=buffer;
ret=OSSemPost(emptySem);
Printc(writec);
}
}