学习记录——day27 进程间通信 IPC通信机制 消息队列 共享内存

目录

一、IPC通信机制

1、system V提供了三种IPC(interprocess communication)进程间通信方式 消息队列 共享内存 信号量集

2、该通信机制是独立于进程而存在的:当进程将数据写入该通信对象中后,即使进程已经结束,对象中保存的内容仍然存在

3、IPC对象相关指令   ipcs  ipcrm

二、消息队列

1、消息队列的特点

2、消息队列的相关API函数接口 ftok msgget msgsnd msgrcv msgctl

 3、发送端实现

4、接收端实现

5、 获取消息队列的属性

三、共享内存

1、特点

2、共享内存的PAI函数接口  shmget  shmat shmdt

 3、发送端实现

4、接收端实现

使用消息队列完成两个进程之间相互通信

00snd.c

00rcv.c


 

一、IPC通信机制

1、system V提供了三种IPC(interprocess communication)进程间通信方式 消息队列 共享内存 信号量集

消息队列:本质上是在内核空间维护的一个队列,发送者可以将消息先放入到该队列上,接收者根据自己的需求读取队列上的信息
共享内存:在进程外申请一个物理内存,不同进程可以通过内存映射的方式共同使用这段内存,类似于多线程中的临界资源,同样存在竟态,通过使用信号量解决
信号量集:将多个无名信号量,放入一个集合中,分别控制不同的进程,用于进程间同步问题

2、该通信机制是独立于进程而存在的:当进程将数据写入该通信对象中后,即使进程已经结束,对象中保存的内容仍然存在

3、IPC对象相关指令   ipcs  ipcrm

1)ipcs                              查看当前所有的ipc对象的信息
2)ipcs -q                          查看消息队列的信息
3)ipcs -m                         查看共享内存的信息
4)ipcs -s                          查看信号量集的信息
5)ipcrm -q\-m\-s   ID号    删除指定的ipc对象

二、消息队列

1、消息队列的特点

1)放入消息队列中的消息需要进行封装,包括消息类型和消息数据

2)消息队列的消息遵循先进先出原则,如果取出时不指定类型,则默认取第一个,如果指定了类型,则取该类型第一个放入队列中的消息

3)消息队列相关API函数接口

4)消息队列的大小为 16k

2、消息队列的相关API函数接口 ftok msgget msgsnd msgrcv msgctl

1)创建用于生成消息队列的钥匙
       #include <sys/types.h>
       #include <sys/ipc.h>

       key_t ftok(const char *pathname, int proj_id);
        功能:通过给定的文件路径和一个随机ID值创建出一个用于IPC通信的key值       ftok("/", 'k');
        参数1:文件路径,该文件的inode号占key值的2字节,该文件的设备号占key值的1字节
        参数2:一个给定的随机值,该值占key值的1字节
        返回值:成功返回创建出的key值,失败返回-1并置位错误码

2)通过钥匙创建出一个消息队列对象
       #include <sys/types.h>
       #include <sys/ipc.h>
       #include <sys/msg.h>

       int msgget(key_t key, int msgflg);
        功能:通过给定的key值创建一个消息队列
        参数1:用于创建消息队列的key值,该值可以由ftok创建出来,也可以是 IPC_PRIVATE,表示进行亲缘进程间的通信
        参数2:创建标识位
                IPC_CREAT:表示本次操作要创建一个消息队列,如果该key值对应的消息队列已经存在,则直接打开该消息对象
                IPC_EXCL:表示本次确保要创建一个新的消息队列,如果该消息队列已经存在,则该函数报错,错误码为EEXIST
                创建文件的权限,也在该参数中,使用位或连接
        返回值:成功返回消息队列的id号,失败返回-1并置位错误码

