您的位置:首页 > 运维架构 > Linux

【原创】《Linux高级程序设计》杨宗德著 - 进程管理与程序开发 - 信号量通信机制 分类: Linux --- 应用程序设计 2014-11-13 11:08 70人阅读 评论(0) 收藏

2014-11-13 11:08 1531 查看
【原创】《Linux高级程序设计》杨宗德著 - 进程管理与程序开发 - 信号量通信机制

信号量通信机制概念图



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

信号量集合数据结构



每一个信号量结构



Linux信号量管理操作

1. 创建信号量集合



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





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

#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. 信号量操作



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


原文链接:

/article/1574930.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