IPC----信号量

来源:互联网 发布:淘宝呼死你哪里有卖 编辑:程序博客网 时间:2024/06/07 07:22

1.为什么引入信号量
Linux 系统采用多道程序设计技术,允许多个进程同时在内核中运行,但同一个系统中的多个进程之间,可能因为进程合作或资源共享,产生制约关系。制约关系分为直接相互制约关系和间接相互制约关系:
(1)直接相互制约关系。 利用管道机制实现进程间通信, 当管道为空时,读进程由于无法从管道中读取数据而进入阻塞;当管道存满时,写进程由于无法向管道中写入数据而进入阻塞, 类似于这种需要进程间协调合作导致的制约关
系,称为直接相互制约关系。
(2) 间接相互制约关系。 若当前系统中只有 1 台打印机,当进程 A 占用打印机时,进程 B 也申请使用打印机,进程 B 就会进入阻塞,等待打印机释放;同样若 B 先获取到打印机, A 进程申请使用打印机后也会进入阻塞。类似于这种因资源共享导致的制约关系,称为间接相互制约关系。
计算机中的多个进程必须互斥地访问系统中的临界资源,用于访问临界资源的代码称为临界区(Critical Section),临界区也属于临界资源,若能保证进程间互斥地进入自己的临界区,就能实现进程对临界资源的互斥访问。
信号量(Semaphore)是专门用于解决进程同步与互斥问题的一种通信机制,它与信号无关,也不同于管道、 FIFO 以及消息队列,一般不用来传输数据,信号量包括一个被称为信号量的表示资源数量的非负整型变量、修改信号量的原子操作 P 和 V,以及该信号量下等待资源的进程队列。
2.信号量本质
信号量的本质是⼀种数据操作锁,它本⾝不具有数据交换的功能,⽽是通过控制其他的通信资源(⽂件,外部设备)来实现进程间通信, 它本⾝只是⼀种外资源的标识。信号量在此过程中负责数据操作的互斥、同步等功能。
3.信号量通信的一般步骤
(1) 创建信号量/信号量集,或获取系统中已有的信号量/信号量集;
(2) 初始化信号量。早期信号量通常被初始为 1,但有些进程一次需要多个同类的临界资源,或多个不同类且不唯一的临界资源,因此可能需要初始化的不是信号量,而是一个信号量集;
(3)信号量的 P、 V 操作,根据进程请求,修改信号量的数量。执行 P 操作会使信号量-1,执行 V 操作会使信号量+1;
(4)从系统中删除不需要的信号量。
4相关函数

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

semget()函数的功能为创建一个新的信号集
若该函数调用成功则返回信号量的标识符,否则返回-1
参数key表示信号量的键值,通常为整数,也可以用ftok(const char* pathname,int proj_id)函数来生成;
参数nsems表示创建的信号量数目;参数senflg为标志位,通常为IPC_CREAT和IPC_EXCL

2.int semctl(int semid,int semnum,int cmd...);

若该函数调用成功则根据参数 cmd 的取值返回相应信息,通常为一个非负整数;否则返回-1参数 semid 表示信号量标识符,通常为 semget()的返回值;参数 semnum 表示信号量在信号量集中的编号, 该参数在使用信号量集时才会使用,通常设置为 0,表示取第一个信号;参数 cmd 表示对信号量进行的操作;最后一个参数是一个可选参数,依赖于参数 cmd,使用该参数时,用户必须在程序中自定义一个如下所示的共用体