3)向消息队列中存放消息
       int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
       功能:向消息队列中存放消息,要求当前进程对消息队列具有可写权限
       参数1:消息队列的id号
       参数2:是指向封装好的消息的起始地址,通常类型如下,但是需要用户自己定义
          struct msgbuf {
               long mtype;       /* message type, must be > 0 */
               char mtext[1];    /* message data */
           };
        参数3:表示参数2中,消息正文的大小,不包含消息类型的大小
        参数4:发送标识位,表示是否阻塞
            0:表示阻塞
            IPC_NOWAIT:表示非阻塞
        返回值:成功返回0,失败返回-1并置位错误码

4)从消息队列中取消息
        size_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);
        功能:从消息队列中取消息
        参数1:消息队列的id号
        参数2:存放消息的容器起始地址
        参数3:消息正文的大小
        参数4:取出消息的类型
                >0: 表示取出该类型的消息的第一个消息
                =0:不限制类型,直接取消息队列中的第一个
                <0: 取出消息队列中类型小于msgtyp绝对值的第一个
                    例如:  50   10   2   10    6   6  8
                    -7:    2  6  6 ---> 2
        参数5:是否阻塞
            0:表示阻塞
            IPC_NOWAIT:表示非阻塞
        返回值:成功返回成功读取的字节个数,失败返回-1并置位错误码

5)控制消息队列
       #include <sys/types.h>
       #include <sys/ipc.h>
       #include <sys/msg.h>

       int msgctl(int msqid, int cmd, struct msqid_ds *buf);
       功能:完成对消息队列指定的cmd操作
       参数1:消息队列的id号
       参数2:操作指令
          IPC_STAT:获取消息队列的属性,此时参数3必须要给定,表示接收消息队列的属性
             struct msqid_ds {
               struct ipc_perm msg_perm;     /* 拥有者和权限 */
               time_t          msg_stime;    /* 最新一次向消息队列中发送数据的时间 */
               time_t          msg_rtime;    /* 最新一次消息队列接受数据的时间 */
               time_t          msg_ctime;    /* 最新一次操作消息队列的时间 */
               unsigned long   __msg_cbytes; /* 当前队列中已用的字节数 */
               msgqnum_t       msg_qnum;     /* 当前队列中消息的个数*/
               msglen_t        msg_qbytes;   /* 队列的最大容量,默认是16K */
               pid_t           msg_lspid;    /* 最后一次向消息队列中发送消息的进程id号 */
               pid_t           msg_lrpid;    /* 最后一次从消息队列中取消息的进程id号 */
           };
               对第一个成员的介绍
              struct ipc_perm {
               key_t          __key;       /* 键值 */
               uid_t          uid;         /* 当前拥有者的用户id号 */
               gid_t          gid;         /*当前拥有者的组id号 */
               uid_t          cuid;        /* 创建消息队列的进程的用户id */
               gid_t          cgid;        /* 创建消息队列进程的组id号 */
               unsigned short mode;        /* 操作权限 */
               unsigned short __seq;       /* 队列号 */
           };
         IPC_SET:设置消息队列的属性  
         IPC_RMID:删除消息队列,当参数2位该值时,参数3可以忽略,直接填NULL即可
         返回值:成功返回0,失败返回-1并置位错误码

 3、发送端实现

#include<myhead.h>
//要发送的消息类型
struct msgbuf 
{
    long mtype;       /* message type, must be > 0 */
    char mtext[1024];    /* message data */
};
#define SIZE sizeof(struct msgbuf)-sizeof(long)


int main(int argc, const char *argv[])
{
    //1、创建key值,用于生产消息队列
    key_t key = ftok("/", 'k');
    if(key == -1)
    {
        perror("ftok error");
        return -1;
    }
    printf("key = %#x\n", key);

    //2、通过key值创建一个消息队列
    int msqid = msgget(key, IPC_CREAT|0664);
    if(msqid == -1)
    {
        perror("msgget error");
        return -1;
    }
    printf("msqid = %d\n", msqid);        //id号

    //向消息队列中存放消息
    struct msgbuf buf;
    
    while(1)
    {
        printf("请输入消息类型:");
        scanf("%ld", &buf.mtype);
        getchar();                    //吸收回车
        printf("请输入消息正文:");
        fgets(buf.mtext, SIZE, stdin);        //从终端获取数据
        buf.mtext[strlen(buf.mtext)-1] = 0;   //将换行换成 '\0'

        //将消息发送到消息队列中
        msgsnd(msqid, &buf, SIZE, 0);
        //参数1:消息队列id号
        //参数2:消息的起始地址
        //参数3:消息正文大小
        //参数4:阻塞形式发送数据
        printf("发送成功\n");

        if(strcmp(buf.mtext, "quit") == 0)
        {
            break;
        }
    }

    return 0;
}

