Linux进程间通信---信号量

  • 信号量是一个计数器,通常在内核中实现,用于多个进程对共享数据对象的同步访问。使用信号量的头文件是#include <sys/sem.h>
  • 信号量的使用规则:
    • 若信号量为正,则进程可使用该资源。
    • 若信号量为0,则进程阻塞等待,并将进程插入等待队列,直到该信号量的值大于0从等待队列中执行进程请求。
    • 加锁操作:如果信号量大于0,则信号量-1;如果信号量为0,则挂起该进程,并将这个进程插入等待队列。
    • 解锁操作:如果等待队列中有进程则唤醒该进程,让它恢复运行;否则,信号量+1。
  • Linux下使用信号量的常用函数:
    • semget(key, num_sems, sem_flags):创建新的信号量或取得已有的信号量,key表示信号量的键值,不相关进程使用同一个key来访问同一个信号量,num_sems表示信号量个数(一般为1),sem_flags表示信号量访问权限,用IPC_CREAT与权限位与可保证信号量不存在时新建一个。函数返回一个int类型的数值,表示信号量的标识符。
    • semop(sem_id, sem_opa, num_sem_ops):改变信号量的值,改变操作在sem_opa中,sem_opa是sumbuf结构体对象,使用方法如下:
      struct sembuf
      {  
         short sem_num;   //除非使用一组信号量,否则它为0  
         short sem_op;    //信号量在一次操作中需要改变的数据,通常是两个数,一个是-1,即P操作(加锁);一个是+1,即V操作(解锁)
         short sem_flg;   //通常为SEM_UNDO,使操作系统跟踪信号,并在进程没有释放该信号量而终止时,操作系统释放信号量  
      };
    • semctl(sem_id, sem_num, command, semun):控制信号量。commond中有:SETVAL初始化信号量为一个值,该值再semun结构体的val字段;IPC_RMID用于删除一个无需继续使用的信号量。
  • 信号量的使用实例,同时开两个进程,每个进程中都用信号量同步临界区,在临界区中向屏幕打印字符:
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    #include <sys/sem.h>
     
    union semun
    {
        int val;
        struct semid_ds *buf;
        unsigned short *arry;
    };
     
    static int sem_id = 0;
    static int set_semvalue();
    static void del_semvalue();
    static int semaphore_p();
    static int semaphore_v();
     
    int main(int argc, char *argv[])
    {
        char message = 'X';
        int i = 0;
     
        //创建信号量
        sem_id = semget((key_t)1234, 1, 0666 | IPC_CREAT);
     
        if(argc > 1)
        {
            //程序第一次被调用,初始化信号量
            if(!set_semvalue())
            {
                fprintf(stderr, "Failed to initialize semaphore\n");
                exit(EXIT_FAILURE);
            }
            //设置要输出到屏幕中的信息,即其参数的第一个字符
            message = argv[1][0];
            sleep(2);
        }
        for(i = 0; i < 10; ++i)
        {
            //进入临界区
            if(!semaphore_p())
                exit(EXIT_FAILURE);
            //向屏幕中输出数据
            printf("%c", message);
            //清理缓冲区,然后休眠随机时间
            fflush(stdout);
            //离开临界区,休眠随机时间后继续循环
            if(!semaphore_v())
                exit(EXIT_FAILURE);
            sleep(2);
        }
     
        sleep(3);
        printf("\n%d - finished\n", getpid());
     
        if(argc > 1)
        {
            //如果程序是第一次被调用,则在退出前删除信号量
            sleep(3);
            del_semvalue();
        }
        exit(EXIT_SUCCESS);
    }
     
    static int set_semvalue()
    {
        //用于初始化信号量,在sem_union的val字段中设置信号量初值。使用信号量之前必须先初始化!
        union semun sem_union;
        sem_union.val = 1;
        if(semctl(sem_id, 0, SETVAL, sem_union) == -1)
            return 0;
        return 1;
    }
     
    static void del_semvalue()
    {
        //删除信号量
        union semun sem_union;
        if(semctl(sem_id, 0, IPC_RMID, sem_union) == -1)
            printf("Failed to delete semaphore\n");
    }
     
    static int semaphore_p()
    {
        //对信号量做减1操作,即加锁 P(sv)
        struct sembuf sem_b;
        sem_b.sem_num = 0;
        sem_b.sem_op = -1;   //P()
        sem_b.sem_flg = SEM_UNDO;
        if(semop(sem_id, &sem_b, 1) == -1)
        {
            printf("semaphore_p failed\n");
            return 0;
        }
        return 1;
    }
     
    static int semaphore_v()
    {
        //这是一个释放操作,它使信号量变为可用,即解锁 V(sv)
        struct sembuf sem_b;
        sem_b.sem_num = 0;
        sem_b.sem_op = 1;   //V()
        sem_b.sem_flg = SEM_UNDO;
        if(semop(sem_id, &sem_b, 1) == -1)
        {
            printf("semaphore_v failed\n");
            return 0;
        }
        return 1;
    }
      ps:以上代码参考自https://blog.csdn.net/ljianhui/article/details/10243617,运行结果如下:
             

 

posted @ 2018-04-17 21:04  IvanB.G.Liu  阅读(2215)  评论(0编辑  收藏  举报