首页 > 系统相关 >进程间通信(信号灯集、消息队列)

进程间通信(信号灯集、消息队列)

时间:2024-09-03 15:57:43浏览次数:6  
标签:信号灯 semid 队列 间通信 int key IPC sem

1.信号灯集

线程:全局变量,同步通过信号量

初始化: sem_init(&sem,0,0);

申请资源:sem_wait(&sem); P操作,-1

释放资源:sem_post(&sem); V操作,+1

1.1 特点

信号灯(semaphore),也叫信号量,信号灯集是一个信号灯的集合。它是不同进程间或一个给定进程内部不同线程间同步的机制;

而Posix信号灯指的是单个计数信号灯:无名信号灯、有名信号灯。(咱们学的是无名信号灯)

System V的信号灯是一个或者多个信号灯的一个集合。其中的每一个都是单独的计数信号灯。

通过信号灯集实现共享内存的同步操作

1.2 步骤

1.创建key值:ftok

2.创建或打开信号灯集: semget

3.初始化信号灯: semctl

4.PV操作: semop

5.删除信号灯集: semctl

1.3 命令

ipcs -s: 查看信号灯集

ipcrm -s semid: 删除信号灯集

注意:有时候可能会创建失败,或者semid为0,所以用命令看看,删了重新创建就可以了。

1.4 函数接口

int semget(key_t key, int nsems, int semflg);
功能:创建/打开信号灯
参数:key:ftok产生的key值
    nsems:信号灯集中包含的信号灯数目
    semflg:信号灯集的访问权限,通常为IPC_CREAT|IPC_EXCL|0666
返回值:成功:信号灯集ID
       失败:-1

int semctl ( int semid, int semnum,  int cmd…/*union semun arg*/);
功能:信号灯集合的控制(初始化/删除)
参数:semid:信号灯集ID
    semnum: 要操作的集合中的信号灯编号,信号灯编号从0开始
     cmd: 
        GETVAL:获取信号灯的值,返回值是获得值
        SETVAL:设置信号灯的值,需要用到第四个参数:共用体
        IPC_RMID:从系统中删除信号灯集合
返回值:成功 0
      失败 -1

用法:
1. 初始化信号灯集中信号灯:
需要自定义共用体:
union semun
{
    int val;
};
union semun mysemun;
mysemun.val=10;
semctl(semid, 0, SETVAL, mysemun);

2. 获取信号灯值: 函数 semctl(semid,0,GETVAL); 的返回值

3. 删除信号灯集: semctl(semid,0,IPC_RMID); 这里传任意一个信号灯的编号就可以删除整个信号灯集了


int semop ( int semid, struct sembuf  *opsptr,  size_t  nops);
功能:对信号灯集合中的信号量进行PV操作
参数:semid:信号灯集ID
     opsptr:操作方式
     nops:  要操作的信号灯的个数 1个
返回值:成功 :0
      失败:-1

struct sembuf {
   short  sem_num; // 要操作的信号灯的编号
   short  sem_op;  //    0 :  等待,直到信号灯的值变成0
                   //   1  :  释放资源,V操作
                   //   -1 :  申请资源,P操作                    
    short  sem_flg; // 0(阻塞),IPC_NOWAIT, SEM_UNDO
};

用法:
申请资源操作:
struct sembuf mysembuf;
mysembuf.sem_num=0;
mysembuf.sem_op=-1;
mysembuf.sem_flg=0;
semop(semid,&mysembuf,1);

释放资源操作:
mysembuf.sem_num=1;
mysembuf.sem_op=1;
mysembuf.sem_flg=0;
semop(semid,&mysembuf,1);

创建信号灯集:

使用信号灯集:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <stdio.h>
#include <errno.h>
union semun
{
    int val;
};

