共享内存
2011年06月27日
一、system v
共享内存允许多个进程共享同一块物理内存空间来实现进程之间的信息交换,其特点是没有中间环节,直接将共享的内存页面通过附接,映射到相互通信的进程各自的虚拟地址空间中,从而使多个进程可以直接访问同一个物理内存页面,如同访问自己的私有空间一样(但实质上不是私有的而是共享的)。如果一个进程向这段共享内存写了数据,所做的改动会立刻被有访问同一段共享内存的其他进程看到。要注意的是共享内存本身没有提供任何同步功能。
一、SystemV共享内存
1、 获得共享内存
int shmget( key_t key, size_t size, int shmflg );
成功返回内存id,失败返回-1。
Key
一般用ftok获得
#include
#include
key_t ftok(const char *pathname, int proj_id);
size
内存大小,向上取整,单位是页,一页大小是4096个字节。所以如果一段进程只申请一块只有一个字节的内存,内存也会分配整整一页。
Shmflg
IPC_CREAT:如果内核中没有此共享内存,则创建它。 如果已经存在,则返回已存在的具有相同关键字值的共享内存的内部标识符
IPC_EXCL当和IPC_CREAT一起使用时,如果内核中没有此共享内存,则创建它。如果此共享内存已经存在,则失败。
在实验中,可以使用0666|IPC_CREAT,表示任意进程可读可写。
2、 将共享内存映射到进程的地址空间
void *shmat( int shmid, const void * shmaddr, int shmflg );
成功返回地址,失败返回-1.
shmid
共享存储区的描述符
Shmaddr
用户提供的共享存储区附接的虚地址,一般取0,表示由系统选择。
Shmflg
规定了对该存储区的操作权限,一般写0,表示可读可写。
3、 断开共享内存和进程的映射
int shmdt(const void *shmaddr);
正确返回0,失败返回-1。
4、 移除共享内存
shmctl(int shmid, int cmd, struct shmid_ds *buf)
正确返回0,失败返回-1。
shmid
共享存储区的内部标识符
Cmd
移除写 PC_RMID
Buf
取0。
例如:shmctl(shmid,IPC_RMID,0);
下面增加一些字符处理的函数
1、以追加的方式写入
#include
char *strcat(char *dest, const char *src);
char *strncat(char *dest, const char *src, size_t n);
The strcat() function appends the src string to the dest string, overwriting the null byte ('\0') at the end of dest, and then adds a terminating null byte. The strings may not overlap, and the dest string must have enough space for the result.
char* strncat(char *dest, const char *src, size_t n)
{
size_t dest_len = strlen(dest);
size_t i;
for (i = 0 ; i
char *strcpy(char *dest, const char *src);
char *strncpy(char *dest, const char *src, size_t n);
3、覆盖的方式
#include
void *memset(void *s, int c, size_t n);
The memset() function fills the first n bytes of the memory area pointed to by s with the constant byte c.
二、posix
1、 打开或创建一个共享内存区
int shm_open(const char *name,int oflag,mode_t mode);
name
路径
Oflag
O_RDONLY、O_RDWR、O_CREAT(已经存在,就直接打开)、O_EXCL(已经存在返回错误)、O_TRUNC(已经存在就清空)。
Mode
指定权限位,它指定O_CREAT标志的前提下使用。
2、 调整共享内存区的大小
int truncate(const char *path, off_t length);
int ftruncate(int fd, off_t length);
可以用于文件和共享内存,成功返回0,失败返回-1。
3、 把共享内存区映射到进程
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
addr参数用于指定映射存储区的起始地址。通常将其设置为NULL。
length是要映射的字节数。
prot参数说明对映射存储区的保护要求。PROT_READ(映射区可读), PROT_WRITE(映射区可写), PROT_EXEC(映射区可执行)任意组合的按位或,也可以是PROT_NONE(映射区不可访问)。对指定映射存储区的保护要求不能超过文件open模式访问权限。
Flags: MAP_SHARED,一个进程对映射的内存做了修改,另一个进程也会看到这种变化。
4、 同步
int msync(void *addr,size_t len,int flags);
如果我们修改了内存映射到某个文件的内存区中某个位置的内容,那么内核将在稍后某个时刻相应地更新文件。然而有时候我们希望确信硬盘上的文件内容与内存映射区中的文件内容一致,于是调用msync()来写回磁盘,实现磁盘上文件内容与共享内存区的内容一致。
若成功则返回0,若出错则返回-1
addr 指向映射存储区的起始地址
len 映射的字节
flags:
MS_ASYNC(执行异步写),一调用,函数就立刻返回,在后台写回文件
MS_SYNC(执行同步写),直到写回文件,函数才会返回
MS_INVALIDATE(使其他进程的映射区的数据失效),一般不用
一般只使用MS_SYNC
5、取消映射
int munmap(caddr_t addr,size_t len);
会执行写回磁盘的操作。然后取消映射。
6、删除一个共享内存区
int shm_unlink(const char *name);
成功返回0,出错返回-1
[b]mmap[/b][b]三种使用方式[/b][b][/b]
void *mmap(void *addr, size_t length, int prot, int flag, int fd, off_t off);[b][/b]
(1)内存映射I/O,将一个普通文件映射进内存:适用于任何进程之间; 此时,需要打开或创建一个文件,然后再调用mmap();典型调用代码如下:
fd=open(name, flag, mode);
if(fd内存映射:适用于具有亲缘关系的进程之间; 由于父子进程特殊的亲缘关系,在父进程中先调用mmap(),然后调用fork()。
那么在调用fork()之后,子进程继承父进程匿名映射后的地址空间,同样也继承mmap()返回的地址,这样,父子进程就可以通过映射区域进行通信了。
对于具有亲缘关系的进程实现共享内存最好的方式应该是采用匿名内存映射的方式。此时,不必指定具体的文件,只要设置相应的标志即可。
Ptr = mmap(0,len, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS,-1,0);
(3)共享内存映射,见上。
2011年06月27日
一、system v
共享内存允许多个进程共享同一块物理内存空间来实现进程之间的信息交换,其特点是没有中间环节,直接将共享的内存页面通过附接,映射到相互通信的进程各自的虚拟地址空间中,从而使多个进程可以直接访问同一个物理内存页面,如同访问自己的私有空间一样(但实质上不是私有的而是共享的)。如果一个进程向这段共享内存写了数据,所做的改动会立刻被有访问同一段共享内存的其他进程看到。要注意的是共享内存本身没有提供任何同步功能。
一、SystemV共享内存
1、 获得共享内存
int shmget( key_t key, size_t size, int shmflg );
成功返回内存id,失败返回-1。
Key
一般用ftok获得
#include
#include
key_t ftok(const char *pathname, int proj_id);
size
内存大小,向上取整,单位是页,一页大小是4096个字节。所以如果一段进程只申请一块只有一个字节的内存,内存也会分配整整一页。
Shmflg
IPC_CREAT:如果内核中没有此共享内存,则创建它。 如果已经存在,则返回已存在的具有相同关键字值的共享内存的内部标识符
IPC_EXCL当和IPC_CREAT一起使用时,如果内核中没有此共享内存,则创建它。如果此共享内存已经存在,则失败。
在实验中,可以使用0666|IPC_CREAT,表示任意进程可读可写。
2、 将共享内存映射到进程的地址空间
void *shmat( int shmid, const void * shmaddr, int shmflg );
成功返回地址,失败返回-1.
shmid
共享存储区的描述符
Shmaddr
用户提供的共享存储区附接的虚地址,一般取0,表示由系统选择。
Shmflg
规定了对该存储区的操作权限,一般写0,表示可读可写。
3、 断开共享内存和进程的映射
int shmdt(const void *shmaddr);
正确返回0,失败返回-1。
4、 移除共享内存
shmctl(int shmid, int cmd, struct shmid_ds *buf)
正确返回0,失败返回-1。
shmid
共享存储区的内部标识符
Cmd
移除写 PC_RMID
Buf
取0。
例如:shmctl(shmid,IPC_RMID,0);
下面增加一些字符处理的函数
1、以追加的方式写入
#include
char *strcat(char *dest, const char *src);
char *strncat(char *dest, const char *src, size_t n);
The strcat() function appends the src string to the dest string, overwriting the null byte ('\0') at the end of dest, and then adds a terminating null byte. The strings may not overlap, and the dest string must have enough space for the result.
char* strncat(char *dest, const char *src, size_t n)
{
size_t dest_len = strlen(dest);
size_t i;
for (i = 0 ; i
char *strcpy(char *dest, const char *src);
char *strncpy(char *dest, const char *src, size_t n);
3、覆盖的方式
#include
void *memset(void *s, int c, size_t n);
The memset() function fills the first n bytes of the memory area pointed to by s with the constant byte c.
二、posix
1、 打开或创建一个共享内存区
int shm_open(const char *name,int oflag,mode_t mode);
name
路径
Oflag
O_RDONLY、O_RDWR、O_CREAT(已经存在,就直接打开)、O_EXCL(已经存在返回错误)、O_TRUNC(已经存在就清空)。
Mode
指定权限位,它指定O_CREAT标志的前提下使用。
2、 调整共享内存区的大小
int truncate(const char *path, off_t length);
int ftruncate(int fd, off_t length);
可以用于文件和共享内存,成功返回0,失败返回-1。
3、 把共享内存区映射到进程
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
addr参数用于指定映射存储区的起始地址。通常将其设置为NULL。
length是要映射的字节数。
prot参数说明对映射存储区的保护要求。PROT_READ(映射区可读), PROT_WRITE(映射区可写), PROT_EXEC(映射区可执行)任意组合的按位或,也可以是PROT_NONE(映射区不可访问)。对指定映射存储区的保护要求不能超过文件open模式访问权限。
Flags: MAP_SHARED,一个进程对映射的内存做了修改,另一个进程也会看到这种变化。
4、 同步
int msync(void *addr,size_t len,int flags);
如果我们修改了内存映射到某个文件的内存区中某个位置的内容,那么内核将在稍后某个时刻相应地更新文件。然而有时候我们希望确信硬盘上的文件内容与内存映射区中的文件内容一致,于是调用msync()来写回磁盘,实现磁盘上文件内容与共享内存区的内容一致。
若成功则返回0,若出错则返回-1
addr 指向映射存储区的起始地址
len 映射的字节
flags:
MS_ASYNC(执行异步写),一调用,函数就立刻返回,在后台写回文件
MS_SYNC(执行同步写),直到写回文件,函数才会返回
MS_INVALIDATE(使其他进程的映射区的数据失效),一般不用
一般只使用MS_SYNC
5、取消映射
int munmap(caddr_t addr,size_t len);
会执行写回磁盘的操作。然后取消映射。
6、删除一个共享内存区
int shm_unlink(const char *name);
成功返回0,出错返回-1
[b]mmap[/b][b]三种使用方式[/b][b][/b]
void *mmap(void *addr, size_t length, int prot, int flag, int fd, off_t off);[b][/b]
(1)内存映射I/O,将一个普通文件映射进内存:适用于任何进程之间; 此时,需要打开或创建一个文件,然后再调用mmap();典型调用代码如下:
fd=open(name, flag, mode);
if(fd内存映射:适用于具有亲缘关系的进程之间; 由于父子进程特殊的亲缘关系,在父进程中先调用mmap(),然后调用fork()。
那么在调用fork()之后,子进程继承父进程匿名映射后的地址空间,同样也继承mmap()返回的地址,这样,父子进程就可以通过映射区域进行通信了。
对于具有亲缘关系的进程实现共享内存最好的方式应该是采用匿名内存映射的方式。此时,不必指定具体的文件,只要设置相应的标志即可。
Ptr = mmap(0,len, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS,-1,0);
(3)共享内存映射,见上。