【原创】《Linux高级程序设计》杨宗德著 - 进程管理与程序开发 - 信号量通信机制

时间:2022-07-11 10:24:34


【原创】《Linux高级程序设计》杨宗德著 - 进程管理与程序开发 - 信号量通信机制


信号量通信机制概念图

【原创】《Linux高级程序设计》杨宗德著 - 进程管理与程序开发 - 信号量通信机制

通常所说的创建一个信号量实际上是创建了一个信号量集合,在这个信号量集合中,可能有多个信号量,整个信号量集合由以下部分组成。

信号量集合数据结构

【原创】《Linux高级程序设计》杨宗德著 - 进程管理与程序开发 - 信号量通信机制

每一个信号量结构

【原创】《Linux高级程序设计》杨宗德著 - 进程管理与程序开发 - 信号量通信机制

Linux信号量管理操作

1. 创建信号量集合

【原创】《Linux高级程序设计》杨宗德著 - 进程管理与程序开发 - 信号量通信机制

2. 控制信号量集合、信号量

【原创】《Linux高级程序设计》杨宗德著 - 进程管理与程序开发 - 信号量通信机制

【原创】《Linux高级程序设计》杨宗德著 - 进程管理与程序开发 - 信号量通信机制

读取设置信号量集合的示例:

 #include <stdio.h>
 #include <sys/types.h>
 #include <sys/sem.h>
 #include <errno.h>
 #define MAX_SEMAPHORES  5
 int main(int argc,char *argv[])
 {
	int i, ret, semid;
    	unsigned short sem_array[MAX_SEMAPHORES];
    	unsigned short sem_read_array[MAX_SEMAPHORES];
  
    	union semun 
	{
      		int val;
      		struct semid_ds *buf;
      		unsigned short *array;
    	} arg;
  
    	semid = semget( IPC_PRIVATE, MAX_SEMAPHORES,IPC_CREAT | 0666 );
  
    	if (semid != -1) 
	{ 
      		/* Initialize the sem_array */
      		for ( i = 0 ; i < MAX_SEMAPHORES ; i++ ) 
	  	{
        		sem_array[i] = (unsigned short)(i+1);
      		}
	      	/* Update the arg union with the sem_array address */
	      	arg.array = sem_array;
	      	/* Set the values of the semaphore-array */ 
	      	ret = semctl( semid, 0, SETALL, arg);
	  	if (ret == -1) printf("SETALL failed (%d)\n", errno); 
	   		/* Update the arg union with another array for read */
	   		arg.array = sem_read_array;
	   		/* Read the values of the semaphore array */
		ret = semctl( semid, 0, GETALL, arg );
		if (ret == -1) 
			printf("GETALL failed (%d)\n", errno); 
	   	/* print the sem_read_array */
	   	for ( i = 0 ; i < MAX_SEMAPHORES ; i++ ) 
	   	{
	     		printf("Semaphore %d, value %d\n", i, sem_read_array[i] );

	   	}
	   	/* Use GETVAL in a similar manner */
	   	for ( i = 0 ; i < MAX_SEMAPHORES ; i++ )
	   	{
	     		ret = semctl( semid, i, GETVAL );
	     		printf("Semaphore %d, value %d\n", i, ret );
	   	}
	   	/* Delete the semaphore */
	   	ret = semctl( semid, 0, IPC_RMID );
 	}
	else 
 	  	printf("Could not allocate semaphore (%d)\n", errno);
 	return 0;
}
运行结果:

$ ./sem_get_value 
Semaphore 0, value 1
Semaphore 1, value 2
Semaphore 2, value 3
Semaphore 3, value 4
Semaphore 4, value 5
Semaphore 0, value 1
Semaphore 1, value 2
Semaphore 2, value 3
Semaphore 3, value 4
Semaphore 4, value 5

3. 信号量操作

【原创】《Linux高级程序设计》杨宗德著 - 进程管理与程序开发 - 信号量通信机制

sem_flg的可选值有:IPC_NOWAIT和 SEM_UNDO。

SEM_UNDO的应用示例

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>

static int semaphore_p(void);
static int semaphore_v(void);
static int set_semvalue(void);
static int get_semvalue(void);

