进程间同步之信号量(semaphore)

进程间同步之信号量(semaphore)

1. 信号量的创建与使用

linux下使用semget创建或打开信号量集,

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

该函数执行成功则返回一个信号量集的标识符,失败返回-1。返回的参数key是由ftok得到的键值;

第二个参数nsems指明要创建的信号量集包含的信号量个数。如果只是打开信号量,把nsems设置为0即可。该参数只在创建信号量集时有效。

第三个参数semflg为操作标识,可取如下值:

0:取信号量集标识符,若不存在则函数会报错

IPC_CREAT:当semflg&IPC_CREAT为真时,如果内核中不存在键值与key相等的信号量集,则新建一个信号量集;如果存在这样的信号量集,返回此信号量集的标识符

IPC_CREAT|IPC_EXCL:如果内核中不存在键值与key相等的信号量集,则新建一个消息队列;如果存在这样的信号量集则报错

上述semflg参数为模式标志参数,使用时需要与IPC对象存取权限(如0600)进行|运算来确定信号量集的存取权限

2.信号量的操作

信号量的值与相应资源的使用情况有关,当它的值大于0时,表示当前可用资源的数量,当他的值小于0时,其绝对值表示等待

使用这个资源的进程个数。信号量的值仅能由PV操作来改变。

在linux下,pv操作通过调用函数semop实现。该函数定义在头文件sys/sem.h中,原型:

int semop(int semid, struct sembuf *sops, unsigned nsops);

对信号量集标识符为semid中的一个或多个信号量进行P操作或V操作

semid:信号量集标识符

sops:指向进行操作的信号量集结构体数组的首地址,此结构的具体说明如下:

struct sembuf {

    short semnum; /*信号量集合中的信号量编号,0代表第1个信号量*/

    short val;/*若val>0进行V操作信号量值加val,表示进程释放控制的资源 */

/*若val<0进行P操作信号量值减val,若(semval-val)<0(semval为该信号量值),则调用进程阻塞,直到资源可用;若设置IPC_NOWAIT不会睡眠,进程直接返回EAGAIN错误*/

  /*若val==0时阻塞等待信号量为0,调用进程进入睡眠状态,直到信号值为0;若设置IPC_NOWAIT,进程不会睡眠,直接返回EAGAIN错误*/

    short flag;  /*0 设置信号量的默认操作*/

/*IPC_NOWAIT设置信号量操作不等待*/

/*SEM_UNDO 选项会让内核记录一个与调用进程相关的UNDO记录,如果该进程崩溃,则根据这个进程的UNDO记录自动恢复相应信号量的计数值*/

  };

nsops:进行操作信号量的个数,即sops结构变量的个数,需大于或等于1。最常见设置此值等于1,只完成对一个信号量的操作

错误代码:

E2BIG:一次对信号量个数的操作超过了系统限制

EACCESS:权限不够

EAGAIN:使用了IPC_NOWAIT,但操作不能继续进行

EFAULT:sops指向的地址无效

EIDRM:信号量集已经删除

 

sops为指向sembuf数组,定义所要进行的操作序列。下面是信号量操作举例。

struct sembuf sem_get={0,-1,IPC_NOWAIT}; /*将信号量对象中序号为0的信号量减1*/

struct sembuf sem_get={0,1,IPC_NOWAIT};  /*将信号量对象中序号为0的信号量加1*/

struct sembuf sem_get={0,0,0};           /*进程被阻塞,直到对应的信号量值为0*/

flag一般为0,若flag包含IPC_NOWAIT,则该操作为非阻塞操作。若flag包含SEM_UNDO,则当进程退出的时候会还原该进程的信号量操作,这个标志在某些情况下是很有用的,

比如某进程做了P操作得到资源,但还没来得及做V操作时就异常退出了,此时,其他进程就只能都阻塞在P操作上,于是造成了死锁。若采取SEM_UNDO标志,就可以避免因为进程异常退出而造成的死锁。

3. semctl (得到一个信号量集标识符或创建一个信号量集对象)

int semctl(int semid, int semnum, int cmd, union semun arg)

semid:信号量集标识符

semnum:信号量集数组上的下标,表示某一个信号量

下面截个图:

下面举例说明:

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <unistd.h>
#include <string.h>


#define NUMBER 5
#define KEY 	1111

union semun{
	int val;
	struct semid_ds *buf;
	unsigned short *array;
	struct seminfo *__buf;
};

int main()
{
	int sem_id = 0;
	union semun semattr;
	struct sembuf sbuf_p = {0, -1, 0};
	struct sembuf sbuf_v = {0, 1, 0};
	pid_t pid;


	sem_id = semget((key_t)KEY, 1,IPC_CREAT | 0666);

	semattr.val = NUMBER;

	semctl(sem_id, 0, SETVAL, semattr);

	pid = fork();	
	if(pid == 0)
	{
		int i = 10;
		printf("enter child process\n");
		while (i > 0)
		{
			semop(sem_id, &sbuf_p, 1);
			printf("the child process get semaphore!\n");
		}	
		return 0;
	}else
	{
		char buf[20]="";
		printf("enter parent process\n");
		while(fgets(buf, 20, stdin) > 0)
		{
			if(strncmp(buf,"over", 4) == 0)
			{
				break;
			}
			printf("parent process set semaphore\n");
			semop(sem_id, &sbuf_v, 1);
		}	
	}
	return 0;
}

 

 

 

模拟进程管理 #include <stdio.h> #include <malloc.h> //Can only be used in independent situation; //#define getmem(type) (type*)malloc(sizeof(type)) #define buffersize 5 int processnum=0;//the num of processes struct pcb { /* 定义进程控制块PCB */ int flag;//flag=1 denote producer;flag=2 denote consumer; int numlabel; char product; char state; struct pcb* processlink; }*exe=NULL,*over=NULL; typedef struct pcb PCB; PCB* readyhead=NULL,* readytail=NULL; PCB* consumerhead=NULL,* consumertail=NULL; PCB* producerhead=NULL,* producertail=NULL; //产品数量 int productnum=0; int full=0,empty=buffersize;// semaphore char buffer[buffersize];//缓冲区 int bufferpoint=0;//缓冲区指针 void linkqueue(PCB* process,PCB** tail); PCB* getq(PCB* head,PCB** tail); bool hasElement(PCB* pro); void display(PCB* p); void linklist(PCB* p,PCB* listhead); void freelink(PCB* linkhead); bool processproc(); bool waitempty(); bool waitfull(); void signalempty(); void signalfull(); void producerrun(); void comsuerrun(); bool hasElement(PCB* pro); void linklist(PCB* p,PCB* listhead) { PCB* cursor=listhead; while(cursor->processlink!=NULL){ cursor=cursor->processlink; } cursor->processlink=p; } void freelink(PCB* linkhead) { PCB* p; while(linkhead!=NULL){ p=linkhead; linkhead=linkhead->processlink; free(p); } } void linkqueue(PCB* process,PCB** tail) { if((*tail)!=NULL){ (*tail)->processlink=process; (*tail)=process; } else{ printf("队列未初始化!"); } } PCB* getq(PCB* head,PCB** tail) { PCB* p; p=head->processlink; if(p!=NULL){ head->processlink=p->processlink; p->processlink=NULL; if( head->processlink ==NULL ) (*tail)=head; } else return NULL; return p; } bool processproc() { int i,f,num; char ch; PCB* p=NULL; PCB** p1=NULL; printf("\n 请输入希望产生的进程个数?"); scanf("%d",&num); getchar(); // if(num>=100){ // printf("您怎么要产生这么多进程!Demands Denied!"); // return false; // } for(i=0;i<num;i++){ printf("\n 请输入您要产生的进程:输入1为生产者进程;输入2为消费者进程\n"); scanf("%d",&f); getchar(); p=(PCB*)malloc(sizeof(PCB)) ; if( !p) { printf("内存分配失败"); return false; } p->flag=f; processnum++; p->numlabel=processnum; p->state='w'; p->processlink=NULL; if(p->flag==1){ printf("您要产生的进程是生产者,它是第%d个进程。请您输入您要该进程产生的字符!\n",processnum); scanf("%c",&ch); getchar(); p->product=ch; productnum++; printf("您要该进程产生的字符是%c \n",p->product); } else { printf("您要产生的进程是消费者,它是第%d个进程。\n",p->numlabel); } linkqueue(p,&readytail); } return true; } bool waitempty() { if(empty<=0) { printf("进程%d:缓冲区存数,缓冲区满,该进程进入生产者等待队列\n",exe->numlabel); linkqueue(exe,&producertail); return false; } else{ empty--; return true; } } void signalempty() { PCB* p; if(hasElement(producerhead)){ p=getq(producerhead,&producertail); linkqueue(p,&readytail); printf("等待中的生产者进程进入就绪队列,它的进程号是%d\n",p->numlabel); } empty++; } bool waitfull() { if(full<=0) { printf("进程%d:缓冲区取数,缓冲区空,该进程进入消费者等待队列\n",exe->numlabel); linkqueue(exe,&consumertail); return false; } else{ full--; return true;} } void signalfull() { PCB* p; if(hasElement(consumerhead)){ p=getq(consumerhead,&consumertail); linkqueue(p,&readytail); printf("等待中的消费者进程进入就绪队列,它的进程号是%d\n",p->numlabel); } full++; } void producerrun() { if(!waitempty()) return; printf("进程%d开始向缓冲区存数%c\n",exe->numlabel,exe->product); buffer[bufferpoint]=exe->product; bufferpoint++; printf("进程%d向缓冲区存数操作结束\n",exe->numlabel); signalfull(); linklist(exe,over); } void comsuerrun() { if(!waitfull()) return; printf("进程%d开始向缓冲区取数\n",exe->numlabel); exe->product=buffer[bufferpoint-1]; bufferpoint--; printf("进程%d向缓冲区取数操作结束,取数是%c\n",exe->numlabel,exe->product); signalempty(); linklist(exe,over); } void display(PCB* p) { p=p->processlink; while(p!=NULL){ printf("进程%d,它是一个",p->numlabel); p->flag==1? printf("生产者\n"):printf("消费者\n"); p=p->processlink; } } bool hasElement(PCB* pro) { if(pro->processlink==NULL) return false; else return true; } void main() { char terminate; bool element; printf("你想开始程序吗?(y/n)"); scanf("%c",&terminate); getchar(); //Queue initialize; readyhead=(PCB*)malloc(sizeof(PCB)); if(readyhead==NULL) return; readytail=readyhead; readyhead->flag=3; readyhead->numlabel=processnum; readyhead->state='w'; readyhead->processlink=NULL; consumerhead=(PCB*)malloc(sizeof(PCB)); if(consumerhead==NULL) return; consumertail=consumerhead; consumerhead->processlink=NULL; consumerhead->flag=4; consumerhead->numlabel=processnum; consumerhead->state='w'; consumerhead->processlink=NULL; producerhead=(PCB*)malloc(sizeof(PCB)); if(producerhead==NULL) return; producertail=producerhead; producerhead->processlink=NULL; producerhead->flag=5; producerhead->numlabel=processnum; producerhead->state='w'; producerhead->processlink=NULL; over=(PCB*)malloc(sizeof(PCB)); if(over==NULL) return; over->processlink=NULL; while(terminate=='y') { if(!processproc()) break; element=hasElement(readyhead); while(element){ exe=getq(readyhead,&readytail); printf("进程%d申请运行,它是一个",exe->numlabel); exe->flag==1? printf("生产者\n"):printf("消费者\n"); if(exe->flag==1) producerrun(); else comsuerrun(); element=hasElement(readyhead); } printf("就绪队列没有进程\n"); if(hasElement(consumerhead)) { printf("消费者等待队列中有进程:\n"); display(consumerhead); } else { printf("消费者等待队列中没有进程\n"); } if(hasElement(producerhead)) { printf("生产者等待队列中有进程:\n"); display(producerhead); } else { printf("生产者等待队列中没有进程\n"); } printf("你想继续吗?(press 'y' for on)"); scanf("%c",&terminate); getchar(); } printf("\n\n 进程模拟完成.\n"); //Free the room; freelink(over); over=NULL; freelink(readyhead); readyhead=NULL; readytail=NULL; freelink(consumerhead); consumerhead=NULL; consumertail=NULL; freelink(producerhead); producerhead=NULL; producertail=NULL; getchar(); }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值