4、接收端实现

#include<myhead.h>
//要发送的消息类型
struct msgbuf 
{
    long mtype;       /* message type, must be > 0 */
    char mtext[1024];    /* message data */
};
#define SIZE sizeof(struct msgbuf)-sizeof(long)


int main(int argc, const char *argv[])
{
    //1、创建key值,用于生产消息队列
    key_t key = ftok("/", 'k');
    if(key == -1)
    {
        perror("ftok error");
        return -1;
    }
    printf("key = %#x\n", key);

    //2、通过key值创建一个消息队列
    int msqid = msgget(key, IPC_CREAT|0664);
    if(msqid == -1)
    {
        perror("msgget error");
        return -1;
    }
    printf("msqid = %d\n", msqid);        //id号

    //从消息队列中读取消息
    struct msgbuf buf;
    
    while(1)
    {
        msgrcv(msqid, &buf, SIZE, 0, 0);
        //参数1:消息队列id号
        //参数2:数据容器起始地址
        //参数3:数据的正文大小
        //参数4:消息类型,0表示任意类型
        //参数5:表示阻塞读取消息

        printf("收到消息为:%s\n", buf.mtext);
    }

    return 0;
}

5、 获取消息队列的属性

#include<myhead.h>


int main(int argc, const char *argv[])
{
    //1、创建key值,用于生产消息队列
    key_t key = ftok("/", 'k');
    if(key == -1)
    {
        perror("ftok error");
        return -1;
    }
    printf("key = %#x\n", key);

    //2、通过key值创建一个消息队列
    int msqid = msgget(key, IPC_CREAT|0664);
    if(msqid == -1)
    {
        perror("msgget error");
        return -1;
    }
    printf("msqid = %d\n", msqid);        //id号

    //获取该消息队列的属性
    struct msqid_ds ds;          //用于存放消息队列属性的变量
    if(msgctl(msqid, IPC_STAT, &ds) == -1)
    {
        perror("msgctl error");
        return -1;
    }

    //程序执行至此,该消息队列中的相关信息就被存入到ds结构体中了
    printf("键:%#x, msqid:%d, 权限:%#o, 已用字节:%ld, 消息:%ld\n", \
            ds.msg_perm.__key, msqid, ds.msg_perm.mode, ds.__msg_cbytes,\
            ds.msg_qnum);



    return 0;
}

三、共享内存

1、特点

1)共享内存是多个进程共享一个外部的物理内存,直接访问内存获取数据,避免了数据的传输,使得效率较高

2)共享内存具有时效性,存放到共享内存区域中的数据,如果不及时读取,下一次写入后,前面的数据会被覆盖

3)共享内存的操作不是一次性的,写入到共享内存中的数据,即使读取出去后,依然存在于共享内存,直到下一次写入

2、共享内存的PAI函数接口  shmget  shmat shmdt

1)创建共享内存对象
         #include <sys/ipc.h>
       #include <sys/shm.h>

       int shmget(key_t key, size_t size, int shmflg);
       功能:通过给定的key值创建一个共享内存的对象,并返回该对象的id
       参数1:key值,可以是IPC_PRIVATE也可以是由ftok创建出来的
       参数2:申请的共享内存段的大小,必须是PAGE_SIZE的整数倍,如果超过,则向上取整
       参数3:创建的标识
                IPC_CREAT:表示本次操作要创建一个共享内存,如果该key值对应的共享内存已经存在,则直接打开该共享内存对象
                IPC_EXCL:表示本次确保要创建一个新的共享内存,如果该共享内存已经存在,则该函数报错,错误码为EEXIST
                创建文件的权限,也在该参数中,使用位或连接
        返回值:成功返回创建出来的共享内存的id,失败返回-1并置位错误码
 