union semun 
{
        int val;                   			/* value for SETVAL */
        struct semid_ds *buf;       		/* buffer for IPC_STAT, IPC_SET */
        unsigned short int *array;  		/* array for GETALL, SETALL */
        struct seminfo *__buf;      		/* buffer for IPC_INFO */
};

int sem_id;
int main(int argc,char *argv)
{
	pid_t pid;
	int i;
	int value;
    	key_t key;
	int status;
	if((pid=fork())==-1)
	{
		perror("fork");
		exit(EXIT_FAILURE);
	}
	else if(pid==0)
	{	
		if((sem_id=semget((key_t)123456,1,IPC_CREAT|0770))==-1)
		{
			perror("semget");
			exit(EXIT_FAILURE);
		}
		if (!set_semvalue()) 
		{
			fprintf(stderr, "Failed to initialize semaphore\n");
			exit(EXIT_FAILURE);
		}
		
			value=get_semvalue();
			printf("this is child,the current value is %d\n",value);
			if(!semaphore_v())
			{
				fprintf(stderr, "Failed to v operator\n");
				exit(EXIT_FAILURE);
			}
			value=get_semvalue();
			printf("the child %d V operator,value=%d\n",i,value);
		
		printf("child exit success\n");
		exit(EXIT_SUCCESS);	
	}
	else	//parent
	{
		sleep(3);
		if((sem_id=semget((key_t)123456,1,IPC_CREAT|0770))==-1)
		{
			perror("semget");
			exit(EXIT_FAILURE);
		}

			value=get_semvalue();
			printf("this is parent ,the current value is %d\n",value);
		printf("the parent will remove the sem\n");
		if(semctl(sem_id,0, IPC_RMID,(struct msquid_ds*)0)==-1)
		{
			perror("semctl");
			exit(EXIT_FAILURE);
		}
		return 0;
	}
}

static int set_semvalue(void)
{
    	union semun sem_union;
	int value;
    	sem_union.val = 5;
    	if (semctl(sem_id, 0, SETVAL, sem_union) == -1) 
		return(0);
    	printf("set value success,");
	printf("init value is %d\n",get_semvalue());
	return(1);
}

static int get_semvalue(void)
{
	int res;
	if((res=semctl(sem_id, 0, GETVAL)) == -1) 
    	{
    		perror("semctl");
		exit(EXIT_FAILURE);
    	}
	return res;
}
    
static int semaphore_v(void)
{
    	struct sembuf sem_b;
    
    	sem_b.sem_num = 0;
    	sem_b.sem_op = 1; /* V() */
    	//sem_b.sem_flg = SEM_UNDO;
    	sem_b.sem_flg=0;
	if (semop(sem_id, &sem_b, 1) == -1) 
    	{
    		perror("semop");
		return(0);
    	}
    return(1);
}

使用SEM_UNDO参数时的运行结果:

$ ./semop_undo_test 
set value success,init value is 5
this is child,the current value is 5
the child 0 V operator,value=6
child exit success
this is parent ,the current value is 5
the parent will remove the sem
不使用SEM_UNDO参数时的运行结果:
$ ./semop_undo_test 
set value success,init value is 5
this is child,the current value is 5
the child 0 V operator,value=6
child exit success
this is parent ,the current value is 6
the parent will remove the sem

使用信号量实现生产消费问题

生产消费问题是一个经典的数学问题,要求生产者-消费者在固定的仓库空间条件下,生产者每生产一个产品将占用一个仓库空间,生产者生产的产品库存不能越过仓库的存储量,消费者每消费一个产品将增加一个仓库空间,消费者在仓库产品为0时不能再消费。

本例中采用信号量来解决这个问题,为了便于理解,本例中使用了两个信号量,一个用来管理消费者(以下为sem_produce),一个用来管理生产者(以下为sem_custom),即sem_produce表示当前仓库可用空间的数量,sem_custom用来表示当前仓库中产品的数量。
对于生产者来说,其需要申请的资源为仓库中的剩余空间,因此,生产者在生产一个产品前,申请sem_produce信号量,当此信号量的值大于0,即有可用空间,将生产产品,并将sem_produce的值减去1(因为占用了一个空间);同时,当其生产一个产品后,当前仓库的产品数量增加1,需要将sem_custom信号量自动加1。
对于消费者来说,其需要申请的资源为仓库中的产品,因此,消费者在消费一个产品前,将申请sem_custom信号量,当此信号量的值大于0时,即有可用产品,将消费一个产品,并将sem_custom信号量的值减去(因为消费了一个产品),同时,当消费一个产品,当前仓库的剩余空间增加1,需要将sem_produce信号量自动加1。