int main(int argc, char const *argv[])
{
    key_t key;
    int semid;

    if ((key = ftok("sem.c", 'a')) < 0)
    {
        perror("ftok err");
        return -1;
    }
    printf("key: %#x\n", key);

    //创建或打开信号灯集
    semid = semget(key, 2, IPC_CREAT | IPC_EXCL | 0777);
    if (semid <= 0)
    {
        if (errno == EEXIST)
            semid = semget(key, 2, 0777);
        else
        {
            perror("semget er");
            return -1;
        }
    }
    else //确保对信号灯集中的信号灯始终初始化一次,下次执行程序还继续获得上一次的值。如果修改初值,需要删除信号灯然后重新打开。
    {
        //初始化信号灯
        union semun sem;
        sem.val = 10;
        semctl(semid, 0, SETVAL, sem); //把0号信号灯初始化值为10

        sem.val = 0;
        semctl(semid, 1, SETVAL, sem); //把1号信号灯初始化值为0
    }
    printf("semid: %d\n", semid);

    //获取信号灯值
    printf("%d\n", semctl(semid, 0, GETVAL));
    printf("%d\n", semctl(semid, 1, GETVAL));

    //PV操作
    struct sembuf buf; //sembuf结构体人家写好的直接拿来用就可以
    buf.sem_num = 0;
    buf.sem_op = -1; //申请资源,P操作,-1
    buf.sem_flg = 0; //阻塞
    semop(semid, &buf, 1); //对0号灯进行P操作申请资源

    buf.sem_num = 1;
    buf.sem_op = 1;  //释放资源,V操作,+1
    buf.sem_flg = 0; //阻塞
    semop(semid, &buf, 1); //对1号灯进行V操作释放资源

    printf("%d\n", semctl(semid, 0, GETVAL));
    printf("%d\n", semctl(semid, 1, GETVAL));

    // //删除信号灯集
    // semctl(semid, 0, IPC_RMID);   //任意传一个信号灯的编号就可以删除整个信号灯集了
    return 0;
}
函数操作:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <stdio.h>
#include <errno.h>
union semun
{
    int val;
};

//初始化
void init(int semid, int num, int val)
{
    union semun sem;
    sem.val = val;
    semctl(semid, num, SETVAL, sem); //把num号信号灯初始化值为val
}

//PV操作
void pv(int semid, int num, int op)
{
    struct sembuf buf; //sembuf结构体人家写好的直接拿来用就可以
    buf.sem_num = num;
    buf.sem_op = op;
    buf.sem_flg = 0;       //阻塞
    semop(semid, &buf, 1); //对num号灯进行op操作
}

int main(int argc, char const *argv[])
{
    key_t key;
    int semid;

    if ((key = ftok("sem.c", 'a')) < 0)
    {
        perror("ftok err");
        return -1;
    }
    printf("key: %#x\n", key);

    //创建或打开信号灯集
    semid = semget(key, 2, IPC_CREAT | IPC_EXCL | 0777);
    if (semid <= 0)
    {
        if (errno == EEXIST)
            semid = semget(key, 2, 0777);
        else
        {
            perror("semget er");
            return -1;
        }
    }
    else //确保对信号灯集中的信号灯始终初始化一次,下次执行程序还继续获得上一次的值。如果修改初值,需要删除信号灯然后重新打开。
    {
        //初始化信号灯
        init(semid, 0, 10); //把0号信号灯初始化值为10
        init(semid, 1, 0);  //把1号信号灯初始化值为0
    }
    printf("semid: %d\n", semid);

    //获取信号灯值
    printf("%d\n", semctl(semid, 0, GETVAL));
    printf("%d\n", semctl(semid, 1, GETVAL));

    //PV操作
    pv(semid, 0, -1); //对0号灯P操作
    pv(semid, 1, 1);  //对1号灯V操作

    printf("%d\n", semctl(semid, 0, GETVAL));
    printf("%d\n", semctl(semid, 1, GETVAL));

    // //删除信号灯集
    // semctl(semid, 0, IPC_RMID);   //任意传一个信号灯的编号就可以删除整个信号灯集了
    return 0;
}

把信号灯集加到共享内存实现同步:输入输出quit结束

input:

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

union semun
{
    int val;
};

//初始化
void init(int semid, int num, int val)
{
    union semun sem;
    sem.val = val;
    semctl(semid, num, SETVAL, sem); //把num号信号灯初始化值为val
}

//PV操作
void pv(int semid, int num, int op)
{
    struct sembuf buf; //sembuf结构体人家写好的直接拿来用就可以
    buf.sem_num = num;
    buf.sem_op = op;
    buf.sem_flg = 0;       //阻塞
    semop(semid, &buf, 1); //对num号灯进行op操作
}

