【进程间通信】------信号量(sems)

一直想看看ftok函数,今天就在这里进行简单的介绍下
ftok()函数我们在进行IPC通讯的时候必须指定一个ID值,这个值通常情况下就是由ftok进行提供。

ftok原型如下:
key_t ftok( char * fname, int id )
参数说明:
fname就时您指定的文档名
id是子序号。
返回值:
在一般的UNIX实现中,是将文档的索引节点号取出,前面加上子序号得到key_t的返回值。
如指定文档的索引节点号为65538,换算成16进制为0x010002,而您指定的ID值为38,换算成16进制 为 0x26,则最后的key_t返回值为0x26010002。

认识信号量

信号量主要用于同步和互斥的,信号量的本质是一个计数器,信号量记录了临界资源的数目,数目为多少,信号量的数目就为多少,信号量的值为1的称为二元信号量(也成二元信号量称为互斥锁)。信号量的生命周期随内核。

进程互斥

  • 由于各进程要求共享资源,而且有些资源需要互斥使用,因此各进程间竞争使用这些资源,进程的这种关系为进程的互斥。
  • 系统中某些资源一次只允许一个进程使用,称这样的资源为临界资源或互斥资源。
  • 在进程中涉及到互斥资源的程序叫做临界区
    进程同步指的是多个进程需要相互配合共同完成一项任务。
    信号量和P,V原语(p、v操作是原子的,要么做了,要么没做,无中间态产生。`

  • 信号量和P、V原语由Dijkstra(迪杰斯特拉)提出的

  • 信号量
    互斥:P、V在同一个进程中
    同步: P、V不在同一个进程当中
    信号量的含义:
    s>0:s表示的是可用资源的数目
    s=0:表示无可用资源,无等待进程
    s<0:|s|表示等待队列中的进程个数

信号量结构体伪代码

信号量本质上是一个计数器
struct   semaphore
{
    int  value;
    point_PCB  queue;
}

p原语

p(s)
{
s.value=s.value–;
if(s.value<0)
{
该进程状态置为等待状状态
该进程的PCB插入到相应的等待队列s.queue末尾
}
}

v原语

v(s)
{
s.value=s.value++;
if(s.value>=0)
{
唤醒相应等待队列s.queue中等待的一个进程
改变其状态为就绪态
并将其插入就绪队列
}
}

信号量集函数

semget函数

功能:用来创建和访问一个信号量集
原型:
int semget(key_t key,int nsems,int semflgs);
参数:
key:信号量集的名字
nsems:信号量集中信号量的个数
semflg:由九个权限标志构成,它们的用法和创建文件时使用的mode模式标志是一样的
返回值;成功返回一个非负整数,即该信号即的标识码,失败返回的是-1

shmctl函数

功能:用于控制信号量集
原型:
int semctl(int semid,int semnum,int cmd,….)
参数:
semid:由semget返回的信号集标识码
semnum:信号集中信号量的编号
cmd:将来采取的动作(有三个可能值)
最后一个参数根据命令的不同而不同
返回值:成功返回的是0,失败返回的是-1

命令 说明
SETVAL 设置信号量集中的信号量的计数值
GETVAL 获取信号量集中的信号量的计数值
IPC_STAT 把semid_ds结构中的数据设置为信号量集的当前关联值
IPC_SET 在进程有足够权限前提下,把信号量集的当前关联值设置为semid_ds数据结构中的值
IPC_RMID 删除信号集

semop函数

用来创建和访问一个信号量集
原型:
int semop(int semid ,struct sembuf *sops,unsigned nsops);
参数:
semid:是该信号量的标识码,也是semget函数的返回值
sops:是个指向一个结构数值的指针
nsops:信号量的个数
返回值:成功返回,失败返回-1

sembuf 结构体:

       struct sembuf{
                   short sem_num;
                   short sem_op;
                   short sem_flag;
                   };

sem_num是信号量的编号。
sem_op是信号量一次pv操作时加减的数值,一般只会用到两个值:
一个是“-1“,也就是进行的是p操作,等待信号量变为可用
一个是“+1”,也就是我们进行的v操作,发出信号量已经变得可用
sem_flag两个取值是IPC_NOWAIT或SEM_UNDO

实例代码:
两个进程同时进行打印,显示器为临界资源,使用二元信号量(互斥锁)进行保护。
代码实现:

Makefile

 sem:comm.c sem.c                                                                                                                                          
  2         gcc -o $@ $^
  3 .PHONY:clean
  4 clean: 
  5         rm -f sem
  6 

comm.h

 1 #include <stdio.h>
  2 #include <sys/types.h>
  3 #include <sys/ipc.h>
  4 #include <sys/sem.h>
  5 
  6 
  7 #define PATHNAME "."
  8 #define PROJ_ID 0x6666
  9 
 10 union semun{
 11     int val;
 12     struct semid_ds *buf;
 13     unsigned short *arr;
 14     struct seminfo *_buf;
 15 };
 16 int cerateSemSet(int nums);
 17 int initSem (int semid,int nums,int initVal);
 18 int P(int semid,int who);
 19 int V(int semid,int who);
 20 int destroySemSet(int semid);
 21 
 22 
 23                                                                                                                                                           
~                       

comm.c

1 #include "comm.h"
  2 
  3 static int  commSemSet(int nums,int flags)
  4 {
  5     key_t _key=ftok(PATHNAME,PROJ_ID);
  6     if(_key<0){
  7         perror("ftork");
  8         return -1;
  9     }
 10     int semid=semget(_key,nums,flags);
 11     if(semid<0){
 12         perror("semget");
 13         return -2;
 14     }
 15     return semid;
 16 }
 17 int createSemSet(int nums){
 18     return commSemSet(nums,IPC_CREAT|IPC_EXCL|0666);
 19 }
 20 int getSemSet(int nums){
 21     return commSemSet(nums,IPC_CREAT);
 22 }
 23 
 24 int initSem(int semid,int nums,int initVal){
 25     union semun _un;
 26     _un.val=initVal;
 27     if(semctl(semid,nums,SETVAL,_un)<0){
 28         perror("semctl");
 29         return -1;
 30     }
 31     return 0;
 32 }
 33 
 34 static int commPV(int semid,int who,int op){
 35     struct sembuf _sf;
 36     _sf.sem_num=who;
 37     _sf.sem_op=op;
 38     _sf.sem_flg=0;
 39     if(semop(semid,&_sf,1)<0){
 40         perror("semop");
 41         return -1;
 42     }
 43     return 0;
 44 }
 45 
 46 int P(int semid,int who){
 47     return commPV(semid,who,1);
 48 }
 49 int V(int semid,int who){
 50    return commPV(semid,who,1);
 51 }
 52                                                                                                                                                                                                                                                                            
 53 int destroySemSet(int semid){
 54     if(semctl(semid,0,IPC_RMID)<0){
 55         perror("semctl");
 56         return -1;
 57     }
 58 }

sem.c

 1 #include "comm.h"
  2 
  3 int main()
  4 {
  5     int semid=createSemSet(1);
  6     initSem(semid,0,1);
  7     pid_t id=fork();
  8     if(id==0){
  9         int _semid=getSemSet(0);
 10         while(1){
 11             P(_semid,0);
 12             printf("A");
 13             fflush(stdout);
 14             usleep(100000);
 15             printf("A");
 16             fflush(stdout);
 17             usleep(100000);
 18             V(_semid,0);
 19         }
 20     }
 21     else {
 22         while(1){
 23             P(semid,0);
 24             printf("B");
 25             fflush(stdout);
 26             usleep(100000);
 27             printf("B");
 28             fflush(stdout);
 29             usleep(100000);
 30             V(semid,0);
 31         }
 32             wait(NULL);
 33         }
 34         destroySemSet(semid);
 35         return 0;
 36 }                                                                                                                                                                                                                                                                          

运行结果如下:
这里写图片描述
当我们再一次进行运行这个程序时,我们又会发现什么呢?
这里写图片描述
我们的解决的办法就是:
这里写图片描述

猜你喜欢

转载自blog.csdn.net/daboluo521/article/details/80036983