信号量是进程间通信方式的其中一种,那么为什么存在信号量这种机制呢?
信号量主要应用于同步互斥操作,先来了解一下,同步和互斥是什么?
同步:多个进程需要相互配合才能完成一项任务
互斥:1.由于各个进程都要访问共享资源,而且这些资源需要排它使用,因此各个进程间需要竞争使用这些资源,我们将这种关系称为进程的互斥。
2.系统中某些资源一次只允许一个进程使用,称这样的资源为临界资源或者互斥资源
3.在进程中涉及到互斥资源的程序的叫临界区
互斥:P和V操作在同一个进程
同步:P和V操作不在同一个进程中
信号量值含义:
S>0: S表示可用资源个数
S=0:S表示无可用资源,无等待进程
S<0:|S|表示等待队列中进程个数
p原语:
p(s)
{
s.value --;
if(s.value < 0){
将该进程置为等待状态
将该进程的task_struct插入到相应的等待队列
}
};
V原语:
V(s)
{
s.value ++;
if(s.value <= 0)
{
唤醒等待队列上的进程
将其改成就绪状态
放入就绪队列
}
}
我们了解了信号量的作用和原理,下面我们看看信号量集是怎么样的:
信号量集创建:
创建:
int semget(key_t key,
int nsems, //信号量的个数 0
int semflg);//IPC_CREAT|0644 0
设置初值:
union semun{
int val; /*value for SETVAL*/
};
int semctl(int semid,
int semnum,//第几个信号量 0,...........
int cmd, //SETVAL
...);//semun 的值
获得信号量当前值:
int semctl(int semid,
int semnum,//获得第几个信号量的当前值
int cmd, //GETVAL
);
返回值:是信号量的当前值
设置PV操作:
int semop(int semid,
struct sembuf *sops,//数组
unsigned nsops);//数组大小
struct sembuf{
unsigned short sem_num; //要操作第几个信号量
short sem_op; /* -1(p) 1(v)*/
short sem_flg; // 0
}
下面是实验代码:
两个进程对标准输出互斥打印:
comm.h
#ifndef _COMM_H
#define _COMM_H
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/ipc.h>
#include<sys/sem.h>
#include<sys/types.h>
union semun{
int val;
struct semid_ds *buf;
unsigned short *array;
struct seminfo *_buf;
};
int createSemSet(int nums);
int initSem(int semid,int nums,int initVal);
int getSemSet(int nums);
int P(int semid,int who);
int V(int semid,int who);
int destroySemSet(int semid);
#endif
comm.c
#include "comm.h"
static int commSemSet(int nums,int flags)
{
key_t key = ftok(".",0x6666);
if(key < 0)
{
perror("ftok\n");
return -1;
}
int semid = semget(key,nums,flags);
if(semid < 0)
{
perror("semget\n");
return -2;
}
return semid;
}
int createSemSet(int nums) //创建信号集
{
return commSemSet(nums,IPC_CREAT|IPC_EXCL|06666);
}
int getSemSet(int nums)
{
return commSemSet(nums,IPC_CREAT);
}
int initSem(int semid,int nums,int initVal) //初始化信号集
{
union semun _un;
_un.val = initVal;
if(semctl(semid,nums,SETVAL,_un) < 0)
{
perror("semctl\n");
return -1;
}
return 0;
}
static int commPV(int semid,int who,int op) //PV操作
{
struct sembuf _sf;
_sf.sem_num = who;
_sf.sem_op = op;
_sf.sem_flg = 0;
if(semop(semid,&_sf,1) < 0)
{
perror("semop\n");
return -1;
}
return 0;
}
int P(int semid,int who)
{
return commPV(semid,who,-1);
}
int V(int semid,int who)
{
return commPV(semid,who,1);
}
int destroySemSet(int semid)
{
if(semctl(semid,0,IPC_RMID)<0) //销毁信号集
{
perror("semctl\n");
return -1;
}
}
sem_test.c
#include"comm.h"
#include<wait.h>
int main ()
{
int semid = createSemSet(1);
initSem(semid,0,1);
pid_t pid = fork();
if(pid < 0)
{
perror("fork()\n");
exit(1);
}
if(pid == 0)
{
int _semid = getSemSet(0);
while(1)
{
P(_semid,0);
printf("A");
fflush(stdout);
usleep(123456);
printf("A");
fflush(stdout);
usleep(344323);
V(_semid,0);
}
}else{
while(1)
{
P(semid,0);
printf("D");
fflush(stdout);
usleep(122222);
printf("D");
fflush(stdout);
usleep(222222);
V(semid,0);
}
wait(NULL);
}
destroySemSet(semid);
return 0;
}
程序效果图:
A 和 D都是成对打印的,互斥访问stdout:
ipcs -s
可以使用 ipcrm -s semid 手动删除信号量