生产者源代码:

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

int sem_id;

void init()
{
	key_t key;
	int ret;
	unsigned short sem_array[2];
	union semun 
	{
		int val;
		struct semid_ds *buf;
		unsigned short *array;
	}arg;

	key=ftok(".",'s');

	sem_id=semget(key,2,IPC_CREAT|0644);
	sem_array[0]=0;				//identify the productor
	sem_array[1]=100;			//identify the space
	//printf("set the productor init value is 0\nset the space init value is 100\n");
	arg.array = sem_array;
	ret = semctl(sem_id, 0, SETALL, arg);	
	if (ret == -1) 
		printf("SETALL failed (%d)\n", errno); 
	//printf("\nread the number\n");
	printf("productor init is %d\n",semctl(sem_id,0,GETVAL));
	printf("space init is %d\n\n",semctl(sem_id,1,GETVAL));
}

void del()
{
	semctl(sem_id,IPC_RMID,0);
}

int main(int argc,char *argv[])
{
	struct sembuf sops[2];	

	sops[0].sem_num = 0;
	sops[0].sem_op = 1;
	sops[0].sem_flg = 0;

	sops[1].sem_num = 1;
	sops[1].sem_op = -1;
	sops[1].sem_flg = 0;

	init();
	printf("this is productor\n");
	while(1)
	{
		printf("\n\nbefore produce:\n");
		printf("productor number is %d\n",semctl(sem_id,0,GETVAL));
		printf("space number is %d\n",semctl(sem_id,1,GETVAL));
		semop(sem_id,(struct sembuf *)&sops[1],1);		//get the space to instore the productor
		printf("now producing......\n");
		semop(sem_id,(struct sembuf *)&sops[0],1);		//now tell the customer can bu cusume
		printf("\nafter produce\n");
		printf("spaces number is %d\n",semctl(sem_id,1,GETVAL));
		printf("productor number is %d\n",semctl(sem_id,0,GETVAL));
		sleep(4);
	}
	del();
}
消费者源代码:

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <errno.h>
int sem_id;

void init()
{
	key_t key;

	key=ftok(".",'s');
	sem_id=semget(key,2,IPC_CREAT|0644);
	//printf("sem id is %d\n",sem_id);
}

int main(int argc,char *argv[])
{
	init();
	struct sembuf sops[2];

	sops[0].sem_num = 0;
	sops[0].sem_op = -1;
	sops[0].sem_flg = 0;

	sops[1].sem_num = 1;
	sops[1].sem_op = 1;
	sops[1].sem_flg = 0;

	init();
	printf("this is customer\n");
	while(1)
	{
		printf("\n\nbefore consume:\n");
		printf("productor is %d\n",semctl(sem_id,0,GETVAL));
		printf("space  is %d\n",semctl(sem_id,1,GETVAL));	

		semop(sem_id,(struct sembuf *)&sops[0],1);		//get the productor to cusume
		printf("now consuming......\n");
		semop(sem_id,(struct sembuf *)&sops[1],1);		//now tell the productor can bu produce
		
		printf("\nafter consume\n");
		printf("products number is %d\n",semctl(sem_id,0,GETVAL));
		printf("space number is %d\n",semctl(sem_id,1,GETVAL));
		sleep(3);
	}
}
要先运行生产者,在运行消费者。

生产者终端:

$ ./sem_productor 
productor init is 0
space init is 100

this is productor

before produce:
productor number is 0
space number is 100
now producing......

after produce
spaces number is 99
productor number is 1


before produce:
productor number is 1
space number is 99
now producing......

after produce
spaces number is 98
productor number is 2
消费者终端:

$ ./sem_customer 
this is customer

before consume:
productor is 2
space  is 98
now consuming......

after consume
products number is 1
space number is 99


before consume:
productor is 2
space  is 98
now consuming......

after consume
products number is 1
space number is 99


before consume:
productor is 1
space  is 99
now consuming......

after consume
products number is 0
space number is 100

原文链接: