进程间通讯---共享内存(mmap)

已经很晚了,但是今天对代码的理解碰到点问题,就搁到如今,并且问题还没解决,在吹牛以前先把问题摆出来吧
#include <fcntl.h>
#include <unistd.h>

int main(int argc, char** argv)
{
  int fd;
  
  fd=open(argv[1],O_CREAT|O_RDWR|O_TRUNC,0777);
  lseek(fd,100,SEEK_SET);
  write(fd,"",1);
  close( fd );
  
  return 0;
}
这个函数的做用是建立一个大小为100的文件
执行以下:
./test /home/nsl/myprogram/shmfile
运行此函数,结果获得的文件还真的是大小为100,但是不知道该如何理解,从程序上看不过是往99的位置处写了个'\0'而已,但是我看有人说文件大小和文件内容彻底无关,在linux系统中由inode记录文件长度和文件中的块数,若是读到最后一块,说明文件结束
还请高手指点
我看了一下apue,对mmap的介绍不是很好理解,因此从网上找了点资料
本人下面所写的东西也是在理解这篇文章的东西上写的,可是毕竟本身重写一遍会好理解一些,因此不辞辛劳
为何要用共享内存呢?为何不直接把数据写到一个文件里,再从一个文件读呢?
由于mmap和memcpy快啊,cpu时间大概是普通的read/write的一半(在linux中)
当要进行重复的数据传输操做时,用这个好
mmap
void* mmap(void* addr,size_t len,int prot,int flags,int fd,off_t offset)
addr:指定文件应被映射到进程空间的起始地址,通常被指定一个空指针,此时选择起始地址的任务留给内核来完成
len:映射到调用进程地址空间的字节数,它从被映射文件开头offset个字节开始算起
prot:指定共享内存的访问权限,可取以下值: PROT_READ,PROT_WRITE,PROT_EXEC,PROT_NONE
flags:包括MAP_SHARED,MAP_PRIVATE,MAP_FIXED,其中MAP_SHARED,MAP_PRIVATE必选其一,而MAP_FIXED则不推荐使用
fd:即将映射到进程空间的文字描述子,通常由open()返回,同时fd能够为-1,表示匿名映射,用于亲缘关系的进程之间
offset:通常设置为0,表示从文件的开头处开始映射
返回值:为最后文件映射到进程空间的地址,进程可直接操做起始地址为该值的有效地址
如需参考资料,请输命令:man mmap
能够看出来,文件mmap之后,磁盘文件就再也不是普通的文件,其实这个普通的文件描述字已经能够关掉,对这个文件的访问已经能够像访问普通内存同样去访问了,其实数据在没有解除映射以前,这些数据也是不写到磁盘文件中去的。
#include <sys/mman.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>

typedef struct
{
  char name;
  int    age;
}people;

int main(int argc, char** argv) // map a normal file as shared mem:
{
  int fd,i;
  people *p_map;
  char temp;
  
  fd=open(argv[1],O_CREAT|O_RDWR|O_TRUNC,0777);
  //O_CREAT:没有就建立的形式打开
  //O_RDWR:读写的方式打开
  //O_TRUNC:若是文件存在且以写方式打开,则清空文件内容和文件长度
  printf( "sizeof(people) = %d\n",sizeof(people));
  lseek(fd,sizeof(people)*5-1,SEEK_SET);
  write(fd,"",1);
  p_map = (people*) mmap( NULL,sizeof(people)*10,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0 );
  close( fd );//映射完了以后磁盘文件就能够关掉了
  temp = 'a';
   for(i=0; i<10; i++)
  {
        (*(p_map+i)).name = temp ;
    temp += 1;
    ( *(p_map+i) ).age = 20+i;
  }
  printf( " initialize over \n ");
  sleep(10);
  munmap( p_map, sizeof(people)*10 );
  printf( "umap ok \n" );
  
  return 0;
}
注意,mmap那一句其实获得的实际的地址,而后对地址进行(people *)强制类型转换,使得这个的p_map指针就像malloc过同样,指向了一块内存的起始地址
把这个编译为test1
#include <sys/mman.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>

typedef struct
{
  char name;
  int    age;
}people;

int main(int argc, char** argv)  
{
  int fd,i;
  people *p_map;

  fd=open( argv[1],O_CREAT|O_RDWR,0777 );
  p_map = (people*)mmap( NULL,sizeof(people)*10,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
   for(i = 0;i<10;i++)
  {
                printf( "name: %c age %d;\n",(*(p_map+i)).name, (*(p_map+i)).age );
  }
  munmap( p_map,sizeof(people)*10 );
  
  return 0;
}
把这个程序编译为test2
执行状况:
./test1 /home/nsl/myprogram/shm_file & (转入后台运行)
./test2 /home/nsl/myprogram/shm_file
在test1还没munmap以前:
name: a age 20;
name: b age 21;
name: c age 22;
name: d age 23;
name: e age 24;
name: f age 25;
name: g age 26;
name: h age 27;
name: i age 28;
name: j age 29;
在test1被munmap以后:
name: a age 20;
name: b age 21;
name: c age 22;
name: d age 23;
name: e age 24;
name:  age 0;
name:  age 0;
name:  age 0;
name:  age 0;
name:  age 0;
可见,在共享文件只有5个people那么大的时候映射10个people没有返回错误,应为,映射文件时按页的,也就说要映射的剩余大小小于一页的时候,通通分配一晚上,因此映射10个也没有错,而且进行的数据的操做也没有返回错误,由于对地址的操做还在那一页当中,若是超出页,那就会出错
当munmap时,那就把数据写入到磁盘文件中去了,由于磁盘文件的大小只有5个people,因此对共享内存数据进行了截断,因此test2再去读,就只有5个people的大小了
因此,映射的大小最好不要超过文件的大小
另外
页的大小是多少,怎么查看?不会,故写了个程序
#include <unistd.h>
int main()
{
  int a = getpagesize();
  printf ( "%d\n",a); }
获得的返回时4096,个人页大小是4096字节
相关文章
相关标签/搜索