(四)C语言 嵌入式开发之共享内存和消息及内存映射概述及实战示例

1、安装多进程通信库boost:

boost中,用到了别的函数库,所以为了使用boost中相应的功能,需要先安装系统中可能缺失的库 

apt-get install mpi-default-dev #安装mpi库

apt-get install libicu-dev #支持正则表达式的UNICODE字符集 

apt-get install python-dev #需要python的话

apt-get install libbz2-dev #如果编译出现错误:bzlib.h: No such file or directory

上述函数库装好之后,就可以编译boost库了。解压boost_1_64_0.tar.bz2,得到/boost_1_64_0,将当前工作目录切换到此文件夹下。

./bootstrap.sh

生成bjam,上述命令可以带有各种选项,具体可参考帮助文档: ./bootstrap.sh --help。其中--prefix参数,可以指定安装路径,如果不带--prefix参数的话(推荐),默认路径是 /usr/local/include 和 /usr/local/lib,分别存放头文件和各种库。执行完成后,会生成bjam,已经存在的脚本将会被自动备份。注意,boost 1. 64会在当前目录下,生成两个文件bjam和b2,这两个是一样的,所以接下来的步骤,可以用这两个中的任意一个来执行。

using mpi ;  #如果需要MPI功能,需要在 /tools/build/v2/user-config.jam 文件的末尾添加

接下来就是利用生成的bjam脚本编译源代码了

./b2 -a -sHAVE_ICU=1  #-a参数,代表重新编译,-sHAVE_ICU=1代表支持Unicode/ICU

编译完成后,进行安装,也就是将头文件和生成的库,放到指定的路径(--prefix)下

./b2 install

至此,如果一切顺利,就完成安装了。(此部分引用于网络,出处不记得了,有相关版权问题请留言)

2. 消息机制

消息队列它允许一个或多个进程向它写消息,一个或多个进程向它写读消息。消息队列存在于系统内核中,消息的数量受系统限制。通过命令ipcs -q进行查看。

//Msgsend.cpp:
#include   
#include   
#include   
#include   
#include   
#include   
#define MAX_TEXT 512  
struct msg_st  {  
    int destAddr;
    int sourceAddr;  
    char text[MAX_TEXT];  
};  
int main()  {  
    int running = 1;  
    struct msg_st data;  
    char buffer[BUFSIZ];  
    int msgid = -1;  
  
    //建立消息队列  
    msgid = msgget((key_t)1234, 0666 | IPC_CREAT);  
    if(msgid == -1)  {  
        fprintf(stderr, "msgget failed with error: %d\n", errno);  
        exit(EXIT_FAILURE);  
    }  
    //向消息队列中写消息,直到写入end  
    while(running)  {  
        //输入数据  
        printf("Enter some text: ");  
        fgets(buffer, BUFSIZ, stdin);  
        data.destAddr = 56;    //注意2  
        data.sourceAddr = 58;
        strcpy(data.text, buffer);  
        //向队列发送数据  
        if(msgsnd(msgid, (void*)&data, MAX_TEXT, 0) == -1)  {  
            fprintf(stderr, "msgsnd failed\n");  
            exit(EXIT_FAILURE);  
        } 
		if(msgrcv(msgid, (void*)&data, BUFSIZ, 58, 0) == -1){
            fprintf(stderr, "msgrcv failed with errno: %d\n", errno);
            exit(EXIT_FAILURE);
        }
        printf("You wrote: %s\n",data.text); 
        //输入end结束输入  
        if(strncmp(buffer, "end", 3) == 0)  
            running = 0;  
        sleep(1);  
    }  
    exit(EXIT_SUCCESS);  
}

