信號量的本質是一種數據操做鎖,它自己不具備數據交換的功能,而是經過控制其餘的
通訊資源(文件,外部設備)來實現進程間通訊, 它自己只是一種外部資源的標識。信號量在此過程當中負責數據操做的互斥、同步等功能。當請求一個使用信號量來表示的資源時,進程須要先讀取信號量的值來判斷資源是否能夠。大於0,資源能夠請求,等於0,無資源可用,進程會進入睡眠狀態直到資源可用。當進程再也不使用一個信號量控制的共享資源時,信號量的值+1,對信號量的值進行的增減操做均爲原子操做,這是因爲信號量主要的做用是維護資源的互斥或多進程的同步訪問。但在信號量的建立及初始化上,不能保證操做均爲原子性。
ide
1、爲何要使用信號量?
spa
爲了防止出現因多個程序同時訪問一個共享資源時引起的一系列問題,咱們須要一種方法,它能夠經過生成並使用令牌來受權,在任什麼時候刻只能有一個執行線程訪問代碼的臨界區域。臨界區域是指執行數據更新的代碼須要獨佔式地執行。而信號量就能夠提供這樣的一種訪問機制,讓一個臨界區同一時間只有一個線程在訪問它, 也就是說信號量是用來調協進程對共享資源的訪問的。其中共享內存的使用就要用到信號量。
線程
2、 信號量的工做原理orm
因爲信號量只能進行兩種操做等待和發送信號,即P(sv)和V(sv),他們的行爲是這樣的:
P(sv):若是sv的值大於零,就給它減1;若是它的值爲零,就掛起該進程的執行;
V(sv):若是有其餘進程因等待sv被掛起,就讓它恢復運行,若是沒有進程因等待sv而掛起,就給它加1.舉個例子,就是兩個進程共享信號量sv,一旦其中一個進程執行了P(sv)操做,它將獲得信號量,並能夠進入臨界區,使sv減1。而第二個進程將被阻塞進入臨界區,由於當它試圖執行P(sv)時, sv爲0,它會被掛起以等待第一個進程離開臨界區域並執行V(sv)釋放信號量,這時第二個進程就能夠恢復執行。
進程
代碼以下:ip
comm.h:內存
#pragma once資源
#include<stdio.h>get
#include<sys/types.h>同步
#include<sys/ipc.h>
#include<sys/sem.h>
#include<errno.h>
#include<unistd.h>
#define _PROJ_NAME_ "/tmp"
#define _PROJ_ID_ 0X666
union semun{
int val;
struct semid_ds *buf;
unsigned short *array;
struct seminfo *_buf;
};
int creat_sem_set(int sems);
int get_sem_set();
int init_sem_set(int sem_id,int which,int _val);
int destory_sem_set(int sem_id);
int P(int sem_id,int which);
int V(int sem_id,int which);
comm.c:
#include"comm.h"
static int comm_sem_set(int sems,int flags)
{
key_t _key=ftok(_PROJ_NAME_,_PROJ_ID_);
if(_key<0)
{
perror("ftok");
return -1;
}
int sem_id=semget(_key,sems,flags);
if(sem_id<0)
{
perror("semget");
return -1;
}
return sem_id;
}
int creat_sem_set(int sems)
{
int flags=IPC_CREAT|IPC_EXCL|0644;
return comm_sem_set(sems,flags);
}
int get_sem_set()
{
int flags=IPC_CREAT;
return comm_sem_set(0,flags);
}
int init_sem_set(int sem_id,int which,int _val)
{
union semun _un;
_un.val=_val;
if(semctl(sem_id,which,SETVAL,_un)<0){
perror("semctl");
return -1;
}
return 0;
}
int destory_sem_set(int sem_id)
{
if(semctl(sem_id,0,IPC_RMID)<0)
{
perror("semctl");
return -1;
}
return 0;
}
static int op(int sem_id,int which,int op)
{
struct sembuf _sem;
_sem.sem_num=which;
_sem.sem_op=op;
_sem.sem_flg=0;
if(semop(sem_id ,&_sem, 1)<0){
perror("semop");
return -1;
}
return 0;
}
int P(int sem_id,int which)
{
return op(sem_id,which,-1);
}
int V(int sem_id,int which)
{
return op(sem_id,which,1);
}
test_sem.c:
#include"comm.h"
int main()
{
int sem_id=creat_sem_set(1);
//sleep(10);
init_sem_set(sem_id,0,1);
pid_t id=fork();
if(id==0){
int c_sem_id=get_sem_set();
while(1){
P(c_sem_id,0);
printf("A");
fflush(stdout);
usleep(rand()%12345);
printf("A");
fflush(stdout);
usleep(rand()%123415);
V(c_sem_id,0);
}
}else
{
while(1){
P(sem_id,0);
printf("C");
fflush(stdout);
usleep(rand()%12344);
printf("C");
fflush(stdout);
usleep(rand()%123454);
V(sem_id,0);
}
wait(NULL);
destory_sem_set(sem_id);
}
return 0;
}