信号量介绍

 一.什么资源是临界资源?

临界资源是一次仅允许一个进程使用的共享资源
二.临界区域是什么意思?什么是临界代码?

每个进程中访问临界资源的那段程序称为临界区,或者叫临界代码。
三.临界区或者临界代码有什么特点:

每次只准许一个进程进入临界区,进入后不允许其他进程进入。
四.信号量是解决是什么问题的?

解决在任一时刻只有一个执行线程访问的临界区的代码(即临界代码),防止多个线程或者多个程序同时访问一个共享资源而引发的问题。

因为:程序中存在着一部分临界代码,我们需要确保只有一个进程(或者一个执行线程),可以进入这个临界代码并拥有对该资源的独占式的访问权。
五.信号量的实质:

用于管理对资源的访问;

解决程序对某个特定的资源具有独占式的访问权。
六.能否举个例子,说明哪些代码属于临界代码?

我们用dbm来访问数据库。如果有多个程序试图在同一时间更新这个数据库,哪么这个数据可能会遭到破坏。

       两个不同的程序要求不同的用户向数据库输入数据,这本身并没有错,但是问题可能出现在对数据库更新的哪部分代码上。因为这部分真正执行数据更新的代码需要独占式执行。

我们称这段代码是:临界代码,或者是临界区。
七.进程进入临界区的调度原则是:

①如果有若干进程要求进入空闲的临界区,一次仅允许一个进程进入;

②任何时候,处于临界区内的进程不可多于一个。如已有进程进入自己的临界区,则其它所有试图进入临界区的进程必须等待;

③进入临界区的进程要在有限时间内退出,以便其它进程能及时进入自己的临界区;

④如果进程不能进入自己的临界区,则应让出CPU,避免进程出现“忙等”现象。
七.信号量的分类:

类型
    

区别

二进制信号量
    

1.  只能取值0和1的变量;

2.  最常见的一种信号量形式;

3.  目前,我们集中讨论的就是:二进制信号量;

通用信号量
    

1.可以取多个正整数值的变量
八.信号量是什么特殊的变量

她是只能取整数值的变量。
九.问什么一个普通变量进行类似的加减法不行?

传统的编程语言如C、C++等语言,都没有一个原子操作,就是检测:检测一个变量是否为true,如果是true的话,再将其变量设置为false。
十.原子操作如何理解:

1.原子操作就是不可拆分的操作

例如,mv就是原子操作,cp就不是原子操作。

2.在多进程(线程)的操作系统中不能被其它进程(线程)打断的操作就叫原子操作;

3.文件的原子操作是指操作文件时的不能被打断的操作。

4.原子还有一层意思,在该次操作不能完成的时候,必须回到操作之前的状态,原子不可分嘛!

如果你删除文件,只删了一半发生错误,你不能只留下另一半吧,必须恢复整个文件。

5.  所有原子操作是同步的,而且不可被外部中断(内中断可以,不过一般是致命错误处理)。

6.  即不可中断的一个或一列系操作

7.  信号量和锁的PV操作可以用CPU的XCHG指令来实现,

8.  原子操作可以用信号量来实现

9.  任何CPU的中断都只能发生在指令边界上

10.(即指令在流水线的执行单元上跑到一半的时候中断/异常是不会发生的)

11.所以原子操作的前提是test和change操作必须在一个指令中完成。

12.原子操作的要求是不能在执行的过程中被中断抢占(要知道操作系统的进程调度也是通过时钟中断引发的)
十一.信号量有什么特点:

只能对该变量,该值进行2种操作,分别是:P操作和V操作。

P操作:用于等待,就是进入临界区域之前的检查点;

V操作:用于信号,就是放弃对临界区的控制权,或者释放资源;
十二.P,V原语理论是谁提出的,该科学家在计算机领域还有什么建树?

赫赫有名的荷兰科学家E.W.Dijkstra

P,V原语的概念以及P,V操作当中需要使用到的信号量的概念都是由他在1965年提出的。

他提出了图论中最短路径问题的Dijkstra算法。
十三.信号量的历史,最开始是解决什么问题的?

信号量是最早出现的用来解决进程同步与互斥问题的机制,包括一个称为信号量的变量及对它进行的两个原语操作。信号量为一个整数,我们设这个信号量为:sem。很显然,我们规定在sem大于等于零的时候代表可供并发进程使用的资源实体数,sem小于零的时候,表示正在等待使用临界区的进程的个数。根据这个原则,在给信号量附初值的时候,我们显然就要设初值大于零。
十四.PV操作的特点:

P操作和V操作是不可中断的程序段,称为原语。

P,V原语中P是荷兰语的Passeren,相当于英文的pass,

V是荷兰语的Verhoog,相当于英文中的incremnet。
十五.PV操作到底实现了什么操作,能够详细解释下吗?
1.P原语操作的动作是:

信号量为一个整数,我们设这个信号量为:sem

(1) sem减1;

(2) 若sem减1后仍大于或等于零,则进程继续执行;

(3) 若sem减1后小于零,则该进程被阻塞后(也就是挂起该进程的执行),进入与该信号相对应的队列中,然后转进程调度。
2.V原语操作的动作是:

(1) sem加1;

(2) 若相加结果大于零,则进程继续执行;

(3) 若相加结果小于或等于零,则从该信号的等待队列中唤醒一等待进程,然后再返回原进程继续执行或转进程调度。
十六.P,V操作有什么注意点?

1.  P,V操作对于每一个进程来说,都只能进行一次;

2.  而且必须成对使用;

3.  且在P,V原语执行期间不允许有中断的发生;
十七.信号量的缺点是什么?

1.信号量机制必须有公共内存;

