Linux中进程间通信(POSIX IPC)-学习笔记(七)

本文详细介绍了内存共享映射(mmap)和消息队列的原理与应用,包括mmap函数参数解析、示例代码及消息队列的创建、发送与接收过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、内存共享映射
1、mmap
(1)将磁盘文件的一部分直接映射到内存
(2)映射机制
    share:内存修改,磁盘跟着修改,磁盘修改内存跟着修改
    private:内存当中的数据发生变化,不影响磁盘空间
2、munmap -->解除映射
3、mmap函数
(1)参数1:内存的地址位于哪个位置
    1)设置多少合适
    1)null,系统会帮找一块内存空间
(2)参数2:lenth--磁盘空间的长度大小
(3)参数3:页面属性  申请页面拥有的权限,页(4096字节)
    * PROT_EXEC表示映射的这一段可执行,例如映射共享库
    * PROT_READ表示映射的这一段可读
    * PROT_WRITE表示映射的这一段可写
    * PROT_NONE表示映射的这一段不可访问
(4)参数4:flag--状态标志
    1)MAP_SHARED   -磁盘映射时候 share  
    2)MAP_PRIVATE -不互相影响
(5)参数5:fd--要映射的磁盘文件是通过哪个文件描述符索引
(6)参数6:offset--偏移量  
    1)从什么位置开始偏移,从0/4096开始
    2)必须是4096的整数倍,以一个页的方式偏移          		 
(7)返回值:void *泛型指针
    1)保存一个地址,内存的首地址
2)内存空间不够时,返回错误
3)只有读权限时,若你要写内容,返回错误
4)MAP_FAILED判断是否成功
//程序1 磁盘文件映射到内存-->修改文件前4字节
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>

int main()
{
    int fd,len;
    int taofd;
    int *p;
    //通过mmap,把hello映射到内存中
    //1、获取hello文件大小
    fd=open("hello",O_RDWR);
    if(fd<0)
    {
        perror("open\n");
        exit(1);
    }
    len=lseek(fd,0,SEEK_END);
    printf("len=%d\n",len);//显示文件大小
    //2、mmap把磁盘文件映射到内存中,MAP_SHARED
    //函数原型void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);
    p=mmap(NULL,len,PROT_READ | PROT_WRITE,MAP_SHARED,fd,0);
    if(p==MAP_FAILED)
    {
        perror("mmap\n");
        exit(1);
    }
    //3、修改内存的值,p[0]=0x30313233 /p 内存的首地址,p[0]索引前4个字节
    //4、查看磁盘的内容
    p[0]=0x30313233;
    //5、close mummap
    close(fd);
    //函数原型int munmap(void *start, size_t length);
    munmap(p,len);
}

程序执行效果:
程序执行前查看文件:
[root@localhost 809]# man ascii  //查看ascii码值
[root@localhost 809]# od -tx1 -tc hello  //查看文本文件内部存储
0000000 68 65 6c 6c 6f 77 6f 72 6c 64 5c 6e 0a
          h   e   l   l   o   w   o   r   l   d   \   n  \n
0000015  //尾地址

 

程序执行后查看文件:

 

二、消息队列-消息的链表
1、概念:
消息队列提供了一个从一个进程向另外一个进程发送一块数据的方法,每个数据块都被认为是有一个类型,接收者进程接收的数据块可以有不同的类型值。
2、特点:随内核存在
3、不足:
   1)每个消息的最大长度有上限
   2)每个消息队列的总字节数有上限
4、函数:
(1)创建和访问消息队列--msgget 
其中key值(暗号) 有两种方式指定
  1)固定值,如 #define MSGKEY 123
  2)通过ftok产生随机key
int main(void)
{
    key_t key;
    key=ftok("./test",'a');
    if(key<0)
    {
        perror("key\n");
        exit(1);
    }
    printf("key=%d\n",key);
}
形参msgflag取值:
IPC_CREAT 如果IPC不存在,则创建1个IPC资源,否则打开操作
IPC_EXCL   只有共享内存不存在的时候,新的共享内存才建立,否则出错

