html5 单页网站东莞有限公司seo
信号量
 信号量(semaphore) 与已经介绍过的 IPC 结构不同,它是一个计数器,信号量用于实现进程间的与斥与同步,而不是用于存储进程间通信数据。
 1、特点
 (1)信号量用于进程间同步,若要在进程间传递数据需要结合共享内存
 (2)信号量基于操作系统的 PV 操作,程序对信号量的操作都是原了操作。
 (3)每次对信号量的 PV 操作不仅限于对信号量值加 1 或减 1,而且可以加减任意正整数
 (4)支持信号量组
 2、原型
 最简单的信号量是只能取 0和 1 的变量,这也是信号量最常见的一种形式,叫做二值信号量(Binary Semaphore) 。而可以取多个正整数的信号量被称为通用信号量。
 Linux 下的信号量函数都是在通用的信号量数组上进行操作,而不是在一个单一的二值信号量上进行操作。
头文件:

1 #include <sys/sem.h>
 2 // 创建或获取一个信号量组: 若成功返回信号量集ID,失败返回-1
3 int semget(key_t key, int nsems, int semflg);
4 // 对信号量组进行操作,改变信号量的值: 成功返回0,失败返回-1
5 int semop(int semid, struct sembuf *sops, unsigned nsops);
 6 // 控制信号量的相关信息
 7 int semctl(int semid, int semnum, int cmd, ...);
PV操作(P可看作拿锁,V看作放回锁)
PV操作的含义:PV操作由P操作原语和V操作原语组成(原语是不可中断的过程),对信号量进行操作,具体定义如下:
    P(S):①将信号量S的值减1,即S=S-1;                                                             
            ②如果S>=0,则该进程继续执行;否则该进程置为等待状态,排入等待队列。
     V(S):①将信号量S的值加1,即S=S+1;                                                           
            ②如果S>0,则该进程继续执行;否则释放队列中第一个等待信号量的进程。
  利用信号量和PV操作实现进程互斥的一般模型是:
     进程P1              进程P2           ……          进程Pn
     ……                  ……                           ……
     P(S);              P(S);                         P(S);
     临界区;             临界区;                        临界区;
     V(S);              V(S);                        V(S);
     ……                  ……            ……           ……
 使用PV操作实现进程互斥时应该注意的是:
    (1)每个程序中用户实现互斥的P、V操作必须成对出现,先做P操作,进临界区,后做V操作,出临界区。若有多个分支,要认真检查其成对性。
     (2)P、V操作应分别紧靠临界区的头尾部,临界区的代码应尽可能短,不能有死循环。
     (3)互斥信号量的初值一般为1。
临界资源(临界区)
多道程序系统中存在许多进程,它们共享各和资源,然而有很多资源一次只能供一个进程使用。一次仅允许一个进程使用的资源称为临界资源。许多物理设备都属于临界资源,如输入机、打印机、磁带机等。
示例:fork一个进程不知道父进程还是子进程先运行(都有可能),那么我们可以通过信号量与PV操作来控制让子进程先运行



编译结果:

(代码可复制):
#include <stdio.h>
 #include <sys/types.h>
 #include <sys/ipc.h>
 #include <sys/sem.h>
 //int semget(key_t key, int nsems, int semflg);
 //int semctl(int semid, int semnum, int cmd, ...);
 //int semop(int semid, struct sembuf *sops, unsigned nsops);
union semun {
                int              val;    /* Value for SETVAL */
                struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */
                unsigned short  *array;  /* Array for GETALL, SETALL */
                struct seminfo  *__buf;  /* Buffer for IPC_INFO
                                            (Linux-specific) */
 };
void pGetKey(int id)
 {
     struct sembuf set;
    set.sem_num = 0;
     set.sem_op = -1;
     set.sem_flg=SEM_UNDO;
    semop(id,  &set ,1);
     printf("getkey\n");
 }
void vPutBackKey(int id)
 {
     struct sembuf set;
    set.sem_num = 0;
     set.sem_op = 1;
     set.sem_flg=SEM_UNDO;
    semop(id,  &set ,1);
     printf("put back the key\n");
 }
int main(int argc, char const *argv[])
 {
     key_t key;
     int semid;
    key = ftok(".",2);
                        //信号量集合中有一个信号量  
     semid = semget(key, 1, IPC_CREAT|0666);//获取/创建信号量
    union semun initsem;
     initsem.val = 0;
                   //操作第0个信号量   
     semctl(semid, 0, SETVAL, initsem);//初始化信号量
                   //SETVAL设置信号量的值,设置为inisem
                   
                   
     int pid = fork();
     if(pid > 0){
         //去拿锁
     pGetKey(semid);
         printf("this is father\n");
     vPutBackKey(semid);
         //锁放回去
     semctl(semid,0,IPC_RMID);
     }
     else if(pid == 0){
         printf("this is child\n");
     vPutBackKey(semid);
     }else{
        printf("fork error\n");
     }            
     return 0;
 }
  
