已经很晚了,可是今天对代码的理解碰到点问题,就搁到现在,而且问题还没解决,在吹牛之前先把问题摆出来吧
#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字节