进程间通讯之信号量

信号量:进程同步控制

共享资源(临界资源):同一时刻只允许一个进程使用的资源

临界区  ---》     使用临界资源的代码区域

原子操作 ----》     不能被中断的操作

P、V操作:

    P操作 -1 使用资源之前进行P操作

    V操作 +1 使用资源之后释放资源

信号量定义:对资源访问控制的计数器。当其>0时,计数器的值代表能被访问的临界资源的个数。当其<0时,代表等待使用临界资源的进程个数。

信号量操作:

创建获取:

   int semget((key_t) key, int nsems, int flg);

如果semget调用的是创建信号量集,那么nsems就代表信号量集中信号量的个数。否则,nsems无作用。

参数解释

key:所创建或打开信号量集的键值。需要是唯一的非零整数。
nsems:创建的信号量集中的信号量的个数,该参数只在创建信号量集时有效。几乎总是取值为1.
flag:调用函数的操作类型,也可用于设置信号量集的访问权限,两者通过 | 表示
返回值说明:
  如果成功,则返回信号量集的IPC标识符(一个正数)。
  如果失败,则返回-1,errno被设定成以下的某个值
EACCES:没有访问该信号量集的权限
EEXIST:信号量集已经存在,无法创建
EINVAL:参数nsems的值小于0或者大于该信号量集的限制;或者是该key关联的信号量集已存在,并且nsems
  大于该信号量集的信号量数
ENOENT:信号量集不存在,同时没有使用IPC_CREAT
ENOMEM :没有足够的内存创建新的信号量集
ENOSPC:超出系统限制

初始化:

对于新创建的信号量集进行初始化

int semctl(int semid, int num, int cmd/*SETVAL*/, union semun un);

sem_id是由semget返回的信号量标识符。

sem_num与前面一个函数相同。

cnd:表示将要采取的动作。最常用的两个值如下:

SETVAL:用来把信号量初始化为一个已知的值。这个值通过union semun中的val成员设置。其作用是在信号量第一次使用之前对它进行设置。

IPC_RMID:用于删除一个无需继续使用的信号量标志符。

semun联合结构的定义:

1. semun是在linux/sem.h中定义的:  

2union semun

3. {  

4. int val;/*value for SETVAL*/  

5. struct semid_ds *buf;/*buffer for IPC_STAT&IPC_SET*/  

6. ushort *array;/*array for GETALL&SETALL*/  

7. struct seminfo *__buf;/*buffer for IPC_INFO*/  

8. void *__pad;

9. };   

注:初始化时是对信号量集中某一信号量的下标进行初始化。

   P操作:使用临界资源之前-1

   V操作:使用临界资源之后+1

int semop(int semid, struct sembuf buf[], size_t nops);

参数解释:

参数semid是一个通过semget函数返回的一个信号量标识符

参数nops标明了参数semoparray所指向数组中的元素个数

参数buf是一个struct sembuf结构类型的数组指针,

结构sembuf来说明所要执行的操作,其定义如下:

1. struct sembuf

2. {  

3. unsigned short sem_num;  

4. short sem_op;  

5. short sem_flg;  

6. ;

 

释放:int semctl(int semid, int num, int cmd/*IPC_RMID*/);

初始化及最后的释放都是有一个函数完成,只是其中的参数不一样。

首先创建一个sem.h文件来保存一些声明及头文件。
#ifndef __SEM_H
#define __SEM_H
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<assert.h>
#include <sys/sem.h>
union semun
{
 int val;
};
void sem_get();
void sem_p();
void sem_v();
void sem_del();

#endif

再创建一个sem.c文件来对函数实现
#include "sem.h"

int semid = -1;
void sem_get() //  创建或者获取信号量集。
{
 semid = semget((key_t)1234, 1, 0664);
 if(semid == -1)
 {
  semid = semget((key_t)1234, SIZE, IPC_CREAT | 0664);
  if(semid == -1)
  {
   perror("");
   exit(0);
  }
  // 初始化新创建的信号量集
  union semun un;
  un.val = 1;
  semctl(semid, 0, SETVAL, un);
 }
}
void sem_p()  //  完成P操作
{
 struct sembuf  buf;
 buf.sem_num = 0;
 buf.sem_op = -1;
 buf.sem_flg = SEM_UNDO;
 semop(semid, &buf, 1);
}
void sem_v() // 完成V操作
{
 struct sembuf buf;
 buf.sem_num = 0;
 buf.sem_op = 1;
 buf.sem_flg = SEM_UNDO;
 if(-1 == semop(semid, &buf, 1))
 {
  perror("");
 }
}
void sem_del() // 释放信号量集
{
 semctl(semid, 0, IPC_RMID);
}

  完成这两个文件之后我们就将信号量的主体部分实现了,现在就是调用我们实现的各个函数就能够实现进程间的同步控制。例如:
A进程代码:
#include "sem.h"
int main()
{
 sem_get();
 sem_p();
 int i = 0;
 for(;i < 20; ++i)
 {
  sleep(1);
  printf("sema running\n");
 }
 sem_v();
}

B进程代码:
#include "sem.h"
int main()
{
 sem_get();
 sem_p();
 int i = 0;
 for(;i < 10; ++i)
 {
  sleep(1);
  printf("semb running\n");
 }
 sem_v();
 sem_del();
}
使用信号量后执行A、B时永远是A先执行完B才开始执行,B开始时是阻塞状态。


猜你喜欢

转载自blog.csdn.net/magic_world_wow/article/details/79838811