union semun{int val;                //cmd 为 SETVAL 时,用于指定信号量值struct semid_ds *buf;   //cmd 为 IPC_STAT 时或 IPC_SET 时生效unsigned short *array;  //cmd 为 GETALL 或 SETALL 时生效struct seminfo *_buf;   //cmd 为 IPC_INFO 时生效};
3.int semop(int semid,struct sembuf *sops,unsigned nsops);

semop()函数功能:改变信号量的值
若该函数调用成功返回 0,否则返回-1,并设置 errno。
semop()函数的参数 semid 同样为 semget()返回的信号量标识符; 参数 nsops表示参数 sops 所指数组中元素的个数。参数 sops 为一个 struct sembuf 类型的数组指针, 该数组中的每个元素设置了要对信号量集中的哪个信号做哪种操作, struct sembuf 结构体定义如

struct sembuf{short sem_num;  //信号量在信号量集中的编号short sem_op;   //信号量操作short sem_flag; //标志位};

当结构体成员 sem_op 设置为-1 时,表示 P 操作;设置为+1 时,表示 V 操作。结构体成员 sem_flg 通常设置为 SEM_UNDO,若进程退出前没有删除信号量,信号量将会由系统自动释放。
5.代码展示
//com.h文件

#include<stdio.h>#include<stdlib.h>#include<sys/sem.h>//自定义共用体union semun{    int val;                     //cmd为SETVAL时,用于指定信号量值    struct semid_ds *buf;    unsigned short *array;    struct seminfo* _buf;};static int sem_id;               //设置全局的信号量idstatic int set_semvalue();static int sem_p();static int sem_v();static void del_sem();//设置信号量值static int set_semvalue(){    union semun sem_union;    sem_union.val=1;    if(semctl(sem_id,0,SETVAL,sem_union)==-1){        exit(1);    }    return 1;}//p操作,获取信号量static int sem_p(){    struct sembuf semoper;    semoper.sem_num=0;    semoper.sem_op=-1;    semoper.sem_flg=SEM_UNDO;    if(semop(sem_id,&semoper,1)==-1){        perror("sem_p");        exit(1);    }    return 1;}//v操作,释放信号量static int sem_v(){    struct sembuf sem_b;    sem_b.sem_num=0;    sem_b.sem_op=1;    sem_b.sem_flg=SEM_UNDO;    if(semop(sem_id,&sem_b,1)==-1){        perror("sem_v");        exit(1);    }    return 1;}//删除信号量static void del_sem(){    union semun sem_union;    if(semctl(sem_id,0,IPC_RMID,sem_union)==-1){        perror("del_sem");        exit(1);    }}
#include<stdio.h>#include"com.h"int main(){    int i;    pid_t pid;    char ch='A';    sem_id=semget((key_t)4050,1,0666|IPC_CREAT|IPC_EXCL);  //创建信号量        if(sem_id==-1)        {            perror("semget");            exit(1);        }    //设置信号量值    if(set_semvalue()==-1){        perror("set_semvalue");        exit(1);    }    pid=fork();  //创建子进程    if(pid==-1){        perror("fork");        del_sem();        exit(1);    }    else if(pid==0){        ch='A';    }    else{        ch='B';    }    while(1){        sem_p();  //p操作        printf("%c",ch);        fflush(stdout);        printf("%c",ch);        fflush(stdout);        sleep(1);        sem_v();  //V操作    }    if(pid>0){        wait(NULL);        del_sem();    }    return 0;}

这里写图片描述
我们可以通过指令:ipcs -s来查看创建的信号量
这里写图片描述
因为信号量的生命周期是随内核的,程序结束后不能删除已创建的信号量,当再次运行可执行文件时,会出现下面的错误
这里写图片描述
我们可以通过这条指令来删除已创建的信号量ipcrm -s  semid
这里写图片描述
6.struct sembuf结构中,sem_flg设置为SEM_UNDO的作用
(1)sem_flg公认的标志是 IPC_NOWAIT 和 SEM_UNDO。如果操作指定SEM_UNDO,它将会自动撤消该进程终止时。在标准操作程序中的操作是在数组的顺序执行、原子的,那就是,该操作要么作为一个完整的单元,要么不。如果不是所有操作都可以立即执行的系统调用的行为取决于在个人sem_flg领域的IPC_NOWAIT标志的存在。
(2)sembuf结构的sem_flg成员为SEM_UNDO时,它将使操作系统跟踪当前进程对这个信号量的修改情况,如果这个进程在没有释放该信号量的情况下终止,操作系统将自动释放该进程持有的信号量.
(3)只有将 sem_flg 指定为 SEM_UNDO 标志后,semadj (所指定信号量针对调用进程的调整值)才会新.此外,如果此操作指定SEM_UNDO,系统更新过程中会撤消此信号灯的计数(semadj)。此操作可以随时进行,它永远不会强制等待的过程。调用进程必须有改变信号量集的权限。

原创粉丝点击