2)将共享内存地址映射到用户空间
       void *shmat(int shmid, const void *shmaddr, int shmflg);
       功能:映射共享内存的地址到用户空间
       参数1:共享内存ID
       参数2:对齐页地址,一般填NULL,让系统选择合适的对齐页
       参数3:共享内存的权限
           SHM_RDONLY:只读权限
           0:    读写权限
       返回值:  成功返回映射的共享内存段的地址,失败返回(void *)-1并置位错误码          
       
 3)取消共享内存的映射
             int shmdt(const void *shmaddr);
             功能:取消共享内存的映射
             参数:共享内存映射的地址
             返回值:成功返回0,失败返回-1并置位错误码
                 
 4)共享内存的控制函数
        #include <sys/ipc.h>
       #include <sys/shm.h>

       int shmctl(int shmid, int cmd, struct shmid_ds *buf);
       功能:控制共享内存对象
       参数1:共享内存的id号
       参数2:操作指令
          IPC_STAT:获取共享内存的属性,此时参数3必须要给定,表示接收共享内存的属性
         IPC_SET:设置共享内存的属性  
         IPC_RMID:删除共享内存,当参数2位该值时,参数3可以忽略,直接填NULL即可
        返回值: 成功返回0,失败返回-1并置位错误码      

 3、发送端实现

#include<myhead.h>
#include<sys/user.h>

int main(int argc, const char *argv[])
{
    //1、创建key值用于创建共享内存段
    key_t key = ftok("/", 't');
    if(key == -1)
    {
        perror("ftok error");
        return -1;
    }

    printf("key = %d\n", key);

    //2、创建一个共享内存的对象
    int shmid = shmget(key, PAGE_SIZE, IPC_CREAT|0664);
    if(shmid == -1)
    {
        perror("shmget error");
        return -1;
    }
    printf("shmid = %d\n", shmid);

    //3、将共享内存段映射到程序中来
    char *addr = (char *)shmat(shmid, NULL, 0);
    //参数1:共享内存的id号
    //参数2:系统自动映射对齐页
    //参数3:表示对共享内存的操作权限为读写权限
    printf("addr = %p\n", addr);         //输出映射的地址

    //向共享内存中写入数据
    strcpy(addr, "hello a hua qing yuan jian\n");

    sleep(5);

    //取消映射关系
    if(shmdt(addr) ==-1)
    {
        perror("shmdt error");
        return -1;
    }


    while(1);

    return 0;
}

4、接收端实现

#include<myhead.h>
#include<sys/user.h>

int main(int argc, const char *argv[])
{
    //1、创建key值用于创建共享内存段
    key_t key = ftok("/", 't');
    if(key == -1)
    {
        perror("ftok error");
        return -1;
    }

    printf("key = %d\n", key);

    //2、创建一个共享内存的对象
    int shmid = shmget(key, PAGE_SIZE, IPC_CREAT|0664);
    if(shmid == -1)
    {
        perror("shmget error");
        return -1;
    }
    printf("shmid = %d\n", shmid);

    //3、将共享内存段映射到程序中来
    char *addr = (char *)shmat(shmid, NULL, 0);
    //参数1:共享内存的id号
    //参数2:系统自动映射对齐页
    //参数3:表示对共享内存的操作权限为读写权限
    printf("addr = %p\n", addr);         //输出映射的地址

    //读出共享内存中的数据
    printf("消息为:%s", addr);

    sleep(5);
    if(shmdt(addr) == -1)
    {
        perror("shmdt error");
        return -1;
    }

    //删除共享内存
    if(shmctl(shmid, IPC_RMID, NULL) == -1)
    {
        perror("shmctl error");
        return -1;
    }


    while(1);

    return 0;
}

使用消息队列完成两个进程之间相互通信

00snd.c