int main(int argc, char const *argv[])
{
    key_t key;
    int semid;
    int shmid;
    char *p;

    if ((key = ftok("sem.c", 'a')) < 0)
    {
        perror("ftok err");
        return -1;
    }
    printf("key: %#x\n", key);

    //创建或打开共享内存
    shmid = shmget(key, 128, IPC_CREAT | IPC_EXCL | 0777);
    if (shmid <= 0)
    {
        if (errno == EEXIST)
            shmid = shmget(key, 128, 0777);
        else
        {
            perror("shmget er");
            return -1;
        }
    }
    printf("shmid: %d\n", shmid);

    //映射共享内存
    p = (char *)shmat(shmid, NULL, 0);
    if (p == (char *)-1)
    {
        perror("shmat err");
        return -1;
    }

    //创建或打开信号灯集
    semid = semget(key, 2, IPC_CREAT | IPC_EXCL | 0777);
    if (semid <= 0)
    {
        if (errno == EEXIST)
            semid = semget(key, 2, 0777);
        else
        {
            perror("semget er");
            return -1;
        }
    }
    else //确保对信号灯集中的信号灯始终初始化一次,下次执行程序还继续获得上一次的值。如果修改初值,需要删除信号灯然后重新打开。
    {
        //初始化信号灯
        init(semid, 0, 0); //把0号信号灯初始化值为0
        init(semid, 1, 1); //把1号信号灯初始化值为1
    }
    printf("semid: %d\n", semid);

    //获取信号灯值
    printf("sem0: %d\n", semctl(semid, 0, GETVAL));
    printf("sem1: %d\n", semctl(semid, 1, GETVAL));

    while (1)
    {
        pv(semid, 1, -1);
        scanf("%s", p);
        pv(semid, 0, 1);
        if (strcmp(p, "quit") == 0)
            break;
    }

    shmdt(p);                      //取消映射
    // shmctl(shmid, IPC_RMID, NULL); //删除共享内存
    // semctl(semid, 0, IPC_RMID);    //删除信号灯集
    return 0;
}

output:

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

union semun
{
    int val;
};

//初始化
void init(int semid, int num, int val)
{
    union semun sem;
    sem.val = val;
    semctl(semid, num, SETVAL, sem); //把num号信号灯初始化值为val
}

//PV操作
void pv(int semid, int num, int op)
{
    struct sembuf buf; //sembuf结构体人家写好的直接拿来用就可以
    buf.sem_num = num;
    buf.sem_op = op;
    buf.sem_flg = 0;       //阻塞
    semop(semid, &buf, 1); //对num号灯进行op操作
}

int main(int argc, char const *argv[])
{
    key_t key;
    int semid;
    int shmid;
    char *p;

    if ((key = ftok("sem.c", 'a')) < 0)
    {
        perror("ftok err");
        return -1;
    }
    printf("key: %#x\n", key);

    //创建或打开共享内存
    shmid = shmget(key, 128, IPC_CREAT | IPC_EXCL | 0777);
    if (shmid <= 0)
    {
        if (errno == EEXIST)
            shmid = shmget(key, 128, 0777);
        else
        {
            perror("shmget er");
            return -1;
        }
    }
    printf("shmid: %d\n", shmid);

    //映射共享内存
    p = (char *)shmat(shmid, NULL, 0);
    if (p == (char *)-1)
    {
        perror("shmat err");
        return -1;
    }

    //创建或打开信号灯集
    semid = semget(key, 2, IPC_CREAT | IPC_EXCL | 0777);
    if (semid <= 0)
    {
        if (errno == EEXIST)
            semid = semget(key, 2, 0777);
        else
        {
            perror("semget er");
            return -1;
        }
    }
    else //确保对信号灯集中的信号灯始终初始化一次,下次执行程序还继续获得上一次的值。如果修改初值,需要删除信号灯然后重新打开。
    {
        //初始化信号灯
        init(semid, 0, 0); //把0号信号灯初始化值为0
        init(semid, 1, 1); //把1号信号灯初始化值为1
    }
    printf("semid: %d\n", semid);

    //获取信号灯值
    printf("sem0: %d\n", semctl(semid, 0, GETVAL));
    printf("sem1: %d\n", semctl(semid, 1, GETVAL));

    while (1)
    {
        pv(semid, 0, -1);
        if (strcmp(p, "quit") == 0)
            break;
        printf("shm: %s\n", p);
        pv(semid, 1, 1);
    }

    shmdt(p);                      //取消映射
    shmctl(shmid, IPC_RMID, NULL); //删除共享内存
    semctl(semid, 0, IPC_RMID);    //删除信号灯集
    return 0;
}

2.消息队列 message queue

传统:无名管道、有名管道、信号

