linux 环境编程学习笔记 第22、23天 基于内存的通信


 

一、基于内存的通信

 

一组内核共享工具

ipcs 可以看到三段东西

Shared Memory Segments  共享内存

Semaphore Arrays          信号量数组,共享内存数组

Message Queues             共享消息队列

 

ipcrm 

 

1.普通的父子进程之间的匿名内存共享映射

 

2.内核共享内存(无序)

 

    编程模型:

          (1)  创建共享内存,得到一个ID             shmget函数

                          int shmget(key_t key,  // 保证由相同key产生的ID是唯一,可以用ftok函数参数唯一的key

                                          size_t size, //共享内存大小

                                           int shmflg);  //创建方式 | 权限  方式:创建  IPC_CREAT 、IPC_EXCL防止覆盖 ,打开  0 | 0

                           key_t ftok(const char *pathname, int proj_id);                         

          (2)  用这个ID映射成虚拟地址 (挂载)      shmat函数

                          void *shmat(int shmid,   //ID

                                              const void *shmaddr, //指定虚拟首地址,0表示系统自动分配,用sbrk(0) 也可以

                                              int shmflg); //挂载方式   SHM_REMAP重新映射  SHM_RDONLY 只读

                               返回合法地址表示成功,发现 1 表示失败

          (3)  用虚拟地址访问内核共享内存         

          (4)  卸载虚拟地址                                   shmdt函数

          (5)  删除共享内存                                   shctl函数(除了删除,还可以修改/ 获取共享内存的属性)

               int shmctl(int shmid, // ID

                                int cmd,    // 0 表示默认,读写IPC_STAT 获取  IPC_SET设置  IPC_RMID 删除共享内存

                                struct shmid_ds *buf);

    共享内存的属性

     key        shmid           owner               perms        bytes       nattch                        status  

                   共享内存id  内存创建者     共享内存的权限      大小       有几个进程挂载         共享内存状态

                                                                                                      在这个共享内存上

 

例子1:

 

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <sys/shm.h>

int main(int argc, const char *argv[])
{
    key_t key;
    int shmid;
    key=ftok(".",255);
    if(key==-1) printf("fork error:%m\n"),exit(-1);
    
    shmid=shmget(key,4,IPC_CREAT|IPC_EXCL|0666);
    if(shmid==-1) printf("shmget error:%m\n"),exit(-1);


    return 0;
}

 

 

 

第一次运行生成的程序 ipcs -m 可以看到创建的共享内存

第二次运行生成的程序 就会产生错误 输出 “shmget error:File exists”  因为加了IPC_EXCL,ipcrm 删除创建的共享内存,就可以正常创建

 


例子2:

共享

 

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <sys/shm.h>

    
key_t key;
int shmid;
int *p;

void handle(int s)
{
    shmdt(p);//卸载共享内存
    shmctl(shmid,IPC_RMID,NULL); //删除共享内存
    exit(1);
}

int main(int argc, const char *argv[])
{
    signal(SIGINT,handle);
    key=ftok(".",255);
    if(key==-1) printf("fork error:%m\n"),exit(-1);

    shmid=shmget(key,4,IPC_CREAT|IPC_EXCL|0666); //创建共享内存
    if(shmid==-1) printf("shmget error:%m\n"),exit(-1);

    p=shmat(shmid,0,0); //挂载共享内存
    if(p==-1) printf("shmat error:%m\n"),exit(-1);
    int i=0;
    while(1)  //访问共享内存
    {
        *p=i;
        ++i;
        sleep(1);
    }

    shmdt(p);//卸载共享内存

    shmctl(shmid,IPC_RMID,NULL); //删除共享内存


    return 0;
}


访问

 

 

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <sys/shm.h>

int main(int argc, const char *argv[])
{
    key_t key;
    int shmid;
    int *p;
    key=ftok(".",255);
    if(key==-1) printf("fork error:%m\n"),exit(-1);
    
    shmid=shmget(key,4,0); //创建共享内存
    if(shmid==-1) printf("shmget error:%m\n"),exit(-1);

    p=shmat(shmid,0,0); //挂载共享内存
    if(p==-1) printf("shmat error:%m\n"),exit(-1);

    while(1)
    {
        printf("%d\n",*p);
        sleep(1);
    }

    shmdt(p);//卸载共享内存



    return 0;
}

 

 

 