#include <myhead.h>

// 要发送的信息类型
struct msgbuf
{
    long mtype;
    char mtext[1024];
};

#define SIZE sizeof(struct msgbuf) - sizeof(long)

int main(int argc, char const *argv[])
{
    // 1、创建key值用于生成消息队列
    key_t key = ftok("/", 'k');
    if (key == -1)
    {
        perror("ftok error");
        return -1;
    }
    printf("key = %#x\n", key);

    // 2、通过key值创建一个消息队列
    int msqid = msgget(key, IPC_CREAT | 0664);
    if (msqid == -1)
    {
        perror("msgget error");
        return -1;
    }
    printf("msqid = %d\n", msqid);

    // 3、创建父子进程
    pid_t pid;
    if ((pid = fork()) == -1)
    {
        perror("fork error");
        return -1;
    }
    else if (pid == 0)
    {
        // 子进程
        // 4、从消息队列接收信息
        struct msgbuf buf;
        while (1)
        {
            msgrcv(msqid, &buf, SIZE, 2, 0);
            printf("进程1子程序收到信息:%s\n", buf.mtext);

            if (strcmp(buf.mtext, "quit") == 0)
            {
                break;
            }
        }

        exit(EXIT_SUCCESS);
    }

    // 父进程
    //  3、向消息队列中输入数据
    struct msgbuf buf;
    buf.mtype = 1;
    while (1)
    {
        printf("进程1父程序输入信息:");
        fgets(buf.mtext, SIZE, stdin);
        buf.mtext[strlen(buf.mtext) - 1] = 0;

        // 将信息发送到消息队列中
        msgsnd(msqid, &buf, SIZE, 0);
        printf("发送结束\n");

        if (strcmp(buf.mtext, "quit") == 0)
        {
            break;
        }
    }
    //回收子程序资源
    wait(NULL);
    // 删除消息队列
    if (msgctl(msqid, IPC_RMID, NULL) == -1)
    {
        perror("msgctl error");
        return -1;
    }

    return 0;
}

00rcv.c

#include <myhead.h>

// 要发送的信息类型
struct msgbuf
{
    long mtype;
    char mtext[1024];
};

#define SIZE sizeof(struct msgbuf) - sizeof(long)

int main(int argc, char const *argv[])
{
    // 1、创建key值用于生成消息队列
    key_t key = ftok("/", 'k');
    if (key == -1)
    {
        perror("ftok error");
        return -1;
    }
    printf("key = %#x\n", key);

    // 2、通过key值创建一个消息队列
    int msqid = msgget(key, IPC_CREAT | 0664);
    if (msqid == -1)
    {
        perror("msgget error");
        return -1;
    }
    printf("msqid = %d\n", msqid);

    // 3、创建父子进程
    pid_t pid;
    if ((pid = fork()) == -1)
    {
        perror("fork error");
        return -1;
    }
    else if (pid == 0)
    {
        // 子进程
        // 4、向消息队列中输入数据
        struct msgbuf buf;
        buf.mtype = 2;
        while (1)
        {
            printf("进程2子程序输入信息:");
            fgets(buf.mtext, SIZE, stdin);
            buf.mtext[strlen(buf.mtext) - 1] = 0;

            // 将信息发送到消息队列中
            msgsnd(msqid, &buf, SIZE, 0);
            printf("发送结束\n");

            if (strcmp(buf.mtext, "quit") == 0)
            {
                break;
            }
        }
        exit(EXIT_SUCCESS);
    }

    // 父进程
    // 4、从消息队列读取信息
    struct msgbuf buf;
    while (1)
    {
        msgrcv(msqid, &buf, SIZE, 1, 0);
        printf("进程2父程序收到信息:%s\n", buf.mtext);

        if (strcmp(buf.mtext, "quit") == 0)
        {
            break;
        }
    }

    // 回收子程序资源
    wait(NULL);
    // 删除消息队列
    if (msgctl(msqid, IPC_RMID, NULL) == -1)
    {
        perror("msgctl error");
        return -1;
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值