并发 -------- 2.5进程间的通信 - 信号量

前言:进程间的通信方式包括


    IPC: 
        1、管道
            pipe 无名管道
            fifo 有名管道 
        2、信号 signal        
        3、消息队列 System V消息队列 / POSIX消息队列     
        4、共享内存 System V共享内存 / POSIX共享内存       
        5、信号量   System V信号量 / POSIX信号量            <-----------
        6、socket套接字


1、引入 

    例子: 
        int i = 0;

        func()
        {
            i++;
        }

        当有两个任务,调用func()函数时,执行之后i的值为多少? 
        答: 不确定的,因为i++ 是一个非原子操作  
                            可以分割成3个步骤: 
                                (1)读取 
                                (2)计算
                                (3)回写

            ====================
             ps.   原子操作 Atomic Operation 
                    一般定义为 在执行过程中不会被线程调度机制中断的操作
                    这种操作一旦开始 就会一直运行到结束,中间不会被其他的线程的操作所打断 
                    原子操作是不可分割的,即操作要么完全执行,要么完全不执行,不存在执行一半的情况

                     非原子操作与之相反

        
        综上所述如果有两个或者多个任务(进程/线程),去访问同一个共享资源(硬件/软件) 那么就必须要保证这个共享资源的 被有序访问。
            

2、信号量 semaphore

    信号量 
        是用于 不同进程之间 或者 同一个进程中不同线程之间 的同步机制  
            同步 ---> 有序、有条件的访问 

        信号量的出现,就是为了保护共享资源,让共享资源被有序的访问 

        信号量 是用来表示一种资源的数量 
                是一种特殊的计数器,用于控制对共享资源的访问 

        什么时候需要信号量? 
            需要保护对象的时候,就需要信号量 
            “保护”:让被保护的对象(共享资源)被有序访问 

    
3、如何保护? 

    信号量机制 其实是对程序设计者的约束,用来保护共享资源的

        例子: 
            进程A   进程B   都需要访问同一个“互斥”设备 

            那么就用一个信号量来表示能不能访问该设备,然后每一个进程在访问这个设备之前,
            都先去访问这个信号量: 
                如果能够访问该设备,就先将信号量调成“NO”,然后再去访问这个互斥设备,
                访问完之后,最后再将该信号量调成“YES”

    信号量如何实现? 
        一个进程(或线程)在某一个信号量上 执行3个操作: 

            (1)创建一个信号量,还必须要求 调用者指定信号量的初始值 
                初始值就表示给信号量保护的共享资源 可以被多少个任务访问 

                    sem --> 1  表示可以被1个进程或者线程去访问
                    sem --> 5  表示可以被5个进程或者线程去访问

            
            (2)等待一个信号量,该操作会测试这个信号量的值 
                如果其值小于或者等于0 那么就会阻塞(等待)
                一旦它的值变成大于0  就会先将它-1 在继续执行访问它的代码

                    while( sem_value <= 0 )
                    {
                        wait 
                    }
                    sem_value --;
                    访问 

                p操作 proberen (尝试 荷兰语)
                lock 上锁


            (3)释放一个信号量,将信号量的值+1 

                    sem_value ++;

                v操作 verhogen (增加)
                unlock 解锁


        信号量保护: 
            在临界区的前面 加上一个p操作,然后在临界区的后面 加上一个v操作 
                “临界区”: 把操作“共享资源”的代码区域 称为 临界区

                p 
                xxx     //临界区
                v 


4、信号量的实现 

    System V semaphore 

    POSIX semaphore 
            POSIX 有名 semaphore 
            POSIX 无名 semaphore 

    
    4.1)System V semaphore 

        System V 信号量  ---> 计数信号量集 (信号量数组) 
                                存在于 内核中 

            对于系统中的每个信号量集,内核都会维护在如下的结构体中:
                    

        
                struct semid_ds 
                {
                    struct ipc_perm sem_perm;  /* 权限 Ownership and permissions */
                    time_t          sem_otime; /* 最后调用semp()的时间 Last semop time */
                    time_t          sem_ctime; /* 最后修改的时间 Last change time */
                    unsigned long   sem_nsems; /* 信号量数组中有多少信号量 No. of semaphores in set */
                };

        接口函数: 
            (1)获取键值key 
                ftok() 

            (2)创建或者打开一个System V信号量集
                semget() 

                    NAME
                        semget - get a System V semaphore set identifier
                    SYNOPSIS
                        #include <sys/types.h>
                        #include <sys/ipc.h>
                        #include <sys/sem.h>

                        int semget(key_t key, int nsems, int semflg);
                            功能:创建或者打开一个System V信号量集
                            参数: 
                                key:键值 
                                nsems:指定要创建的信号量集中 信号量的个数
                                semflg: 标志位
                                        (1)创建标志 
                                            IPC_CREAT | 权限位 
                                            例子: 
                                                IPC_CREAT | 0666 
                                            注意: 
                                                如果创建失败的原因 是因为已经存在了
                                                且 创建的标志为 IPC_CREAT | IPC_EXCL 一起使用 
                                                此时 errno == EEXIST 
                                        (2)打开标志 
                                            0 
                            返回值: 
                                成功,返回该信号量集的id
                                失败,返回-1,同时errno被设置

                    
                    注意: 
                        在创建一个信号量集时,信号量的值是不确定的
                        因此 我们在创建一个信号量集之后,要马上指定它们的初始值 


            (3)控制操作 
                semctl 

                    NAME
                        semctl - System V semaphore control operations
                    SYNOPSIS
                        #include <sys/types.h>
                        #include <sys/ipc.h>
                        #include <sys/sem.h>

                        int semctl(int semid, int semnum, int cmd, ...);
                            功能:对System V信号量集的控制操作 
                            参数: 
                                semid: 信号量集id 
                                semnum:指定要操作哪个信号量,就是信号量数组的下标(0,1,2,...)
                                cmd: 命令号 
                                        IPC_RMID    删除 
                                        IPC_STAT    获取属性 
                                        IPC_SET     设置属性 
                                        GETVAL      获取某个信号量的值
                                        SETVAL      设置某个信号量的值 
                                        GETALL      获取所有信号量的值 
                                        SETALL      设置所有信号量的值
                                        ...
                                ...:第四个参数,根据cmd的不同,第四个参数也不相同
                                        第四个参数的类型: 
                                            union semun 
                                            {
                                                int              val;    /* Value for SETVAL */
                                                struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */
                                &nb

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值