3.内核共享队列有序)

    编程模型: 

        (1)创建共享队列 / 得到队列   msgget函数

                   int msgget(key_t key, int msgflg);  //msgflg 跟shmflg一样

        (3)使用队列   (发送消息msgsnd函数 / 接受消息msgrcv函数)

                 发送消息   

                  int msgsnd(int msqid,   // ID 

                                       const void *msgp,  //要发送的消息

                                       size_t msgsz,   //消息的长度 不包含类型的4个字节

                                        int msgflg);   //发送消息的方式,建议为0 

                       失败返回-1,成功返回实际发送的字节数

                    消息有固定的格式:前面4个字节专门表示消息的类型,后面若干字节表示消息的内容。

                     系统没有给我们定义,要自己定义这个结构体 

                     struct msgbuf {
                            long mtype;       /* message type, must be > 0 */
                           char mtext[1];    /* message data */
                       };

                  接收消息:

                   ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,
                      int msgflg);

        (4)删除队列    msgctl 函数

                 int msgctl(int msqid, int cmd, struct msqid_ds *buf);                 


例子:

 

#include <stdio.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdlib.h>
#include <string.h>

struct msgbuf
{
    long type;
    char data[40];
};

int main(int argc, const char *argv[])
{
    key_t key;
    int msgid;
    key=ftok(".",200);
    if(key==-1) printf("ftok error:%m\n"),exit(-1);

    msgid=msgget(key,IPC_CREAT|IPC_EXCL|0666);  //创建消息队列
    if(msgid==-1)printf("msgget error:%m\n"),exit(-1);

    struct msgbuf msg;
    int i;
    for(i=0;i<10;i++){
        bzero(msg.data,sizeof(msg.data));
        msg.type=1;
        sprintf(msg.data,"Message:%d",i);
        msgsnd(msgid,&msg,sizeof(msg.data),0); //发送消息1
    }

    for(i=0;i<10;i++){
        bzero(msg.data,sizeof(msg.data));
        msg.type=2;
        sprintf(msg.data,"Message2:%d",i);
        msgsnd(msgid,&msg,sizeof(msg.data),0); //发送消息2
    }

//    msgctl(msgid,IPC_RMID,0); //删除队列


    return 0;
}

 

 

例子2:

发送消息

 

#include <stdio.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdlib.h>
#include <string.h>

struct msgbuf
{
    long type;
    char data[40];
};

int main(int argc, const char *argv[])
{
    key_t key;
    int msgid;
    key=ftok(".",200);
    if(key==-1) printf("ftok error:%m\n"),exit(-1);

    msgid=msgget(key,IPC_CREAT|IPC_EXCL|0666);  //创建消息队列
    if(msgid==-1)printf("msgget error:%m\n"),exit(-1);

    struct msgbuf msg;
    int i;
    for(i=0;i<10;i++){
        bzero(msg.data,sizeof(msg.data));
        msg.type=1;
        sprintf(msg.data,"Message:%d",i);
        msgsnd(msgid,&msg,sizeof(msg.data),0); //发送消息1
    }

    for(i=0;i<10;i++){
        bzero(msg.data,sizeof(msg.data));
        msg.type=2;
        sprintf(msg.data,"Message2:%d",i);
        msgsnd(msgid,&msg,sizeof(msg.data),0); //发送消息2
    }

//    msgctl(msgid,IPC_RMID,0);


    return 0;
}

接收消息

#include <stdio.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdlib.h>
#include <string.h>

struct msgbuf
{
    long type;
    char data[40];
};

int main(int argc, const char *argv[])
{
    key_t key;
    int msgid;
    key=ftok(".",200);
    if(key==-1) printf("ftok error:%m\n"),exit(-1);

    msgid=msgget(key,0);  //得到消息队列
    if(msgid==-1)printf("msgget error:%m\n"),exit(-1);

    struct msgbuf msg;
    while(1)
    {
        bzero(&msg,sizeof(msg));
        msgrcv(msgid,&msg,sizeof(msg.data),1,0); //接收消息1
        printf("%s\n",msg.data);
        
        bzero(&msg,sizeof(msg));
        msgrcv(msgid,&msg,sizeof(msg.data),2,0); //接收消息2
        printf("%s\n",msg.data);

    }



    return 0;
}