一、信号灯简介Linux支持System V的信号灯(semaphore),是一种进程间通信的方式,只不过它和管道、FIFO或者共享内存不一样,信号灯主要用于同步或者互斥对共享资源的访问,它的发明来源于火车运行系统中的"信号灯",利用信号灯可以实现"PV"操作这种进程间同步进制。P操作时获得资源,将信号灯的值减1,如果结果不为负则执行完毕,进程获得资源,否则进程睡眠以等待的进程释放;V操作则是释放资源,给信号灯的值加1, 唤醒一个因执行P操作而等待的进程。二、信号灯的两种类型A、二值信号灯最简单的信号灯形式,信号灯的值只能取0或1,类似互斥锁。注意:虽然二值信号灯能够实现互斥锁的功能,但两者的关注内容不同。信号灯强调共享资源,只要共享资源可用,其他进程同样可以修改信号灯的值;互斥锁更强调进程,占用资源的进程使用完资源后,必须由进程本身来解锁。B.计数信号灯信号灯的值可以取任意非负值(当然受内核本省的约束),用来统计资源,其值就代表可用资源的个数。注意:通常所说的系统V信号灯指的是计数信号灯集三、相关APIA.创建一个信号灯集功能:创建一个信号灯集并返回这个信号灯集的ID 或直接返回一个已经存在的信号灯集的ID参数说明:key:如果key值为IPC_PRIVATE或key为0并且semflg设置了IPC_CREAT,此时调用此函数总是创建一个新的信号灯集(我们在共享内存的时候验证过,还记的吗?其实system v 的ipc对象的创建机制都很类似。nsems:指定这个信号灯集中信号灯的个数(每个信号灯代表了某一类资源)。semflg:可以指定为IPC_CREAT | 0666,其含义为,不存在则创建,访问权限为0666。我们也可以通过IPC _CREAT | IPC_EXCL一起使用的时候确定要创建的信号灯集是否存在,如果存在此时这个函数放回-1 。B.控制信号灯集参数说明:semid : 信号灯集IDsemnum:要修改的信号灯编号(创建信号灯集时,信号灯的编号从0开始)cmd:IPC_STAT  获取信号灯信息,信息由arg.buf(即第四个参数)返回GETVAL  : 获取semnum信号灯的值SETVAL  : 设置semnum信号灯的值IPC_RMID : 从系统中删除semnum所代表的信号灯  注意:semctl是可变参数,cmd为GETVAL或SETVAL时,需要传递第四个参数,其参数类型为 union semun。这个结构体的类型必须在应用程序中定义,定义如下:设置信号灯集中第一个信号灯的数值为1(即某一类资源的个数)a.必须在应用程序中定义如下类型union semun{  int   val;  struct  semid_ds  *buf;  unsigned  short  *array;  struct  seminfo  *  __buf;};b.定义一个union semun变量,并赋值union semun mysemun;mysemun.val = 1;c.调用semctl函数//第一个信号灯的编号为0if ( ( semctl (  semid  ,  0  ,  SETVAL,mysemun)) {    perror("Fail to semctl");    exit(EXIT_FAILURE);}删除一个信号灯if(  semctl  (  semid  ,  0  ,  IPC_RMID  ,  0 ) {    perror("Fail to semctl  IPC_RMID");    exit(EXIT_FAILURE);}C.操作信号灯功能:semop系统调用可以实现对由semid标志的信号等集中的某一个指定信号灯的一系列操作参数说明:semid信号灯集的标识ID。sops :指向结构体sembuf的指针,设置信号灯集中某一个信号灯的工作方式。sem_num对应信号灯集中的信号灯,0代表第一个信号灯sem_op值决定了对sem_num的三种不同操作:a.sem_op = 0,调用者阻塞等待,直到信号灯的值等于0时返回。可以用来测试共享资源是否已用完。b.sem_op = 1,释放资源,V操作c.sem_op = -1,分配资源,P操作sem_flg可取0,IPC_NOWAIT以及SEM_UNDO三个标志a.   0  代表阻塞调用(资源不满足阻塞)b.   IPC_NOWAIT  代表非阻塞调用c.   如果设置了SEM_UNDO标志,那么在进程结束时,相应的操作将被取消,这是一个比较重要的一个标志。案例:封装一个P操作和一个V操作//p操作int my_sem_wait(int semid,int sem_num){    struct sembuf  op;         op.sem_num = sem_num;    op.sem_op = -1;    op.sem_flg = 0;        if(semop(sem_id,&op,1)     {perror("fail to semop");exit(-1);    }    return 0;}//V操作int my_sem_wait(int semid,int sem_num){    struct sembuf  op;         op.sem_num = sem_num;    op.sem_op = 1;    op.sem_flg = 0;        if(semop(sem_id,&op,1)     {perror("fail to semop");exit(-1);    }    return 0;}案例探究(用信号灯集实现共享内存间的同步):A .读共享内存#include stdio.h>#include stdlib.h>#include string.h>#include errno.h>#include unistd.h>#include sys/ipc.h>#include sys/shm.h>#include sys/types.h>#include sys/sem.h>#define READ 0#define WRITE 1#define N 2union semun{    int val;    struct semid_ds *buf;    unsigned short *array;    struct seminfo *__buf;};//初始化信号灯集中的信号灯的值int my_sem_init(int semid){    int i = 0;    union semun mysemun;    for(i = 0;i N;i ++)    {        mysemun.val = i;        if(semctl(semid,i,SETVAL,mysemun) 0)        {            perror("Fail to semctl");            return -1;        }    }    return 0;}//信号灯集中的信号灯释放资源int my_sem_post(int semid,int sem_num){    struct sembuf op;    op.sem_num = sem_num;    op.sem_op = 1;    op.sem_flg = 0;    if(semop(semid,&op,1) 0)    {        perror("Fail to semop");        return -1;    }    return 0;}//信号灯集中的信号灯申请资源 int my_sem_wait(int semid,int sem_num){    struct sembuf op;    op.sem_num = sem_num;    op.sem_op = -1;    op.sem_flg = 0;    if(semop(semid,&op,1) 0)    {        perror("Fail to semop");        return -1;    }    return 0;}//读共享内存int read_share_memory(int semid,char *addr){    int n;    while(1)    {        my_sem_wait(semid,READ);                printf("Read : %s.\n",addr);        my_sem_post(semid,WRITE);        if(strncmp(addr,"quit",4) == 0)        {            if(shmdt((void *)addr) 0)            {                perror("Fail to semdt");                return -1;            }                        break;        }    }        return 0;}int main(int argc,char *argv[]){    key_t key;    void *shmaddr;    int shmid,semid;        if(argc 2)    {        fprintf(stderr,"usage : %s argv[1].\n",argv[0]);        exit(EXIT_FAILURE);    }        //获取键值    if((key = ftok(argv[1],'a')) 0)    {        perror("Fail to ftok");        exit(EXIT_FAILURE);    }        //创建共享内存    if((shmid = shmget(key,1024,IPC_CREAT | 0666)) 0)    {        perror("Fail to shmget");        exit(EXIT_FAILURE);    }    //映射共享内存到进程地址空间     if((shmaddr = shmat(shmid,NULL,0)) == (void *)-1)    {        perror("Fail to shmat");        exit(EXIT_FAILURE);    }        //创建含有2个的信号灯的信号灯集    if((semid = semget(key,N,IPC_CREAT | 0666)) 0)    {        perror("Fail to shmget");        exit(EXIT_FAILURE);    }    //初始化信号灯集中的信号灯    my_sem_init(semid);        read_share_memory(semid,(char *)shmaddr);    exit(EXIT_SUCCESS);}写共享内存#include stdio.h>#include stdlib.h>#include string.h>#include errno.h>#include unistd.h>#include sys/ipc.h>#include sys/shm.h>#include sys/types.h>#include sys/sem.h>#define READ 0#define WRITE 1#define N 2union semun{    int val;    struct semid_ds *buf;    unsigned short *array;    struct seminfo *__buf;}mysemun;//初始化信号灯集中的信号灯的值int my_sem_init(int semid){    int i = 0;    for(i = 0;i N;i ++)    {        mysemun.val = i;        if(semctl(semid,i,SETVAL,mysemun) 0)        {            perror("Fail to semctl");            return -1;        }    }    return 0;}//信号灯集中的信号灯释放资源int my_sem_post(int semid,int sem_num){    struct sembuf op;    op.sem_num = sem_num;    op.sem_op = 1;    op.sem_flg = 0;    if(semop(semid,&op,1) 0)    {        perror("Fail to semop");        return -1;    }    return 0;}//信号灯集中的信号灯申请资源 int my_sem_wait(int semid,int sem_num){    struct sembuf op;    op.sem_num = sem_num;    op.sem_op = -1;    op.sem_flg = 0;    if(semop(semid,&op,1) 0)    {        perror("Fail to semop");        return -1;    }    return 0;}//读共享内存int read_share_memory(int semid,char *addr){    int n;    while(1)    {        my_sem_wait(semid,WRITE);        printf(">");        fgets(addr,1024,stdin);        addr[strlen(addr)-1] = '\0';        my_sem_post(semid,READ);        if(strncmp(addr,"quit",4) == 0)        {            if(shmdt((void *)addr) 0)            {                perror("Fail to semdt");                return -1;            }                        break;        }    }        return 0;}int main(int argc,char *argv[]){    key_t key;    void *shmaddr;    int shmid,semid;        if(argc 2)    {        fprintf(stderr,"usage : %s argv[1].\n",argv[0]);        exit(EXIT_FAILURE);    }        //获取键值    if((key = ftok(argv[1],'a')) 0)    {        perror("Fail to ftok");        exit(EXIT_FAILURE);    }        //创建共享内存    if((shmid = shmget(key,1024,IPC_CREAT | 0666)) 0)    {        perror("Fail to shmget");        exit(EXIT_FAILURE);    }    //映射共享内存到进程地址空间     if((shmaddr = shmat(shmid,NULL,0)) == (void *)-1)    {        perror("Fail to shmat");        exit(EXIT_FAILURE);    }        //创建含有2个的信号灯的信号灯集    if((semid = semget(key,N,IPC_CREAT | 0666)) 0)    {        perror("Fail to shmget");        exit(EXIT_FAILURE);    }    //初始化信号灯集中的信号灯    my_sem_init(semid);        read_share_memory(semid,(char *)shmaddr);    if(shmctl(shmid,IPC_RMID,NULL) 0)    {        perror("Fail to shmctl");        return -1;    }    if(semctl(semid,0,IPC_RMID,0) 0)    {        perror("Fail to semctl WRITE");        return -1;    }    exit(EXIT_SUCCESS);}
02-11 11:40