(2)插入一条消息到消息队列--msgsed   
int  msgsnd(int msgid,const void *msg_ptr,size_t msg_sz,int msgflg);
  1)参数1:消息队列的标识
  2)参数2:指向准备发送消息的结构体指针
  3)参数3:结构体长度
  4)参数4:msgflg控制当前消息队列,是否已经满或到达系统上限
(3)从消息队列接收一个消息--msgrcv  
  int msgrcv(int msgid, void *msg_ptr,size_t msgsz,long int msgtype,int msgflg);
  1)参数1:消息队列的标识
  2)参数2:指向准备接收消息的结构体指针
  3)参数3:结构体长度
  4)参数4:msgtype实现接收优先级,形式
  5)参数5:msgflg控制当前消息队列,是否已经满或到达系统上限
形参msgtype取值:
=0 返回队列的最早一个消息
>0 返回其类型为msgtype的第一个消息
<0 返回其类型小于或等于msgtype参数的绝对值的最小的一个消息
//程序2 消息队列 输出key值
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>   //mmap
#include <sys/ipc.h> //ftok

//对外调用,创建key
int createMsgkey()
{
    key_t key=ftok("./test",'a');
    if(key<0)
    {
        perror("key\n");
        exit(1);
    }
    return key;
}

int getMsgkey()//获取key
{
    key_t key=ftok("./test",'a');
    return key;
}
//执行程序前创建test文件中写入一串数字,如1111
int main(void)
{
    key_t key;//定义消息队列的名字
    key=ftok("./test",'a');
    if(key<0)
    {
        perror("key\n");
        exit(1);
    }
    printf("key=%d\n",key);
}


程序执行效果:

 

//程序3 消息队列发送数据
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <errno.h>

#define MSGKEY 123  //定义消息队列

//消息的数据结构是以一个长整型成员变量开始的结构体
struct msgstru
{
    long msgtype;
    char msgtext[2048];
};

int main(int argc,char *argv[])
{
    struct msgstru msgs;
    char str[256];//输入数据缓存数组
    int msg_type;
    int ret_value;//消息的长度
    int msqid;//消息队列标识码
    
    //检查消息队列是否存在
    msqid=msgget(MSGKEY,IPC_EXCL);//(键名,权限)
    if(msqid<0)
    {
        //创建消息队列
        msqid=msgget(MSGKEY,IPC_CREAT | 0666);
        if(msqid<0)
        {
            printf("failed to create msq | errno=%d [%s]\n",errno,strerror(errno));
            exit(-1);
        }
    }
    while(1)
    {
        printf("input message type(end:0):\n");
        scanf("%d",&msg_type);
        if(msg_type==0)
        {
            break;
        }
        printf("input message to be sent:\n");
        scanf("%s",str);
        msgs.msgtype=msg_type;
        strcpy(msgs.msgtext,str);
        //发送消息队列(sizeof消息的长度,而不是整个结构体的长度)
        ret_value=msgsnd(msqid,&msgs,sizeof(msgs.msgtext),IPC_NOWAIT);
        if(ret_value<0)
        {
            printf("msgsnd() write msg failed,errno=%d[%s]\n",errno,strerror(errno));
            exit(-1);
        }
    }
    //删除消息队列
    msgctl(msqid,IPC_RMID,0);
    return 0;
}
//程序4 消息队列接收数据
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#define MSGKEY 123

//消息的数据结构是以一个长整型成员变量开始的结构体
struct msgstru
{
    long msgtype;
    char msgtext[2048];
};

//监听消息队列
int main(void)
{
    struct msgstru msgs;
    int ret_value;
    int msqid;
    while(1)
    {
        //1、判断消息队列是否存在
        msqid=msgget(MSGKEY,IPC_EXCL);//(键名,权限)
        if(msqid < 0)
        {
            //打印错误信息
            continue;
        }
        //(msqid,&msgs,sizeof(msgs.msgtext),IPC_NOWAIT);
        //接收消息队列
        ret_value=msgrcv(msqid, &msgs, sizeof(msgs.msgtext), 0,0);
        printf("text=%s,ret_value=%d\n",msgs.msgtext,ret_value);
    }
    return 0;
}
程序3和程序4结合使用,程序运行效果:

 

