信號量是進程間通信方式的其中一種,那麼爲什麼存在信號量這種機制呢?
信號量主要應用於同步互斥操作,先來了解一下,同步和互斥是什麼?
同步:多個進程需要相互配合才能完成一項任務
互斥: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 手動刪除信號量