进程间的通信——信号量

原创 2018年04月17日 16:43:26

进程间的通信——管 道https://blog.csdn.net/q496958148/article/etails/79948367
进程间的通信——消息队列https://blog.csdn.net/q496958148/article/details/79951727
进程间的通信——共享内存https://blog.csdn.net/q496958148/article/details/79953349

说道信号量,则必须要提到俩个名词:“同步””互斥”

进程同步:

同步:就是对个进程相互协作共同完成一个任务。
例如:

  • 一个理发店有一个理发师,一把剪子,和多个座位。
  • 如果没有顾客,那么理发师就会休息
  • 只要有一个顾客来了,就必须叫醒理发师
  • 再有多余的顾客来了,那么他只能在座位上等待,如果没座位只能离开了。

进程互斥:

互斥:在同一时间某个资源只允许一个进程访问
例如:

  • 有一个超市,入口处只有一个篮子
  • 当第一个顾客进入时取一个篮子,而这时有第二个顾客来了,看到没篮子,只能坐在一旁等待第一个顾客买完归还篮子,第二个顾客取了篮子他才能进去购物。

而在上述例子中,这个超市在某一个时间段要么没有人,要么只能存在一个人。而这时超市这个资源被称为临界资源或者互斥资源。

信号量与P 、V:

信号量:
    互斥:P V 在同一个进程内
    同步:P V 在不同进程中
信号量值的含义:
    S>0:S代表可用的资源
    S=0:表示无可用资源,无等待队列
    S<0:S的数值表示等待队列的进程个数

P、V操作:

S为一个信号量(互斥模型中S通常设置为1,同步模型中S通常设置为0)
P原语操作步骤:
(1)S=S-1;
(2)若S>=0,则该进程继续运行;
(3)若S<0.则该进程被阻塞,并将它插入该信号量的等待队列中。
V原语操作步骤:
(1)S=S+1;
(2)若S>0,则该进程继续运行;
(3)若S<=0.则从信号量的等待队列中唤醒第一个进程,
使其变为就绪状态,然后再返回原进程继续执行

互斥模型:(S通常为1)

p1进程                    p2进程

P(S);                   P(S);
F();                                    F();    
V(S);                               V(S);   

分析如下:

  • 首先p1进程先执行P操作这候S=S-1=0;
  • 然后执行F();这时候p2进程来了,先进行P操作S=S-1=0-1=-1;这时候 p2进程被阻塞,进入等待队列。
  • 等到p1进程执行完F();进行V操作S=S+1=-1+1=0;这时候唤醒消息队列的第一个位置。
  • p2进程被唤醒,执行F();之后如果再来个p3进程,类似上述做法。

同步模型:(S通常为0)

p1进程                    p2进程

P(S);                   V(S);   

分析如下:

  • 首先进程p1先进行P操作S=S-1=-1;这时候p1进程进入等待队列
  • 这时候p2进程来了,进行V操作S=S+1=-1+1=0;唤醒等待队列的第一个进程p1
  • 就这样不断进行同步操作。

信号量结构体:

 struct semid_ds {
               struct ipc_perm sem_perm;  /* Ownership and permissions */
               time_t          sem_otime; /* Last semop time */
               time_t          sem_ctime; /* Last change time */
               unsigned short  sem_nsems; /* No. of semaphores in set */
           };

信号量有关的函数:

  • 创建和访问一个信号量集
 int semget(key_t key, int nsems, int semflg);
 //key 还是和之前的一样 “暗号”
 //nsems 信号集中信号量的个数
 //semflg IPC_CREAT:创建新的消息队列。 IPC_EXCL:与IPC_CREAT一同使用,表示如果要创建的消息队列已经存在,则返回错误。 IPC_NOWAIT:读写消息队列要求无法满足时,不阻塞。
//返回值成功返回信号量集的标识码,失败返回-1
  • 控制信号量集
 int semctl(int semid, int semnum, int cmd, ...);