system V: 共享内存、信号灯集、消息队列

队列原则

按消息的类型添加或读取消息

2.1. 特点:

消息队列是IPC对象(活动在内核级别的一种进程间通信的工具)的一种

一个消息队列由一个标识符 (即队列ID)来标识

消息队列就是一个消息的列表。用户可以在消息队列中添加消息、读取消息等

消息队列可以按照类型(自己设一个值作为类型)来发送/接收消息

2.2 步骤

1.创建key值: ftok

2.创建或打开消息队列: msgget

3.添加消息:按照消息的类型把消息添加到已经打开的消息队列末尾 msgsnd

4.读取消息:可以按照消息类型把消息从消息队列中取走 msgrcv

5.删除消息队列: msgctl

2.3 操作命令

ipcs -q: 查看消息队列

ipcrm -q msgid: 删除消息队列

2.4 函数接口

int msgget(key_t key, int flag);
功能:创建或打开一个消息队列
参数:  key值
       flag:创建消息队列的权限IPC_CREAT|IPC_EXCL|0666
返回值:成功:msgid
       失败:-1

int msgsnd(int msqid, const void *msgp, size_t size, int flag); 
功能:添加消息
参数:msqid:消息队列的ID
      msgp:指向消息的指针。常用消息结构msgbuf如下:
          struct msgbuf{
            long mtype;          //消息类型
            char mtext[N]};     //消息正文
   size:发送的消息正文的字节数
   flag:IPC_NOWAIT消息没有发送完成函数也会立即返回    
         0:直到发送完成函数才返回
返回值:成功:0
      失败:-1
用法: msgsnd(msgid, &msg, sizeof(msg)-sizeof(long),0);

int msgrcv(int msgid,  void* msgp,  size_t  size,  long msgtype,  int  flag);
功能:读取消息
参数:msgid:消息队列的ID
     msgp:存放读取消息的空间
     size:接受的消息正文的字节数(sizeof(msgp)-sizeof(long))
    msgtype:
            0:接收消息队列中第一个消息。
            大于0:接收消息队列中第一个类型为msgtyp的消息.
            小于0:接收消息队列中类型值不小于msgtyp的绝对值且类型值又最小的消息。
     flag:
           0:若无消息函数会一直阻塞
           IPC_NOWAIT:若没有消息,进程会立即返回ENOMSG
返回值:成功:接收到的消息的长度
      失败:-1

int msgctl ( int msgqid, int cmd, struct msqid_ds *buf );
功能:对消息队列的操作,删除消息队列
参数:msqid:消息队列的队列ID
     cmd:
        IPC_STAT:读取消息队列的属性,并将其保存在buf指向的缓冲区中。
        IPC_SET:设置消息队列的属性。这个值取自buf参数。
        IPC_RMID:从系统中删除消息队列。
     buf:消息队列缓冲区
返回值:成功:0
      失败:-1
用法:msgctl(msgid, IPC_RMID, NULL);

打开或创建消息队列:

操作消息队列:

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <errno.h>

struct msgbuf
{
    long type; //必须有,在第一个,表示消息的类型,值>0!
    int num;   //消息正文,自己定义
    char ch;
};

int main(int argc, char const *argv[])
{
    key_t key;
    int msgid;

    if ((key = ftok("msg.c", 'a')) < 0)
    {
        perror("ftok err");
        return -1;
    }
    printf("key: %#x\n", key);

    //打开或创建消息队列
    msgid = msgget(key, IPC_CREAT | IPC_EXCL | 0777);
    if (msgid <= 0)
    {
        if (errno == EEXIST)
            msgid = msgget(key, 0777); //如果已经存在消息队列那直接打开该消息队列
        else
        {
            perror("msgget err");
            return -1;
        }
    }
    printf("msgid: %d\n", msgid);

    //添加消息
    struct msgbuf msg;
    msg.type = 10;
    msg.num = 1000;
    msg.ch = 'a';
    msgsnd(msgid, &msg, sizeof(msg) - sizeof(long), 0); //0:发完消息再返回,而不是立即返回函数

    msg.type = 20;
    msg.num = 2000;
    msg.ch = 'b';
    msgsnd(msgid, &msg, sizeof(msg) - sizeof(long), 0);

    //读取消息
    struct msgbuf m;
    msgrcv(msgid, &m, sizeof(m) - sizeof(long), 20, 0); //0:阻塞,读完消息再返回

    printf("%d %c\n", m.num, m.ch);

    //删除消息队列
    msgctl(msgid, IPC_RMID, NULL);

    return 0;
}