2.不能用于分布式操作系统;
十八.如何实现P,V操作?

1.  可以用硬件实现;

2.  也可以用软件实现;

实现的参考代码如下:

procedure p(var s:samephore);

{

s.value=s.value-1;

if (s.value<0) asleep(s.queue);

}

procedure v(var s:samephore);

{

s.value=s.value+1;

if (s.value<=0) wakeup(s.queue);

}

其中用到两个标准过程:

asleep(s.queue);执行此操作的进程控制块进入s.queue尾部,进程变成等待状态

wakeup(s.queue);将s.queue头进程唤醒插入就绪队列

对于这个过程,s.value初值为1时,用来实现进程的互斥。
十九.信号量的初始值是什么?为什么是这样?

       信号量的初始值是1。

       信号量为一个整数,我们设这个信号量为:sem

1.我们规定在sem大于等于零的时候,代表可供并发进程使用的资源实体数;

2.sem小于零的时候,表示正在等待使用临界区的进程的个数。

根据这个原则,在给信号量附初值的时候,我们显然就要设初值大于零。

 
二十.如何用P V原语实现进程互斥?

把临界区置于P(sem) 和V(sem)之间。当一个进程想要进入临界区时,它必须先执行P原语操作以将信号量sem减1,

在进程完成对临界区的操作后,它必须执行V原语操作以释放它所占用的临界区。从而就实现了进程的互斥:

 

具体的过程我们可以简单的描述如下:

 

PA:

P(sem)

<S>;

V(sem)

PB:

P(sem)

<S>;

V(sem)

 
二十一.用P V原语如何实现进程通信

我们以邮箱通信为例说明问题:
邮箱通信满足的条件是:

<1>;发送进程发送消息的时候,邮箱中至少要有一个空格能存放该消息。

<2>;接收进程接收消息时,邮箱中至少要有一个消息存在。

发送进程和接收进程我们可以进行如下的描述:

Deposit(m)为发送进程,接收进程是remove(m).

Fromnum为发送进程的私用信号量,信箱空格数n。mesnum为接收进程的私用信号量,初值为0.

2个信号量,分别是:发送进程Fromnum和接收进程mesnum
1.发送进程的实现:

Deposit(m)

 

Begin local x

P(fromnum) //发送进程的P操作

 

选择空格x //邮箱中至少要有一个空格能存放该消息

将消息m放入空格x中

置格x的标志为满

 

V(mesnum) //接受进程的v操作

end
2.接受进程的实现:

Remove(m)

 

Begin local x

 

P(mesnum) //接受进程的P操作

 

选择满格x //邮箱中至少要有一个消息存在

把满格x中的消息取出放m中

置格x标志为空

 

V(fromnum) //发送进程的v操作

End
二十二.如何使用信号量的几个步骤:

1.创建一个新的信号量;

2.初始化信号量;

3.在程序进入临界区的时候,调用P操作,设置信号量以等待进入;

4.离开临界区的时候,调用V操作,设置信号量,来表示可以再次访问和操作;

5.删除信号量。
二十三.使用信号量的源码:
1.创建一个信号量:

static int sem_id;

sem_id = semget((key_t)1234, 1, 0666 | IPC_CREAT);
2.初始化信号量:

static int set_semvalue(void)

{

    union semun sem_union;      

    sem_union.val = 1;

    //semctl函数允许我们直接控制信号量信息

    //SETVAL用来把信号量初始化为一个已知的值,其作用是在信号量第一次使用之前,//对它进行设置;返回-1:表示失败

 if (semctl(sem_id, 0, SETVAL, sem_union) == -1)//semctl函数允许我们直接控制信号量信息

    return(0);

          

    return(1);

}
3.删除信号量:

//删除信号量ID,是如何删除的呢?通过将semctl函数的command参数设置为IPC_RMID,然后调用该函数,来进行删除

static void del_semvalue(void)

{

    union semun sem_union;

//IPC_RMID用于删除一个已经无需继续使用的信号量标识符

 if (semctl(sem_id, 0, IPC_RMID, sem_union) == -1)//semctl函数允许我们直接控制信号//量信息

        fprintf(stderr, "Failed to delete semaphore\n");

}
4.P操作:

//信号量的P操作

static int semaphore_p(void)

{

    struct sembuf sem_b;   

    sem_b.sem_num = 0;//表示信号量的编号,如果一个信号量,其取值为0,如果是一组信号量,则是非0

    sem_b.sem_op = -1; /* P() *///-1表示P操作,她等待一个信号量变成可用,也就是说,此时可以进入临界区,进行工作

    sem_b.sem_flg = SEM_UNDO;//她将使得操作系统跟踪当前进程对这个信号量的修改情况

    //进程退出时,清除其信号量

    if (semop(sem_id, &sem_b, 1) == -1) //semop函数用于改变信号量的值

    {

        fprintf(stderr, "semaphore_p failed\n");

        return(0);

    }

    return(1);

}
5.V操作:

//信号量的V操作

static int semaphore_v(void)

{

    struct sembuf sem_b;

   

    sem_b.sem_num = 0;

    sem_b.sem_op = 1; /* V() */

    sem_b.sem_flg = SEM_UNDO;

    if (semop(sem_id, &sem_b, 1) == -1) //

    {

        fprintf(stderr, "semaphore_v failed\n");

        return(0);

    }

    return(1);

}
二十四.信号量的补充说明:

1.信号量对进程而言是共享的的,也就是说对系统来说,必须要有共享内存;

2.如果不删除信号量,它将继续在系统中存在,即使无程序在使用她也是粗次。

3.信号量也是一种有限资源,大家需要节约使用。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值