//Msgreceive.cpp:
#include   
#include   
#include   
#include   
#include   
#include   
#define MAX_TEXT 512   
struct msg_st  {  
    int destAddr;
    int sourceAddr;  
    char text[BUFSIZ];  
};  
int main()  {  
    int running = 1;  
    int msgid = -1;  
    struct msg_st data;  
    long int msgtype = 0; //注意1  
    char buffer[32] = {'a','s','r','k'};  
    //建立消息队列  
    msgid = msgget((key_t)1234, 0666 | IPC_CREAT);  
    if(msgid == -1)  {  
        fprintf(stderr, "msgget failed with error: %d\n", errno);  
        exit(EXIT_FAILURE);  
    }  
    printf("BUFSIZ = %d\r\n",BUFSIZ);
    //从队列中获取消息,直到遇到end消息为止  
    while(running)  {  
        if(msgrcv(msgid, (void*)&data, BUFSIZ, 56, 0) == -1)  {  
            fprintf(stderr, "msgrcv failed with errno: %d\n", errno);  
            exit(EXIT_FAILURE);  
        }  
        printf("You wrote: %s\n",data.text); 
		data.destAddr = 58;
		data.sourceAddr = 56;
		strcpy(data.text, buffer); 
       if(msgsnd(msgid, (void*)&data, MAX_TEXT, 0) == -1){
            fprintf(stderr, "msgsnd failed\n");
            exit(EXIT_FAILURE);
        }
        //遇到end结束  
        if(strncmp(data.text, "end", 3) == 0)  
            running = 0;  
    }  
    //删除消息队列  
    if(msgctl(msgid, IPC_RMID, 0) == -1)  {  
        fprintf(stderr, "msgctl(IPC_RMID) failed\n");  
        exit(EXIT_FAILURE);  
    }  
    exit(EXIT_SUCCESS);  
}  

以上例子,实测可用,修改为自己需要的函数体即可。

3. 共享内存及内存映射

mmap和shm共享内存的区别和联系

       mmap()系统调用使得进程之间通过映射同一个普通文件实现共享内存。普通文件被映射到进程地址空间后,进程可以向访问普通内存一样对文件进行访问,不必再调用read(),write()等操作。 成功执行时,mmap()返回被映射区的指针,munmap()返回0。失败时,mmap()返回MAP_FAILED[其值为(void *)-1],munmap返回-1。

mmap和shm的机制

      mmap的机制:就是在磁盘上建立一个文件,每个进程存储器里面,单独开辟一个空间来进行映射。如果多进程的话,那么不会对实际的物理存储器(主存)消耗太大。
       shm的机制:每个进程的共享内存都直接映射到实际物理存储器里面。

区别:
1、mmap保存到实际硬盘,实际存储并没有反映到主存上。

     优点:储存量可以很大(多于主存);缺点:进程间读取和写入速度要比主存的要慢。
2、shm保存到物理存储器(主存),实际的储存量直接反映到主存上。

      优点:进程间访问速度(读写)比磁盘要快;缺点:储存量不能非常大(多于主存)

        在使用时,如果分配的存储量不大,那么使用shm;如果存储量大,那么使用mmap。
        mmap系统调用并不是完全为了用于共享内存而设计的。它本身提供了不同于一般对普通文件的访问方式,进程可以像读写内存一样对普通文件的操作。而Posix或系统V的共享内存IPC则纯粹用于共享目的,当然mmap()实现共享内存也是其主要应用之一。
        mmap并不分配空间, 只是将文件映射到调用进程的地址空间里, 然后你就可以用memcpy等操作写文件, 而不用write()了.写完后用msync()同步一下, 你所写的内容就保存到文件里了. 不过这种方式没办法增加文件的长度, 因为要映射的长度在调用mmap()的时候就决定了。简单说就是把一个文件的内容在内存里面做一个映像,内存比磁盘快些。基本上它是把一个档案对应到你的virtual memory 中的一段,并传回一个指针。

//创建文件映射
#include 
#include 
#include 
#include 
#include 

int main(int argc, char* argv[ ])
{
    using namespace boost::interprocess;
    try {
    // creating our first shared memory object.
    shared_memory_object sharedmem1(create_only, "multishm", read_write);

    // setting the size of the shared memory
    sharedmem1.truncate(256);
    // map the shared memory to current process 
    mapped_region mmap(sharedmem1,read_write);

    // access the mapped region using get_address
    std::strcpy(static_cast(mmap.get_address()), "you are the best!\n");

    } catch (interprocess_exception& e) {
    // .. .  clean up 
    }
}
//读取文件映射后的共享内存
#include 
#include 
#include 
#include 
#include 
int main(int argc, char *argv[ ])
{
      using namespace boost::interprocess;
      try {
      // opening an existing shared memory object 
      shared_memory_object sharedmem2(open_only, "multishm", read_only);
      // map shared memory object in current address space
      mapped_region mmap(sharedmem2, read_only);
      // need to type-cast since get_address returns void* 
      char *str1 = static_cast (mmap.get_address());
      printf("shm access ok\r\n");
      printf("sharememory str=%s\r\n",str1);
      shared_memory_object::remove("multishm");
      } catch (interprocess_exception& e) {
      }
      return 0;
}