进程间通信:

截至到此,IO进程篇分享已经结束,不知道各位同学学的怎么样了,有没有都掌握呢。当然,学习是一个漫长且持久的过程,需要大家持之以恒的努力。在接下来的日子里我会继续为大家分享网络编程相关的笔记供大家参考学习,希望在接下来的日子里我们共同努力,一起进步。谢谢大家。

标签:信号灯,semid,队列,间通信,int,key,IPC,sem
From: https://blog.csdn.net/2301_77143270/article/details/141863934

相关文章

  • 进程间通信----信号灯集
    目录一丶概念二丶操作步骤三丶命令四丶函数接口1.创建信号灯集2.初始化或删除信号灯集3.pv操作练习:一丶概念        信号灯(semaphore),也叫信号量。它是不同进程间或一个给定进程内部不同线程间同步的机制;        SystemV信号灯集是一个或......
  • 优先队列模板
    基础用法intmain(){ /* c++优先队列默认为大根堆 */ priority_queue<int,vector<int>>heap; heap.push(1); heap.push(2); heap.push(3); while(heap.size()){ cout<<heap.top()<<''; heap.pop(); } /*output:321*/ /* 优先队......
  • Spring中基于redis stream 的消息队列实现方法
       本文主要介绍了消息队列的概念性质和应用场景,介绍了kafka、rabbitMq常用消息队列中间件的应用模型及消息队列的实现方式,并实战了在Spring中基于redisstream的消息队列实现方法。一、消息队列   消息队列是一种进程间通信或者同一个进程中不同线程间的通信方......
  • [数据结构] 循环队列
    front:头指针rear:尾指针maxsize:数组长度循环队列通常会让留空数组中的一位,区分队列为空和队列为满的状态。入队移动rear,出队移动front。形式1(默认):front指向队头元素的前一位,而rear指向队尾元素。队列为空:front==rear队列为满:front==(rear+1)%maxsize元素个数:(r......
  • day11(IO进程)进程间的通信---信号灯集
    目录1.特点2.步骤3.命令4.函数接口1.ftok3.semctl3.semop函数操作:把信号灯集加到共享内存实现同步:输入输出quit结束input:output:1.特点信号灯(semaphore),也叫信号量,信号灯集是一个信号灯的集合。它是不同进程间或一个给定进程内部不同线程间同步的机制......
  • linux进程间通信——信号量(通俗易懂,看这一篇就够了)
    信号量概念特点信号量实际是一个计数器。信号量用于实现进程间的互斥与同步,而不是用于存储进程间通信数据。很多进程会访问同一资源,或者向共享内存写入一些东西,为防止争夺资源混乱。可以给一些进程上锁,让其排队等待工作原理P(sv):如果sv的值大于零,就给它减1;如果它的值为......
  • IO进程day07(信号灯集、消息队列)
    【1】信号灯集semaphore1》概念信号灯(semaphore),也叫信号量,信号灯集是一个信号灯的集合。它是不同进程间或一个给定进程内部不同线程间同步的机制;而Posix信号灯指的是单个计数信号灯:无名信号灯、有名信号灯。(咱们学的是无名信号灯)SystemV的信号灯是一个或者多个信号......
  • 算法练习题09:滑动窗口最大值(队列、双端队列)
    classSolution{publicint[]maxSlidingWindow(int[]nums,intk){if(nums==null||nums.length==0){returnnewint[0];}intn=nums.length;int[]result=newint[n-k+1];Deque<Integer&......
  • 线性表之队列API设计思路
    Java学习手册+面试指南:https://javaxiaobear.cn队列是一种基于先进先出(FIFO)的数据结构,是一种只能在一端进行插入,在另一端进行删除操作的特殊线性表,它按照先进先出的原则存储数据,先进入的数据,在读取数据时先读被读出来。1、队列的API设计类名Queue构造方法Queue():创建Queue对象成......
  • Java 最小优先队列API设计与实现
    Java学习+面试指南:https://javaxiaobear.cn最小的元素放在数组的索引1处。每个结点的数据总是小于等于它的两个子结点的数据。1、API设计类名MinPriorityQueue构造方法MinPriorityQueue(intcapacity):创建容量为capacity的MinPriorityQueue对象成员方法privatebooleanless(inti......