进程间通信—信号量

RayCongLiang 2019-12-29

进程间通信——信号量(信号灯)

信号与信号量

信号:是由用户、系统或者进程发送给目标进程的信息,以通知目标进程某个状态的改变或系统异常,是一种处理异步事件的方式。

信号量:是一个特殊的变量,本质是计数器,记录了临界资源的数量。进程对其访问都是原子操作(PV操作),用于多线程、多进程之间同步临界资源。

信号量分类

按实现方式,信号量可以分为POSIX信号量与System V信号量。

System V信号量是基于内核维护的,,通常用于Linux系统中。Posix是由文件系统中的路径名对应的名字来标识的。

基于内存的信号量,同步多线程时,可放到该多线程所属进程空间里;如果是同步多进程,那就要把信号量放入到共享内存中(方便多个进程访问)。

     System V 信号量使用步骤

打开/创建信号量   semget
信号量初始化     semctl
P/V操作         semop
删除信号量       semctl

1、信号量创建/打开---semget

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

int semget(key_t key, int nsems, int semflg);
//成功返回信号量id,失败返回-1
//参数
//key---和消息队列关联的key, IPC_PRIVATE或ftok
//nsems---集合中包含计数信号量个数
//semflg---标志位, IPC_CREAT|0666  IPC_EXCL

2、信号量初始化---semctl

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

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

//成功返回0,失败返回EOF
//semid---要操作的信号量集的id
//semnum---要操作的集合中的信号量编号
//cmd---执行的操作  SETVAL  IPC_RMID
//union semun---联合体,取决于cmd

union semun

union semun {
     int               val;   //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
}

3、信号量P/V操作---semop

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

int semop(int semid, struct sembuf *sops, unsigned nsops);

//成功返回0,失败EOF
//参数
//semid---要操作的信号量集的id
//sops ---描述对信号量操作的结构体(数组)
//nsops---要操作的信号量的个数

struct sembuf{
     short sem_num;    //信号量编号
     short  sem_op;    //-1:P操作,1:V操作
     short  sem_flg;    //SEM_UNDO即0 ,IPC_NOWAIT
16 }

  通常为SEM_UNDO即0,使操作系统跟踪信号,并在进程没有释放该信号量而终止时,操作系统释放信号量


 
示例:信号量集/共享内存
要求:父子进程通过System V信号量同步对共享内存的读写
   父进程从键盘输入字符串
   子进程输出字符串

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

union semun{
    int val;
}

#define SEM_READ 0
#define SEM_WRITE 0

//实现P操作
poperation(int index, int semid) //传入要操作的信号量,信号量集
{
    //初始化sembuf结构体
    struct sembuf sop;
    sop.sem_num = index; //信号量编号
    sop.sem_op  = -1;    //P操作:-1
    sop.sem_flg =  0;    //SEM_UNDO

    //信号量集ID,对信号量操作的结构体,操作的信号量个数
    semop(semid,&sop,1);
}

voperation(int index, int semid)
{
    //初始化sembuf结构体
    struct sembuf sop;
    sop.sem_num = index; //信号量编号
    sop.sem_op  =  1;    //V操作:1
    sop.sem_flg =  0;    //SEM_UNDO

    //信号量集ID,对信号量操作的结构体,操作的信号量个数
    semop(semid,&sop,1);

}

int main()
{
    key_t key;
    int semid; //信号量集ID
    int shmid; //共享内存ID
    char * shmaddr;
    pid_t pid;

    key = ftok(".",123); //创建key键值,进而生成不同的IPC对象的标识符ID

    //创建2个信号量
    semid = semget(key, 2, IPC_CREAT|0777);
    if(semid < 0)
    {
        perror("semid");
        return -1;
    }

    //创建共享内存
    shmid = shmget(key, 256, IPC_CREAT|0777);
    if(shmid < 0)
    {
        perror("shmget");
        return -1;
    }

    //初始化2个信号量
    union semun myun;
    myun.val = 0; //初始化读信号量的值为0,未读
    semctl(semid, SEM_READ, SETVAL, myun);

    myun.val = 1; //初始化写信号量的值为1,可写
    semctl(semid, SEM_WRITE,SETVAL, myun);

    //创建一个子进程
    pid = fork();
    if(pid < 0)
    {
        perror("fork");
        return -1;
    }
    else if(pid == 0) //子进程
    {
        //获取映射后的共享内存的地址
        shmaddr = (char *)shmat(shmid, NULL, 0);
        
        poperation(SEM_READ,semid); //读之前对读信号量进行P操作

        printf("getshm:%s",shmaddr);

        voperation(SEM_WRITE,semid); //V操作,读完了,告诉父进程可写

    }else{ //父进程从键盘输入字符

        //获取映射后的共享内存的地址
        shmaddr = (char *)shmat(shmid, NULL, 0);
        
        poperation(SEM_WRITE,semid); //写之前对写信号量进行P操作

        printf("Please input char to shm:");
        fgets(shmaddr, 32,stdin);

        voperation(SEM_READ,semid); //V操作,写完,告诉子进程可以读
    }


}

结果:

进程间通信—信号量

【信号量的意图在于进程间同步,互斥锁和条件变量的意图则在于线程间同步。但是信号量也可用于线程间,互斥锁和条件变量也可用于进程间。我们应该使用适合具体应用的那组原语。】--------https://www.cnblogs.com/fangshenghui/p/4039946.html

相关推荐