//semid 由emget得到的标识码
//semnum 信号集的信号量的序号
//cmd 将要采取的动作,SETVAL设置信号量集中信号量的计数值,GETVAL获取信号量集中的信号量计数值,IPC_STAT把msqid_ds结构的数据设置为信号量集的当前关联值, IPC_SET在权限允许的情况下,把信号量集的当前关联值设置为msqid_ds数据结构中给出的值,IPC_RMID删除信号量集
//最后参数,根据命令不同而不同
//返回值成功为0,失败为-1
  • 操作一个或者一组信号
 int semop(int semid, struct sembuf *sops, unsigned nsops);
 //semid 信号量集标识符
 //sops 指向结构体数值的指针
 //nsops 信号量的个数
 //返回值成功为0,失败为-1
  • sembuf结构体
struct sembuf{
           short          sem_num;  /* semaphore number */
           short          sem_op;   /* semaphore operation */
           short          sem_flg;  /* operation flags */
};
//sem_num是信号量的编号
//sem_op是信号量进行一次PV操作减的数值,一般为-1,+1分别对应P操作和V操作
//sem_flg的俩个值是IPC_NOWAIT和IPC_UNDO

代码实例:

sem.h:

#pragma once

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>

#define TAPH "."
#define NUM 0x6666

union semun {
    int val;
    struct semid_ds *buf;
    unsigned short *array;
    struct seminfo *_buf;
};

int createSem(int nums);

int getSem(int nums);

int initSem(int semid,int nums,int initval);

int P(int semid,int who);

int V(int semid,int who);

int destroySem(int semid);

sem.c:

#include "sem.h"

int createSem(int nums)
{
    key_t key = ftok(TAPH,NUM);
    if(key < 0)
    {
        perror("ftok");
        return -1;
    }

    int semid = semget(key,nums,IPC_CREAT|IPC_EXCL|0666);
    if(semid < 0)
    {
        perror("semget");
        return -1;
    }
    return semid;
}

int getSem(int nums)
{
    key_t key = ftok(TAPH,NUM);
    if(key < 0)
    {
        perror("ftok");
        return -1;
    }

    int semid = semget(key,nums,IPC_CREAT);
    if(semid < 0)
    {
        perror("semget");
        return -1;
    }
    return semid;
}

int initSem(int semid,int nums,int initval)
{
    union semun _un;
    _un.val = initval;
    if(semctl(semid,nums,SETVAL,_un) < 0)
    {
        perror("semctl");
        return -1;
    }
    return 0;
}

static int commPV(int semid,int who,int op)
{
    struct sembuf buf;
    buf.sem_num = who;
    buf.sem_op = op;
    buf.sem_flg = 0;
    if(semop(semid,&buf,1) < 0)
    {
        perror("semop");
        return -1;
    }
    return 0;
}

int P(int semid,int who)
{
    return commPV(semid,who,-1);
}

int V(int semid,int who)
{
    return commPV(semid,who,1);
}

int destroySem(int semid)
{
    if(semctl(semid,0,IPC_RMID) < 0)
    {
        perror("semctl");
        return -1;
    }
    return 0;

}

test.c:

#include "sem.h"

int main()
{
    int semid = createSem(1);
    initSem(semid,0,1);
    pid_t id = fork();
    if(id == 0)
    {
        int semid1 = getSem(0);
        while(1)
        {
            P(semid1,0);
            printf("A");
            fflush(stdout);
            usleep(123456);
            printf("A ");
            fflush(stdout);
            usleep(312456);
            V(semid1,0);
        }
    }
    else
    {
        while(1)
        {
            P(semid,0);
            printf("B");
            fflush(stdout);
            usleep(234561);
            printf("B ");
            fflush(stdout);
            usleep(121134);
            V(semid,0);
        }
        wait(NULL);
    }
    destroySem(semid);
    return 0;
}

Makefile:

test:test.c sem.c
    gcc $^ -o $@;

.PHONY:clean
clean:
    rm -f test 

没有PV操作前:

这里写图片描述