共享内存示例

//Servershm.c
#include
#include
#include
#include
#include
#include 
#define SEMID 67865678
#define SHMID 13566876
int semid;
int shmid;
struct People{
	char name[10];
	int age;
};
/*信号量的P操作*/
void SemphoreP(){
	struct sembuf sem_p;
	sem_p.sem_num=0;/*设置哪个信号量*/
	sem_p.sem_op=-1;/*定义操作*/
	if(semop(semid,&sem_p,1)==-1)
	printf("p operation is fail\n");
}
/*信号量的V操作*/
void SemphoreV(){
	struct sembuf sem_v;
	sem_v.sem_num=0;
	sem_v.sem_op=1;
	if(semop(semid,&sem_v,1)==-1)
	printf("v operation is fail\n");
}
int main(){
	/*创建信号量的XSI IPC*/
	semid=semget(SEMID,1,0666|IPC_CREAT);//参数nsems,此时为中间值1,指定信号灯集包含信号灯的数目
	if(semid==-1){
		printf("creat sem is fail\n");
	}
	//创建共享内存
	shmid=shmget(SHMID,1024,0666|IPC_CREAT);//对共享内存
	if(shmid==-1){
		printf("creat shm is fail\n");
	}
	/*设置信号量的初始值,就是资源个数*/
	union semun{
		int val;
		struct semid_ds *buf;
		unsigned short*array;
	}sem_u;
	sem_u.val=1;/*设置变量值*/
	semctl(semid,0,SETVAL,sem_u);//初始化信号量,设置第0个信号量,p()操作为非阻塞的
	struct People*addr;
	addr=(struct People*)shmat(shmid,0,0);//将共享内存映射到调用此函数的内存段
	if(addr==(struct People*)-1){
		printf("shm shmat is fail\n");
	}
	/*向共享内存写入数据*/
	SemphoreP();
	strcpy((*addr).name,"xiaoming");
	(*addr).age=10;
	SemphoreV();
	/*将共享内存与当前进程断开*/
	if(shmdt(addr)==-1){
		printf("shmdt is fail\n");
	}
}

//Clientshm.c
#include
#include 
#include
#include
#include
#include 
#define SEMID 67865678
#define SHMID 13566876
int semid;
int shmid;
void SemphoreP(){
	struct sembuf sem_p;
	sem_p.sem_num=0;
	sem_p.sem_op=-1;
	if(semop(semid,&sem_p,1)==-1){
		printf("p operation is fail\n");
	}
}
/*信号量的V操作*/
void SemphoreV(){
	struct sembuf sem_v;
	sem_v.sem_num=0;
	sem_v.sem_op=1;
	if(semop(semid,&sem_v,1)==-1){
		printf("v operation is fail\n");
	}
}
int main(){
	struct People{
		char name[10];
		int age;
	};
	/*读取共享内存和信号量的IPC*/
	semid=semget(SEMID,0,0666);
	if(semid==-1){
		printf("creat sem is fail\n");
	}
	shmid=shmget(SHMID,0,0666);
	if(shmid==-1){
		printf("creat shm is fail\n");
	}
	/*将共享内存映射到当前进程的地址中,对共享内存操作*/
	struct People*addr;
	addr=(struct People*)shmat(shmid,0,0);
	if(addr==(struct People*)-1){
		printf("shm shmat is fail\n");
	}
	while(1){
	/*从共享内存读出数据*/
		SemphoreP();
		printf("name:%s\n",addr->name);
		printf("age:%d\n",addr->age);
		SemphoreV();
		usleep(2000000);
	}
	/*将共享内存与当前进程断开*/
	if(shmdt(addr)==-1){
		printf("shmdt is fail\n");
	}
	/*IPC必须显示删除。否则会一直留存在系统中*/
	if(semctl(semid,0,IPC_RMID,0)==-1){
		printf("semctl delete error\n");
	}
	if(shmctl(shmid,IPC_RMID,NULL)==-1){
		printf("shmctl delete error\n");
	}
}

以上示例均实测可用,修改后可加入工程或作为独立模块调试使用。

你可能感兴趣的:((四)C语言 嵌入式开发之共享内存和消息及内存映射概述及实战示例)