信号量
今天去参加北京市的植树志愿者活动啦!早上起来的挺早的,6:10就被杰子给叫起来啦,带着对春天的向往,我们坐着不花钱的大巴去做为市领导服务去啦!发了一个小红帽还有一个红色的制服。
唉。。。说好了,早上9点多学学校的,结果下午四点到的学校,专业课给耽误了。不过今天的收获还是蛮多的,还亲自为了后代埋了两颗小树苗呢,哈哈。。。
现在我们就谈谈信号量吧,再没有讲这个之前,我就预习了一下信号量,所以待明天老师再一讲,我就能更理解它的真正含义和作用啦!
信号量(s):是一种特殊的变量,它的仅仅取正值,并且允许对信号量的操作只有两种:1、等待(wait)
2、发送信号(signal)
wait操作:如果S大于零,wait就在一个原子操作中测试S,并对其进行减量运算;
如果S等于零,wait就在一个原子操作中测试S,并阻塞调用程序。
signal操作:如果有线程在信号量上阻塞,S就等于零,signal就会解除对某个等待线程的阻塞;
如果没有线程在信号量上阻塞,signal就对S进行增量运算。
信号量分为:内核信号量和用户态进程使用信号量
POSIX信号量又分为:有名信号量和无名信号量、
他们之间的区别近似与有名管道与无名管道。
有名信号量,其值保存在文件中,所以它可以用于线程也可以用于进程间的同步。无名信号量,其值保存在内存中。
下面介绍一下,与信号量有关的函数吧,主要应用到的函数有:
1. Semget()
2. Semop()
3. Semctl()
其中,semget函数是为了获取信号量的标识码的,然后其他的函数只能通过对semget的返回值对信号量进行处理。
Semget()函数:
#include<sys/sem.h>
Int semget(key_t _key,int_nsems,int _semflg);
函数的功能:是创建一个信号量或者是为了获得一个新的键值
返回值:成功的返回信号量的标识码,如果函数调用失败返回-1
第一个参数_key,为整型值,是允许其他的进程访问信号量的一个整型的变量。所以的信号都是通过间接的方式获得的,运行的程序会提供一个信号的键值,系统为每一个键值赋予一个信号量,其他的处理函数只能通过对semget函数的返回值进行处理。
第二个参数_nsems,参数表示信号量的编号,几乎总是取值为1.
第三个参数_semflg,信号量的权限。
系统调用semget()的第一个参数是关键字值(一般是由系统调用ftok()返回的)。系统内核将此值和系统中存在的其他的信号量集的关键字值进行比较。打开和存取操作与参数semflg中的内容相关。IPC_CREAT如果信号量集在系统内核中不存在,则创建信号量集。IPC_EXCL当和 IPC_CREAT一同使用时,如果信号量集已经存在,则调用失败。如果单独使用IPC_CREAT,则semget()要么返回新创建的信号量集的标识符,要么返回系统中已经存在的同样的关键字值的信号量的标识符。如果IPC_EXCL和IPC_CREAT一同使用,则要么返回新创建的信号量集的标识符,要么返回-1。IPC_EXCL单独使用没有意义。参数nsems指出了一个新的信号量集中应该创建的信号量的个数。
下面是一个打开和创建信号量的程序:
int mykey=5555;
int semid;
semid=semget(mykey,numsems,IPC_CREAT|0660);
if(semid==-1)
{
perror("semget :");
exit(1);
}
需要说明的是,返回一个信号量的ID号。其他通过对信号量的ID号进行操作。
semop()函数使用:
函数功能:用户改变信号量的值.
返回值:函数调用成功返回0,失败返回-1
参数semid:系统分配给该信号量的一个ID号,通过semget函数的返回值来获得,也称为信号的标识码
参数sops:一个指向信号量结构体数组的指针,信号量的结构体至少有3个成员。
其结构如下:
struct sembuf
{
unsigned short sem_num;操作信号在信号集中的编号,第一个信号的编号为0,第二个为1,一次后推
short sem_op;信号资源,如果有就分配,如果为负值就等待(wait),如果正值就分配信号资源
short sem_flg;信号操作标志,有两种状态,一个是SEM_UNDO,另一个是SEM_NOWAIT
}
SEM_NOWAIT//对信号的操作不能满足是,semop()函数就会阻塞,并立即返回,同时设置错误信息。
SEM_UNDO//程序结束时(无论是否正常结束),保证信号值会被重新设为semop()调用前的值。这样做
的目的在于避免在异常情况下结束时未将锁定资源解锁,造成该资源永远锁定。
下面写一个小例子声明一个结构体:
struct sembuf sem1_opt_wakeup[1]={0,1,SEM_UNDO};
struct sembuf sem1_opt_wait[1]={1,-1,SEM_UNDO};
semctl()函数:
功能:控制信号量的信息
返回值:成功返回0,失败返回-1
第一个参数semid:信号量标识码,还是通过semget来获得的,是semget函数的一个返回值
第二个参数semnum:信号量的编号,当用到信号量数组的时候,这个编号就会起到作用
第三个参数cmd:为允许的命令,其范围在:
第四个参数union semun:是一个共同体
结构如下:
union semun{
int val;
struct semid_ds *buf;
unsigned short *array;
}
下面看一个信号量的使用图解:
下面看一个小案例吧,来说明一下信号量的使用,这里我们用到了两个进程,也就是要写两个函数,也用到了共享内存的知识。
进程1:/*
* filename:shm
*
*/
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/stat.h>
#include<sys/shm.h>
#include<sys/sem.h>
#include<fcntl.h>
#include<stdlib.h>
#include<time.h>
#include<stdio.h>
#include<string.h>
#define SEM_KEY1 5555
union semun
{
int setval;
struct semid_ds *buf;
unsigned short *array;
};
int main()
{
int shmid,semid;
int *addr;
int h=0,w=0;
int ret;
union semun sem_args;
unsigned short array[2]={1,1};
sem_args.array=array;
shmid=shmget(ftok("/etc/passwd",1001),getpagesize(),IPC_CREAT|IPC_EXCL|S_IRUSR| S_IWUSR| S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
if(shmid==-1)
{
perror("shmget error:");
exit(EXIT_FAILURE);
}
semid=semget(SEM_KEY1,2,IPC_CREAT|0600);
if(semid==-1)
{
perror("semget error:");
exit(EXIT_FAILURE);
}
ret=semctl(semid,1,SETALL,sem_args);
if(ret==-1)
{
perror("shmctl error:");
exit(EXIT_FAILURE);
}
struct sembuf sem1_opt_wakeup[1]={0,1,SEM_UNDO};
struct sembuf sem2_opt_wait[1]={1,-1,SEM_UNDO};
while(1)
{
int i=0;
semop(semid,sem2_opt_wait,1);
addr=shmat(shmid,0,0);
printf("please input your infornation:\n");
scanf("%d%d",addr,addr+1);
shmdt(addr);
semop(semid,sem1_opt_wakeup,1);
printf("退出清输入1\n");
scanf("%d",&i);
if(i==1)
{
shmctl(shmid,IPC_RMID,NULL);
semctl(semid,IPC_RMID,NULL);
exit(1);
}
}
return 0;
}
进程2:
#include<sys/types.h>
#include<sys/shm.h>
#include<sys/sem.h>
#include<string.h>
#include<stdio.h>
#include<stdlib.h>
#include<sys/stat.h>
#define SEM_KEY 5555
int main()
{
int shmid,semid;
int *addr;
int h,w;
float result,sum;
struct sembuf sem1_opt_wait[1]={0,-1,SEM_UNDO};
struct sembuf sem2_opt_wakeup[1]={1,1,SEM_UNDO};
while(1)
{
shmid=shmget(ftok("/etc/passwd",1001),getpagesize(),S_IRUSR| S_IWUSR| S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
sleep(1);
if(shmid==-1)
{
perror("shmget error:");
exit(EXIT_FAILURE);
}
semid=semget(SEM_KEY,2,0600);
if(semid==-1)
{
perror("semget error:");
exit(EXIT_FAILURE);
}
addr=shmat(shmid,0,0);
w=*(addr+1);
h=*addr;
sum=h*h/10000;
result=w/sum;
semop(semid,sem1_opt_wait,1);
if(result>25)
{
printf("胖拉!\n");
*(addr+2)=0;
}
else if(result<20)
{
printf("瘦啦!\n");
*(addr+2)=0;
}
else
{
if(result>5&&result<20)
{
printf("正常!\n");
*(addr+2)=0;
}
}
shmdt(addr);
semop(semid,sem2_opt_wakeup,1);
sleep(2);
}
}