//程序5 内存共享映射实现本地聊天室(结构体和临时文件的应用)-->写数据
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/mman.h>
#define MAPLEN 1024

//出错信息判断函数定义
void sys_err(char *str,int exitno)
{
    perror(str);
    exit(exitno);
}

struct STU
{
    int id;
    char name[20];
    char sex;
};
//通过命令行传参
int main(int argc,char *argv[])
{
    
    //定义文件描述符
    int fd,i=0;
    //char *mm;//定义字符指针 对 内存共享映射 数据写入
    struct STU *mm;//定义结构体类型指针 对 内存共享映射 数据写入
    //打开一个文件,是否小于2
    if(argc<2)
    {
        printf("please enter the filename!\n");
        exit(1);
    }
    //以读写或创建方式打开一个文件
    fd = open(argv[1],O_RDWR|O_CREAT,0777);
    //打开文件失败
    if(fd<0)
    {
        sys_err("open",1);//调用出错显示信息函数
    }
    //保证文件的扩展
    lseek(fd,MAPLEN-1,SEEK_SET);
    write(fd,"\0",1);
    //总线出错:文件大小不够,空间不够 4096-->0(right)
    //段错误:权限出错  PROT_NONE-->PROT_READ | PROT_WRITE(right)
    mm=mmap(NULL,MAPLEN,PROT_READ | PROT_WRITE,MAP_SHARED,fd,0);
    if(mm == MAP_FAILED)
    {
        sys_err("mmap",2);
    }
    close(fd);//关闭文件
    while(1)
    {
        //sprintf(mm,"xiaoming %d\n",i++);
        mm->id=1;
        sprintf(mm->name,"yue-%d",i);
        if(i%2)
        {
            mm->sex='m';
        }
        else
        {
            mm->sex='w';
        }
        i++;
        sleep(1);//延时1s
    }
    munmap(mm,MAPLEN);
    
    return 0;
}

//程序6 内存共享映射实现本地聊天室(结构体和临时文件的应用)-->读数据
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/mman.h>

#define MAPLEN 1024

//出错信息判断函数定义
void sys_err(char *str,int exitno)
{
    perror(str);
    exit(exitno);
}

struct STU
{
    int id;
    char name[20];
    char sex;
};

//通过命令行传参
int main(int argc,char *argv[])
{
    //定义文件描述符
    int fd,i=0;
    //char *mm;//定义字符指针 对 内存共享映射 数据写入
    struct STU *mm;//定义结构体类型指针 对 内存共享映射 数据写入
    //打开一个文件,是否小于2
    if(argc<2)
    {
        printf("please enter the filename!\n");
        exit(1);
    }
    //以读写或创建方式打开一个文件
    fd = open(argv[1],O_RDWR);
    //打开文件失败
    if(fd<0)
    {
        sys_err("open",1);//调用出错显示信息函数
    }
    //段错误:权限出错  PROT_NONE-->PROT_READ | PROT_WRITE
    mm=mmap(NULL,MAPLEN,PROT_READ | PROT_WRITE ,MAP_SHARED,fd,0);
    if(mm == MAP_FAILED)
    {
        sys_err("mmap",2);
    }
    close(fd);//关闭文件
    //解除内存共享映射,并删除产生的临时文件
    unlink(argv[1]);
    while(1)
    {
        printf("%d\n",mm->id);
        printf("%s\n",mm->name);
        printf("%c\n",mm->sex);
        sleep(1);
    }
    munmap(mm,MAPLEN); //解除内存共享映射
    return 0;
}

程序5和程序6结合使用,程序执行效果:
执行结果1

 

执行结果2

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值