7.Linux应用编程——共享内存

来源:互联网 发布:sql语句insert 编辑:程序博客网 时间:2024/04/30 04:24

ftok函数:
描述:系统建立IPC通讯 (消息队列、信号量和共享内存) 时必须指定一个ID值。通常情况下,该id值通过ftok函数得到。
头文件:#include <sys/types.h>
        #include <sys/ipc.h>
函数原型:key_t ftok(const char *pathname, int proj_id);
pathname:已存在的合法的路径
proj_id:转化为十六进制,作为key第一个字节的值




命令:
ipcs  查看共享内存
ipcrm -m shmid
共享内存
概述: 共享内存区是最快的IPC形式。一旦这样的内存映射到共享它的进程的地址空间,这些进程间数据传递不再涉及到内核,换句话说是进程不再通过执行进入内核的系统调用来传递彼此的数据(如图)。


共享内存 VS. 其他IPC形式
     用管道/消息队列传递数据
 
用共享内存传递数据
 
    共享内存生成之后,传递数据并不需要再走Linux内核,共享内存允许两个或多个进程共享一个给定的存储区域,数据并不需要在多个进程之间进行复制,因此,共享内存的传输速度更快!


共享内存API
1.shmget(share memery get)
int shmget(key_t key, size_t size, int shmflg);
   创建共享内存,并将该内存的内容初始化为0; 
   打开一个已经存在共享内存, 如果打开时不知道共享内存的大小, 可以将size指定为0, shmflg可以指定为0(按照默认的权限打开);    
参数:
   key(键值):这个共享内存段名字(IPC_PRIVATE和ftok函数的返回值);
   size:所创建共享内存区大小(bytes);
   shmflg:用法类似open函数的权限位,可以用8进制表示;
返回值:
成功返回一个非负整数,即该共享内存段的标识码;
失败返回-1
/**示例: 创建并打开一个共享内存 **/
int main(int argc,char **argv)
{
    const int SHM_SIZE = 1024;
    int shmid = shmget(0x1234, SHM_SIZE, 0666|IPC_CREAT|IPC_EXECL);
    if (shmid == -1)
        err_exit("shmget error");
    cout << "share memory get success" << endl;
}
/**示例: 打开一个共享内存 **/


2.shmat(share memary at)
void *shmat(int shmid, const void *shmaddr, int shmflg);
   连接到本进程地址空间,成功连接之后,对该内存的操作就与malloc来的一块内存非常类似了,而且如果这块内存中有数据,则就可以直接将其中的数据取出来!!
参数:
shmid:shmget的返回值及该共享内存段的标识码
   shmaddr:指定连接的地址(推荐使用NULL)
   shmflg:一般指定为0, 表示可读,可写; 而它的另外两个可能取值是SHM_RND和SHM_RDONLY(见下)
返回值:
   成功返回一个指针,指向共享内存起始地址;失败返回(void *) -1
shmaddr与shmflg组合说明
shmaddr为NULL Linux内核自动为进程连接到进程的内存(推荐使用)
shmaddr不为NULL且shmflg无SHM_RND标记 以shmaddr为连接地址
shmaddr不为NULL且shmflg设置了SHM_RND标记 连接的地址会自动向下调整为SHMLBA的整数倍;
公式:shmaddr - (shmaddr % SHMLBA)
SHMLBA为内存页面的大小(4K)
shmflg=SHM_RDONLY 只读共享内存, 不然的话就是可读,可写的, 注意: 此处没有可读,可写这个概念
 
3.shmdt
int shmdt(const void *shmaddr);
参数:
   shmaddr: 由shmat所返回的指针
注意:将共享内存段与当前进程脱离,不等于删除共享内存段
/** 示例: 将数据写入/读出共享内存
程序write: 将数据写入共享内存
程序read: 将数据读出共享内存(当然, 可以读取N多次)
**/
//write程序
struct Student
{
    char name[26];
    int age;
};
int main(int argc,char **argv)
{
    int shmid = shmget(0x1234, sizeof(Student), 0666|IPC_CREAT);
    if (shmid == -1)
        err_exit("shmget error");


    // 以可读, 可写的方式连接该共享内存
    Student *p = (Student *)shmat(shmid, NULL, 0);
    if (p == (void *)-1)
        err_exit("shmat error");
    strcpy(p->name, "xiaofang");
    p->age = 22;
    shmdt(p);
}
//read程序
int main(int argc,char **argv)
{
    int shmid = shmget(0x1234, 0, 0);
    if (shmid == -1)
        err_exit("shmget error");


    // 以只读方式连接该共享内存
    Student *p = (Student *)shmat(shmid, NULL, 0);
    if (p == (void *)-1)
        err_exit("shmat error");


    // 直接将其中的内容打印输出
    cout << "name: " << p->name << ", age: " << p->age << endl;
    shmdt(p);
}
4.shmctl
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
描述:设置/获取共享内存属性
参数:
   cmd:将要采取的动作(三个取值见下)
   buf:指向一个保存着共享内存的模式状态和访问权限的数据结构()


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


