zoukankan      html  css  js  c++  java
  • 学习笔记:Linux下共享内存的方式实现进程间的相互通信

    #一、常用函数 #函数系列头文件 ``` #include #include #include ``` #ftok(): 系统建立IPC通讯(如消息队列、共享内存时)必须指定一个ID值。通常情况下,该id值通过ftok函数得到。 ``` key_t ftok( char * fname, int id );//当成功执行的时候,一个key_t值将会被返回,否则 -1 被返回。 ```

    应用举例:

    key_t key = ftok(".", 2);//通过ftok函数得到id值存在key中,下面创建共享内存时会用到
    

    shmget() :

    用于创建共享内存

    int shmget(key_t key, size_t size, int shmflg);	//成功返回共享内存的ID,出错返回-1
    

    应用举例:

    int shmid = shmget(key, 100, IPC_CREAT | IPC_EXCL | 0666);
    //创建共享内存,成功返回共享内存的ID,出错返回-1,IPC_CREAT | IPC_EXCL则可以创建一个新的,唯一的共享内存,如果共享内存已存在,返回一个错误。
    

    (1)第一个参数key是长整型(唯一非零),系统建立IPC通讯 ( 消息队列、 信号量和 共享内存) 时必须指定一个ID值。通常情况下,该id值通过ftok函数得到,由内核变成标识符,要想让两个进程看到同一个信号集,只需设置key值不变就可以。

    (2)第二个参数size指定共享内存的大小,它的值一般为一页大小的整数倍(未到一页,操作系统向上对齐到一页,但是用户实际能使用只有自己所申请的大小)。

    (3)第三个参数shmflg是一组标志,创建一个新的共享内存,将shmflg 设置了IPC_CREAT标志后,共享内存存在就打开。而IPC_CREAT | IPC_EXCL则可以创建一个新的,唯一的共享内存,如果共享内存已存在,返回一个错误。一般我们会还或上一个文件权限

    shmctl():

    用于操作共享内存

    int shmctl(int shm_id, int cmd, struct shmid_ds *buf); //成功返回0,出错返回-1
    

    (1)第一个参数,shm_id是shmget函数返回的共享内存标识符。

    (2)第二个参数,cmd是要采取的操作,它可以取下面的三个值 :

    • IPC_STAT:把shmid_ds结构中的数据设置为共享内存的当前关联值,即用共享内存的当前关联值覆盖shmid_ds的值。

    • IPC_SET:如果进程有足够的权限,就把共享内存的当前关联值设置为shmid_ds结构中给出的值

    • IPC_RMID:删除共享内存段

    (3)第三个参数,buf是一个结构指针,它指向共享内存模式和访问权限的结构。 shmid_ds结构至少包括以下成员

    struct shmid_ds  
    {  
        uid_t shm_perm.uid;  
        uid_t shm_perm.gid;  
        mode_t shm_perm.mode;  
    };    
    

    shmat():

    用于创建共享存储段之后,将进程连接到它的地址空间

    void *shmat(int shm_id, const void *shm_addr, int shmflg); //成功返回指向共享存储段的指针,出错返回-1
    

    应用举例:

    void *pshm = shmat(shmid, 0, 0);//挂接操作,成功返回指向共享存储段的指针,出错返回-1
    if (*(int *)pshm == -1) {//查看挂接是否成功如果出错返回-1,报错
    	printf("shmat error!
    ");
    	exit(0);
    }
    
    

    (1)第一个参数,shm_id是由shmget函数返回的共享内存标识。

    (2)第二个参数,shm_addr指定共享内存连接到当前进程中的地址位置,通常为空,表示让系统来选择共享内存的地址。

    (3)第三个参数,shm_flg是一组标志位,通常为0

    shmdt():

    用于分离操作

    int shmdt(const void *shmaddr); //成功返回0,出错返回-1
    //addr参数是以前调用shmat时的返回值
    

    二、函数实践

    在设计实验中,运用共享内存的四个主要函数shmaget函数,shmat函数,shmdt函数,shmctl函数之后,读出传送数据的文件可以视为服务器read.c,传送数据相当于客户端文件write.c,实现进程之间的相互通信。

    write.c程序段(将数据写入共享空间):

    //write.c
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/shm.h>
    #include <string.h>
    #include <signal.h>
    struct Msg {
    	int flag;//0为读,1为写
    	char content[104];
    };
    int shmid;
    void *pshm;
    void Handle(int s) {//监听函数
    	if (s == 2)
    	{
    		shmdt(pshm);
    		shmctl(shmid, IPC_RMID, 0);
    		exit(0);
    	}
    }
    int main() {
    	signal(2, Handle);//按ctrl+c键退出时会处理这个消息,进行共享内存卸载、删除操作,最后exit(0)退出程序
    
    	key_t key = ftok(".", 2);//通过ftok函数得到id值存在key中
    
    	shmid = shmget(key, 100, IPC_CREAT | IPC_EXCL | 0666);/*创建共享内存,成功返回共享内存的ID,出错返回-1,IPC_CREAT | IPC_EXCL则可以创建一个新的,唯一的共享内存,如果共享内存已存在,返回一个错误。*/
    
    	pshm = shmat(shmid, 0, 0);//挂接操作,成功返回指向共享存储段的指针,出错返回-1
    
    	if (*(int *)pshm == -1) {//查看挂接是否成功如果出错返回-1,报错
    		printf("shmat error!
    ");
    		exit(0);
    	}
    	memset(pshm, 0, 100);//初始化
    	struct Msg *msg = (struct Msg *)pshm;
    	msg->flag = 1;
    	while (1) {
    		if (msg->flag == 1) {//当为1时写消息,此时读文件中不能操作此共享内存
    			printf("请输入内容至共享内存:");
    			scanf("%s", msg->content);
    			msg->flag = 0;//当写消息后flag置为0,让读文件开始执行读操作,此时写文件不能进行写操作
    		}
    		else {
    			sleep(1);
    		}
    	}
    	return 0;
    }
    

    read.c程序段(将数据同步从共享空间读出):

    //read.c
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/shm.h>
    #include <string.h>
    #include <signal.h> 
    struct Msg {
    	int flag;
    	char content[104];
    };
    int main() {
    
    	key_t key = ftok(".", 2);
    	int shmid = shmget(key, 0, 0);//通过ftok函数得到id值存在key中
    
    	void *pshm = shmat(shmid, 0, 0);//挂接操作,成功返回指向共享存储段的指针,出错返回-1
    
    	if (*(int *)pshm == -1) {//查看挂接是否成功如果出错返回-1,报错
    		printf("shmat error!
    ");
    		exit(0);
    	}
    
    	struct Msg * msg = (struct Msg *)pshm;
    	while (1) {
    		if (msg->flag == 0) {//当为0时读消息,此时写文件中不能操作此共享内存
    			printf("从共享内存收到 : %s
    ", msg->content);
    			msg->flag = 1;
    		}
    		else {
    			sleep(1);
    		}
    	}
    
    	return 0;
    }
    

    运行结果与分析:

    在程序运行中,同时打开两个终端,分别运行write和read程序,便能发现write将内容写入共享内存,而同时read进程自行同步将共享内存中的数据读出,进而实现进程通信 。

    设计的这个实验的优点是方便简单,效率非常的高,有着很高的灵活性。但若有多个进程同时读取,可能会出现错误,解决该问题需要有一种进程同步的机制,或者借助其他手段来进行进程间的同步工作,将在接下来的学习中会进一步解决。

    三、参考博客与资料

    sky_Mata 的 进程间通信方式——共享内存: https://blog.csdn.net/skyroben/article/details/72625028
    清清飞扬 的 linux ftok()函数: https://www.cnblogs.com/joeblackzqq/archive/2011/05/31/2065161.html

  • 相关阅读:
    L9,a cold welcome
    别说你不知道java中的包装类,wrapper type,以及容易在自动拆箱中出现的问题
    java导出和读取excel数据
    简单实用句型更新
    PAT1027
    生成英语单词
    c# 操作Word总结【转】
    压缩分卷
    VS2010中,无法嵌入互操作类型“……”,请改用适用的接口的解决方法
    HOW TO:使用 Visual C# .NET 打印 RichTextBox 控件的内容
  • 原文地址:https://www.cnblogs.com/huakui/p/11563584.html
Copyright © 2011-2022 走看看