进行PV操作后:

这里写图片描述

PV操作规范了进程的执行,有了先后之分,相当与制定了规则,让所有的所需要对这块内存进行操作的进程有序且正常运行。

如果出现一下情况:
这里写图片描述

解决方法如下:
这里写图片描述

进程间通信方式之信号量

信号量,又称信号灯,主要用于进程间以及同一进程不同线程间的同步手段,用来解决进程间的同步与互斥问题的一种进程之间通信机制,包括一个称为信号量的变量和在该变信号量下等待资源的进程等待队列,以及对信号量进...
  • Echo_Ana
  • Echo_Ana
  • 2016-10-28 16:02:31
  • 885

linux进程间的通信--信号量同步

1.核心理论 进程的同步:是指一组并发的进程互相合作互相等待,使得各进程按照一定的顺序执行的过程叫做进程同步。 同步与互斥的区别:同步时信号量初始值为0,互斥时信号量初始值大于0。 解释:如果用信号...
  • liusirboke
  • liusirboke
  • 2015-11-17 17:13:24
  • 1016

Linux下的进程间各种通信方式的实现代码

  • 2010年08月03日 15:27
  • 7KB
  • 下载

uc笔记09---进程通信,管道,进程间通信,共享内存,消息队列,信号量,IPC 命令

1.    基本概念     何为进程间通信:     进程间通信 (Interprocess Communication, IPC) 是指两个,     或多个进程之间进行数据交换的过程。   ...
  • wolfsun3
  • wolfsun3
  • 2015-09-30 17:04:05
  • 537

Linux进程间通信 共享内存+信号量+简单例子

每一个进程都有着自己独立的地址空间,比如程序之前申请了一块内存,当调用fork函数之后,父进程和子进程所使用的是不同的内存。因此进程间的通信,不像线程间通信那么简单。但是共享内存编程接口可以让一个进程...
  • u011408355
  • u011408355
  • 2015-08-19 16:42:06
  • 2636

进程间通信方式——信号量(Semaphore)

信号量的工作原理,进程通过信号量如何获得共享资源,详解与信号量有关的函数,sembuf的sem_flg标志设为SEM_UNDO的作用以及模拟实现二元信号量。...
  • skyroben
  • skyroben
  • 2017-05-19 01:26:32
  • 1514

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

前面我们在介绍共享内存(传送门: 【Linux进程间通信】 - 共享内存)这种进程间通信时方式时提到,使用共享内存通信时需要使用同步机制来控制多个进程对统一内存区的读写操作。今天我们就来讲述一种常用的...
  • Xiejingfa
  • Xiejingfa
  • 2016-03-16 17:01:01
  • 1738

多进程编程之进程间通信-共享内存,信号量和套接字

1. 背景本文将介绍进程通信中的共享内存,信号量和套接字方法。2. 共享内存共享内存是最快的IPC(进程间通信)方式。共享内存是一个程序向内存写数据,另一个程序读数据,共享内存牵扯到同步的问题,一般有...
  • ljp1919
  • ljp1919
  • 2016-10-09 11:11:56
  • 1290

Posex信号量 实现进程间的同步(生产者&消费者)

Posex信号量sem实现多个线程的互斥,只需在进程空间定义好sem_t变量即可,因为各个线程是共享该sem_t变量。同理,如果sem要实现进程间的互斥,这个sem_t的变量就要在共享存储中定义,因为...
  • ordeder
  • ordeder
  • 2014-03-10 22:35:33
  • 1802

进程的通信方式及其优缺点

 进程通信的含义 进程是转入内存并准备执行的程序,每个程序都有私有的虚拟地址空间,由代码,数据以及它可利用的系统资源(如文件,管道)组成.多进程/多线程是windows操作系统的一个基本特征....
  • zhu_1211
  • zhu_1211
  • 2014-09-05 09:44:00
  • 3029
收藏助手
不良信息举报
您举报文章:进程间的通信——信号量
举报原因:
原因补充:

(最多只允许输入30个字)