System V共享内存小结:
   1.共享内存被别的程序占用,则删除该共享内存时,不会马上删除(引用计数计数);
   2.此时会出现一个现象:该共享内存的key变为0x00000000,变为私有;
   3.此时还可以读,但必须还有办法获取该共享内存的ID(shmid),因为此时试图通过该共享内存的key获取该共享内存,是白费的!
/** 示例: 删除共享内存 **/
int main(int argc,char *argv[])
{
    int shmid = shmget(0x1234, 0, 0);
    if (shmid == -1)
        err_exit("shmget error");


    if (shmctl(shmid, IPC_RMID, NULL) == -1)
        err_exit("shmctl IPC_RMID error");
    cout << "share memory remove success" << endl;
}




练习:对共享内存进行读写,分别编写read和write。两个进程间的通信用信号(signal)来实现。
程序1:read
#include<stdio.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<signal.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
//当前进程用于读共享内存
//封装一个结构体
typedef struct tag_cap
{
pid_t id;
char data[100];
}cap,*p_cap;


void fun(int sig)
{
printf("read rec sig!\n");
}


int main(int argc, const char *argv[])
{
key_t key;
p_cap p;
pid_t p_w;
int shmid;
//捕捉
// signal(SIGUSR2,fun);
signal(SIGUSR1,fun);
key = ftok("/",'w');//fork
shmid = shmget(key,sizeof(cap),0664 | IPC_CREAT | IPC_EXCL);
if(shmid == -1)
{
if(errno == EEXIST)//共享内存已经存在,只需要打开共享内存
{
printf("share memary have!\n");
shmid = shmget(key,sizeof(cap),0664);//打开共享内存
p=shmat(shmid,NULL,0);//读取对方PID
p_w=p->id;
p->id=getpid();//写自己的PID到共享内存
kill(p_w,SIGUSR2);//给对方发送信号
}
else
{
//报错
}


}
else //当前进程创建并且打开共享内存
{
printf("share memary don't have!\n");
p=shmat(shmid,NULL,0);
p->id=getpid();//写自己的PID
pause();//阻塞
printf("read1 out pause\n");
p_w=p->id;//读取对方PID
}


while(1)
{
pause();//阻塞
if(!strcmp(p->data,"quit"))
{
break;
}
printf("read2 out pause\n");
printf("%s\n",p->data);//读共享内存
kill(p_w,SIGUSR2);//给对方发送信号
}
shmdt(p);//取消映射
shmctl(shmid,IPC_RMID,NULL);//删除共享内存
return 0;
}
程序2:write
#include<stdio.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<stdlib.h>
#include<signal.h>
#include<string.h>
#include<errno.h>
//当前进程用于写共享内存
//封装一个结构体
typedef struct tag_cap
{
pid_t id;
char data[100];
}cap,*p_cap;


void fun(int sig)
{
printf("write rec sig!\n");
}


int main(int argc, const char *argv[])
{
key_t key;
p_cap p;
pid_t p_w;
//捕捉
key = ftok("/",'w');//fork
// signal(SIGUSR1,fun);
signal(SIGUSR2,fun);
int shmid;
shmid = shmget(key,sizeof(cap),0664 | IPC_CREAT | IPC_EXCL);
if(shmid == -1)
{
if(errno == EEXIST)//共享内存已经存在,只需要打开共享内存
{
printf("share memary have!\n");
shmid = shmget(key,sizeof(cap),0664);//打开共享内存
p=shmat(shmid,NULL,0);//读取对方PID
p_w=p->id;
p->id=getpid();//写自己的PID到共享内存
kill(p_w,SIGUSR1);//给对方发送信号
}
else
{
//报错
}


}
else //当前进程创建并且打开共享内存
{
p=shmat(shmid,NULL,0);//写自己的PID
p->id=getpid();
pause();//阻塞
p_w=p->id;//读取对方PID
}


while(1)
{
scanf("%s",p->data);//写数据到共享内存
// getchar();
kill(p_w,SIGUSR1);//发信号给对方
if(!strcmp(p->data,"quit"))
{
break;
}
pause();//阻塞
}


shmdt(p);//取消映射


return 0;
}
0 0
原创粉丝点击