Linux访问其他进程空间,Linux环境进程间通信系列(五):共享内存

共享内存可以说是最有用的进程间通信方式,也是最快的

IPC

形式。两个不同进程

A

B

共享内存的意思是,同一块物理内存被映射到进程

A

B

各自的进程地址空间。进程

A

可以即时看到进程

B

对共享内存中数据的更新,反之亦然。由于多个进程共享同一块内存区域,必然需要某种同步机制,互斥锁和信号量都可以。

采用共享内存通信的一个显而易见的好处是效率高,因为进程可以直接读写内存,而不需要任何数据的拷贝。对于像管道和消息队列等通信方式,则需要在内核和用户空间进行四次的数据拷贝,而共享内存则只拷贝两次数据

[1]

一次从输入文件到共享内存区,另一次从共享内存区到输出文件。实际上,进程之间在共享内存时,并不总是读写少量数据后就解除映射,有新的通信时,再重新建

立共享内存区域。而是保持共享区域,直到通信完毕为止,这样,数据内容一直保存在共享内存中,并没有写回文件。共享内存中的内容往往是在解除映射时才写回

文件的。因此,采用共享内存的通信方式效率是非常高的。

Linux

2.2.x

内核支持多种共享内存方式,如

mmap()

系统调用,

Posix

共享内存,以及系统

V

共享内存。

linux

发行版本如

Redhat 8.0

支持

mmap()

系统调用及系统

V

共享内存,但还没实现

Posix

共享内存,本文将主要介绍

mmap()

系统调用及系统

V

共享内存

API

的原理及应用。

一、内核怎样保证各个进程寻址到同一个共享内存区域的内存页面

1

page cache

swap cache

中页面的区分:一个被访问文件的物理页面都驻留在

page cache

swap cache

中,一个页面的所有信息由

struct page

来描述。

struct page

中有一个域为指针

mapping

,它指向一个

struct address_space

类型结构。

page cache

swap cache

中的所有页面就是根据

address_space

结构以及一个偏移量来区分的。

2

、文件与

address_space

结构的对应:一个具体的文件在打开后,内核会在内存中为之建立一个

struct inode

结构,其中的

i_mapping

域指向一个

address_space

结构。这样,一个文件就对应一个

address_space

结构,一个

address_space

与一个偏移量能够确定一个

page cache

swap cache

中的一个页面。因此,当要寻址某个数据时,很容易根据给定的文件及数据在文件内的偏移量而找到相应的页面。

3

、进程调用

mmap()

时,只是在进程空间内新增了一块相应大小的缓冲区,并设置了相应的访问标识,但并没有建立进程空间到物理页面的映射。因此,第一次访问该空间时,会引发一个缺页异常。

4

、对于共享内存映射情况,缺页异常处理程序首先在

swap cache

中寻找目标页(符合

address_space

以及偏移量的物理页),如果找到,则直接返回地址;如果没有找到,则判断该页是否在交换区

(swap area)

,如果在,则执行一个换入操作;如果上述两种情况都不满足,处理程序将分配新的物理页面,并把它插入到

page cache

中。进程最终将更新进程页表。

注:对于映射普通文件情况(非共享映射),缺页异常处理程序首先会在

page cache

中根据

address_space

以及数据偏移量寻找相应的页面。如果没有找到,则说明文件数据还没有读入内存,处理程序会从磁盘读入相应的页面,并返回相应地址,同时,进程页表也会更新。

5

、所有进程在映射同一个共享内存区域时,情况都一样,在建立线性地址与物理地址之间的映射之后,不论进程各自的返回地址如何,实际访问的必然是同一个共享内存区域对应的物理页面。

注:一个共享内存区域可以看作是特殊文件系统

shm

中的一个文件,

shm

的安装点在交换区上。

上面涉及到了一些数据结构,围绕数据结构理解问题会容易一些。

二、

mmap()

及其相关系统调用

mmap()

系统调用使得进程之间通过映射同一个普通文件实现共享内存。普通文件被映射到进程地址空间后,进程可以向访问普通内存一样对文件进行访问,不必再调用

read()

write

()等操作。

注:实际上,

mmap()

系统调用并不是完全为了用于共享内存而设计的。它本身提供了不同于一般对普通文件的访问方式,进程可以像读写内存一样对普通文件的操作。而

Posix

或系统

V

的共享内存

IPC

则纯粹用于共享目的,当然

mmap()

实现共享内存也是其主要应用之一。

1

mmap()

系统调用形式如下:

void* mmap ( void * addr , size_t len , int prot , int flags , int fd , off_t offset )

参数

fd

为即将映射到进程空间的文件描述字,一般由

open()

返回,同时,

fd

可以指定为

-1

,此时须指定

flags

参数中的

MAP_ANON

,表明进行的是匿名映射(不涉及具体的文件名,避免了文件的创建及打开,很显然只能用于具有亲缘关系的进程间通信)。

len

是映射到调用进程地址空间的字节数,它从被映射文件开头

offset

个字节开始算起。

prot

参数指定共享内存的访问权限。可取如下几个值的或:

PROT_READ

(可读)

, PROT_WRITE

(可写)

, PROT_EXEC

(可执行)

, PROT_NONE

(不可访问)。

flags

由以下几个常值指定:

MAP_SHARED , MAP_PRIVATE , MAP_FIXED

,其中,

MAP_SHARED , MAP_PRIVATE

必选其一,而

MAP_FIXED

则不推荐使用。

offset

参数一般设为

0

,表示从文件头开始映射。参数

addr

指定文件应被映射到进程空间的起始地址,一般被指定一个空指针,此时选择起始地址的任务留给内核来完成。函数的返回值为最后文件映射到进程空间的地址,进程可直接操作起始地址为该值的有效地址。这里不再详细介绍

mmap()

的参数,读者可参考

mmap()

手册页获得进一步的信息。

2

、系统调用

mmap()

用于共享内存的两种方式:

(

1

)使用普通文件提供的内存映射:适用于任何进程之间;此时,需要打开或创建一个文件,然后再调用

mmap()

;典型调用代码如下:

fd=open(name, flag, mode);

if(fd<0)

...

ptr=mmap(NULL, len , PROT_READ|PROT_WRITE, MAP_SHARED , fd , 0);

通过

mmap()

实现共享内存的通信方式有许多特点和要注意的地方,我们将在范例中进行具体说明。

(

2

)使用特殊文件提供匿名内存映射:适用于具有亲缘关系的进程之间;由于父子进程特殊的亲缘关系,在父进程中先调用

mmap()

,然后调用

fork()

。那么在调用

fork()

之后,子进程继承父进程匿名映射后的地址空间,同样也继承

mmap()

返回的地址,这样,父子进程就可以通过映射区域进行通信了。注意,这里不是一般的继承关系。一般来说,子进程单独维护从父进程继承下来的一些变量。而

mmap()

返回的地址,却由父子进程共同维护。

对于具有亲缘关系的进程实现共享内存最好的方式应该是采用匿名内存映射的方式。此时,不必指定具体的文件,只要设置相应的标志即可,参见范例

2

3

、系统调用

munmap()

int munmap( void * addr, size_t len )

该调用在进程地址空间中解除一个映射关系,

addr

是调用

mmap()

时返回的地址,

len

是映射区的大小。当映射关系解除后,对原来映射地址的访问将导致段错误发生。

4

、系统调用

msync()

int msync ( void * addr , size_t len, int flags)

一般说来,进程在映射空间的对共享内容的改变并不直接写回到磁盘文件中,往往在调用

munmap

()后才执行该操作。可以通过调用

msync()

实现磁盘上文件内容与共享内存区的内容一致。

三、

mmap()

范例

下面将给出使用

mmap()

的两个范例:范例

1

给出两个进程通过映射普通文件实现共享内存通信;范例

2

给出父子进程通过匿名映射实现共享内存。系统调用

mmap()

有许多有趣的地方,下面是通过

mmap

()映射普通文件实现进程间的通信的范例,我们通过该范例来说明

mmap()

实现共享内存的特点及注意事项。

范例

1

:两个进程通过映射普通文件实现共享内存通信

范例

1

包含两个子程序:

map_normalfile1.c

map_normalfile2.c

。编译两个程序,可执行文件分别为

map_normalfile1

map_normalfile2

。两个程序通过命令行参数指定同一个文件来实现共享内存方式的进程间通信。

map_normalfile2

试图打开命令行参数指定的一个普通文件,把该文件映射到进程的地址空间,并对映射后的地址空间进行写操作。

map_normalfile1

把命令行参数指定的文件映射到进程地址空间,然后对映射后的地址空间执行读操作。这样,两个进程通过命令行参数指定同一个文件来实现共享内存方式的进程间通信。

下面是两个程序代码:

/*-------------map_normalfile1.c-----------*/

#include

#include

#include

#include

typedef struct{

char name[4];

intage;

}people;

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,00777);

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++)

{

temp += 1;

memcpy( ( *(p_map+i) ).name, &temp,2 );

( *(p_map+i) ).age = 20+i;

}

printf(" initialize over \n ")

sleep(10);

munmap( p_map, sizeof(people)*10 );

printf( "umap ok \n" );

}

/*-------------map_normalfile2.c-----------*/

#include

#include

#include

#include

typedef struct{

char name[4];

intage;

}people;

main(int argc, char** argv)// map a normal file as shared mem:

{

int fd,i;

people *p_map;

fd=open( argv[1],O_CREAT|O_RDWR,00777 );

p_map = (people*)mmap(NULL,sizeof(people)*10,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);

for(i = 0;i<10;i++)

{

printf( "name: %s age %d;\n",(*(p_map+i)).name, (*(p_map+i)).age );

}

munmap( p_map,sizeof(people)*10 );

}

map_normalfile1.c

首先定义了一个

people

数据结构,(在这里采用数据结构的方式是因为,共享内存区的数据往往是有固定格式的,这由通信的各个进程决定,采用结构的方式有普遍代表性)。

map_normfile1

首先打开或创建一个文件,并把文件的长度设置为

5

people

结构大小。然后从

mmap()

的返回地址开始,设置了

10

people

结构。然后,进程睡眠

10

秒钟,等待其他进程映射同一个文件,最后解除映射。

map_normfile2.c

只是简单的映射一个文件,并以

people

数据结构的格式从

mmap()

返回的地址处读取

10

people

结构,并输出读取的值,然后解除映射。

分别把两个程序编译成可执行文件

map_normalfile1

map_normalfile2

后,在一个终端上先运行

./map_normalfile2 /tmp/test_shm

,程序输出结果如下:

initialize over

umap ok

map_normalfile1

输出

initialize over

之后,输出

umap ok

之前,在另一个终端上运行

map_normalfile2 /tmp/test_shm

,将会产生如下输出

(

为了节省空间,输出结果为稍作整理后的结果

)

name: bage 20;name: cage 21;name: dage 22;name: eage 23;name: fage 24;

name: gage 25;name: hage 26;name: Iage 27;name: jage 28;name: kage 29;

map_normalfile1

输出

umap ok

后,运行

map_normalfile2

则输出如下结果:

name: bage 20;name: cage 21;name: dage 22;name: eage 23;name: fage 24;

name:age 0;name:age 0;name:age 0;name:age 0;name:age 0;

从程序的运行结果中可以得出的结论

1

最终被映射文件的内容的长度不会超过文件本身的初始大小,即映射不能改变文件的大小;

2

可以用于进程通信的有效地址空间大小大体上受限于被映射文件的大小,但不完全受限于文件大小。打开文件被截短为

5

people

结构大小,而在

map_normalfile1

中初始化了

10

people

数据结构,在恰当时候(

map_normalfile1

输出

initialize over

之后,输出

umap ok

之前)调用

map_normalfile2

会发现

map_normalfile2

将输出全部

10

people

结构的值,后面将给出详细讨论。

注:在

linux

中,内存的保护是以页为基本单位的,即使被映射文件只有一个字节大小,内核也会为映射分配一个页面大小的内存。当被映射文件小于一个页面大小时,进程可以对从

mmap()

返回地址开始的一个页面大小进行访问,而不会出错;但是,如果对一个页面以外的地址空间进行访问,则导致错误发生,后面将进一步描述。因此,可用于进程间通信的有效地址空间大小不会超过文件大小及一个页面大小的和。

3

文件一旦被映射后,调用

mmap()

的进程对返回地址的访问是对某一内存区域的访问,暂时脱离了磁盘上文件的影响。所有对

mmap()

返回地址空间的操作只在内存中有意义,只有在调用了

munmap()

后或者

msync()

时,才把内存中的相应内容写回磁盘文件,所写内容仍然不能超过文件的大小。

范例

2

:父子进程通过匿名映射实现共享内存

#include

#include

#include

#include

typedef struct{

char name[4];

intage;

}people;

main(int argc, char** argv)

{

int i;

people *p_map;

char temp;

p_map=(people*)mmap(NULL,sizeof(people)*10,PROT_READ|PROT_WRITE,MAP_SHARED|MAP_ANONYMOUS,-1,0);

if(fork() == 0)

{

sleep(2);

for(i = 0;i<5;i++)

printf("child read: the %d people's age is %d\n",i+1,(*(p_map+i)).age);

(*p_map).age = 100;

munmap(p_map,sizeof(people)*10); //

实际上,进程终止时,会自动解除映射。

exit();

}

temp = 'a';

for(i = 0;i<5;i++)

{

temp += 1;

memcpy((*(p_map+i)).name, &temp,2);

(*(p_map+i)).age=20+i;

}

sleep(5);

printf( "parent read: the first people,s age is %d\n",(*p_map).age );

printf("umap\n");

munmap( p_map,sizeof(people)*10 );

printf( "umap ok\n" );

}

考察程序的输出结果,体会父子进程匿名共享内存:

child read: the 1 people's age is 20

child read: the 2 people's age is 21

child read: the 3 people's age is 22

child read: the 4 people's age is 23

child read: the 5 people's age is 24

parent read: the first people,s age is 100

umap

umap ok

四、对

mmap()

返回地址的访问

前面对范例运行结构的讨论中已经提到,

linux

采用的是页式管理机制。对于用

mmap()

映射普通文件来说,进程会在自己的地址空间新增一块空间,空间大小由

mmap()

len

参数指定,注意,进程并不一定能够对全部新增空间都能进行有效访问。进程能够访问的有效地址大小取决于文件被映射部分的大小。简单的说,能够容纳文件被映射部分大小的最少页面个数决定了进程从

mmap()

返回的地址开始,能够有效访问的地址空间大小。超过这个空间大小,内核会根据超过的严重程度返回发送不同的信号给进程。可用如下图示说明:

6.gif

注意:文件被映射部分而不是整个文件决定了进程能够访问的空间大小,另外,如果指定文件的偏移部分,一定要注意为页面大小的整数倍。下面是对进程映射地址空间的访问范例:

#include

#include

#include

#include

typedef struct{

char name[4];

intage;

}people;

main(int argc, char** argv)

{

int fd,i;

int pagesize,offset;

people *p_map;

pagesize = sysconf(_SC_PAGESIZE);

printf("pagesize is %d\n",pagesize);

fd = open(argv[1],O_CREAT|O_RDWR|O_TRUNC,00777);

lseek(fd,pagesize*2-100,SEEK_SET);

write(fd,"",1);

offset = 0;//

此处

offset = 0

编译成版本

1

offset = pagesize

编译成版本

2

p_map = (people*)mmap(NULL,pagesize*3,PROT_READ|PROT_WRITE,MAP_SHARED,fd,offset);

close(fd);

for(i = 1; i<10; i++)

{

(*(p_map+pagesize/sizeof(people)*i-2)).age = 100;

printf("access page %d over\n",i);

(*(p_map+pagesize/sizeof(people)*i-1)).age = 100;

printf("access page %d edge over, now begin to access page %d\n",i, i+1);

(*(p_map+pagesize/sizeof(people)*i)).age = 100;

printf("access page %d over\n",i+1);

}

munmap(p_map,sizeof(people)*10);

}

如程序中所注释的那样,把程序编译成两个版本,两个版本主要体现在文件被映射部分的大小不同。文件的大小介于一个页面与两个页面之间(大小为:

pagesize*2-99

),版本

1

的被映射部分是整个文件,版本

2

的文件被映射部分是文件大小减去一个页面后的剩余部分,不到一个页面大小

(

大小为:

pagesize-99)

。程序中试图访问每一个页面边界,两个版本都试图在进程空间中映射

pagesize*3

的字节数。

版本

1

的输出结果如下:

pagesize is 4096

access page 1 over

access page 1 edge over, now begin to access page 2

access page 2 over

access page 2 over

access page 2 edge over, now begin to access page 3

Bus error//

被映射文件在进程空间中覆盖了两个页面,此时,进程试图访问第三个页面

版本

2

的输出结果如下:

pagesize is 4096

access page 1 over

access page 1 edge over, now begin to access page 2

Bus error//

被映射文件在进程空间中覆盖了一个页面,此时,进程试图访问第二个页面

结论:采用系统调用

mmap()

实现进程间通信是很方便的,在应用层上接口非常简洁。内部实现机制区涉及到了

linux

存储管理以及文件系统等方面的内容,可以参考一下相关重要数据结构来加深理解。在本专题的后面部分,将介绍系统

v

共享内存的实现。

共享内存(下)

在共享内存(上)中,主要围绕着系统调用

mmap()

进行讨论的,本部分将讨论系统

V

共享内存,并通过实验结果对比来阐述两者的异同。系统

V

共享内存指的是把所有共享数据放在共享内存区域(

IPC shared memory region

),任何想要访问该数据的进程都必须在本进程的地址空间新增一块内存区域,用来映射存放共享数据的物理内存页面。

系统调用

mmap()

通过映射一个普通文件实现共享内存。系统

V

则是通过映射特殊文件系统

shm

中的文件实现进程间的共享内存通信。也就是说,每个共享内存区域对应特殊文件系统

shm

中的一个文件(这是通过

shmid_kernel

结构联系起来的),后面还将阐述。

1

、系统

V

共享内存原理

进程间需要共享的数据被放在一个叫做

IPC

共享内存区域的地方,所有需要访问该共享区域的进程都要把该共享区域映射到本进程的地址空间中去。系统

V

共享内存通过

shmget

获得或创建一个

IPC

共享内存区域,并返回相应的标识符。内核在保证

shmget

获得或创建一个共享内存区,初始化该共享内存区相应的

shmid_kernel

结构注同时,还将在特殊文件系统

shm

中,创建并打开一个同名文件,并在内存中建立起该文件的相应

dentry

inode

结构,新打开的文件不属于任何一个进程(任何进程都可以访问该共享内存区)。所有这一切都是系统调用

shmget

完成的。

注:每一个共享内存区都有一个控制结构

struct shmid_kernel

shmid_kernel

是共享内存区域中非常重要的一个数据结构,它是存储管理和文件系统结合起来的桥梁,定义如下:

struct shmid_kernel /* private to the kernel */

{

struct kern_ipc_permshm_perm;

struct file *shm_file;

intid;

unsigned longshm_nattch;

unsigned longshm_segsz;

time_tshm_atim;

time_tshm_dtim;

time_tshm_ctim;

pid_tshm_cprid;

pid_tshm_lprid;

};

该结构中最重要的一个域应该是

shm_file

,它存储了将被映射文件的地址。每个共享内存区对象都对应特殊文件系统

shm

中的一个文件,一般情况下,特殊文件系统

shm

中的文件是不能用

read()

write()

等方法访问的,当采取共享内存的方式把其中的文件映射到进程地址空间后,可直接采用访问内存的方式对其访问。

这里我们采用

[1]

中的图表给出与系统

V

共享内存相关数据结构:

正如消息队列和信号灯一样,内核通过数据结构

struct ipc_ids shm_ids

维护系统中的所有共享内存区域。上图中的

shm_ids.entries

变量指向一个

ipc_id

结构数组,而每个

ipc_id

结构数组中有个指向

kern_ipc_perm

结构的指针。到这里读者应该很熟悉了,对于系统

V

共享内存区来说,

kern_ipc_perm

的宿主是

shmid_kernel

结构,

shmid_kernel

是用来描述一个共享内存区域的,这样内核就能够控制系统中所有的共享区域。同时,在

shmid_kernel

结构的

file

类型指针

shm_file

指向文件系统

shm

中相应的文件,这样,共享内存区域就与

shm

文件系统中的文件对应起来。

在创建了一个共享内存区域后,还要将它映射到进程地址空间,系统调用

shmat()

完成此项功能。由于在调用

shmget()

时,已经创建了文件系统

shm

中的一个同名文件与共享内存区域相对应,因此,调用

shmat()

的过程相当于映射文件系统

shm

中的同名文件过程,原理与

mmap()

大同小异。

7.jpg

2

、系统

V

共享内存

API

对于系统

V

共享内存,主要有以下几个

API

shmget()

shmat()

shmdt()

shmctl()

#include

#include

shmget

()用来获得共享内存区域的

ID

,如果不存在指定的共享区域就创建相应的区域。

shmat()

把共享内存区域映射到调用进程的地址空间中去,这样,进程就可以方便地对共享区域进行访问操作。

shmdt()

调用用来解除进程对共享内存区域的映射。

shmctl

实现对共享内存区域的控制操作。这里我们不对这些系统调用作具体的介绍,读者可参考相应的手册页面,后面的范例中将给出它们的调用方法。

注:

shmget

的内部实现包含了许多重要的系统

V

共享内存机制;

shmat

在把共享内存区域映射到进程空间时,并不真正改变进程的页表。当进程第一次访问内存映射区域访问时,会因为没有物理页表的分配而导致一个缺页异常,然后内核再根据相应的存储管理机制为共享内存映射区域分配相应的页表。

3

、系统

V

共享内存限制

/proc/sys/kernel/

目录下,记录着系统

V

共享内存的一下限制,如一个共享内存区的最大字节数

shmmax

,系统范围内最大共享内存区标识符数

shmmni

等,可以手工对其调整,但不推荐这样做。

[2]

中,给出了这些限制的测试方法,不再赘述。

4

、系统

V

共享内存范例

本部分将给出系统

V

共享内存

API

的使用方法,并对比分析系统

V

共享内存机制与

mmap()

映射普通文件实现共享内存之间的差异,首先给出两个进程通过系统

V

共享内存通信的范例:

/***** testwrite.c *******/

#include

#include

#include

#include

typedef struct{

char name[4];

int age;

} people;

main(int argc, char** argv)

{

int shm_id,i;

key_t key;

char temp;

people *p_map;

char* name = "/dev/shm/myshm2";

key = ftok(name,0);

if(key==-1)

perror("ftok error");

shm_id=shmget(key,4096,IPC_CREAT);

if(shm_id==-1)

{

perror("shmget error");

return;

}

p_map=(people*)shmat(shm_id,NULL,0);

temp='a';

for(i = 0;i<10;i++)

{

temp+=1;

memcpy((*(p_map+i)).name,&temp,1);

(*(p_map+i)).age=20+i;

}

if(shmdt(p_map)==-1)

perror(" detach error ");

}

/********** testread.c ************/

#include

#include

#include

#include

typedef struct{

char name[4];

int age;

} people;

main(int argc, char** argv)

{

int shm_id,i;

key_t key;

people *p_map;

char* name = "/dev/shm/myshm2";

key = ftok(name,0);

if(key == -1)

perror("ftok error");

shm_id = shmget(key,4096,IPC_CREAT);

if(shm_id == -1)

{

perror("shmget error");

return;

}

p_map = (people*)shmat(shm_id,NULL,0);

for(i = 0;i<10;i++)

{

printf( "name:%s\n",(*(p_map+i)).name );

printf( "age %d\n",(*(p_map+i)).age );

}

if(shmdt(p_map) == -1)

perror(" detach error ");

}

testwrite.c

创建一个系统

V

共享内存区,并在其中写入格式化数据;

testread.c

访问同一个系统

V

共享内存区,读出其中的格式化数据。分别把两个程序编译为

testwrite

testread

,先后执行

./testwrite

./testread

./testread

输出结果如下:

name: bage 20;name: cage 21;name: dage 22;name: eage 23;name: fage 24;

name: gage 25;name: hage 26;name: Iage 27;name: jage 28;name: kage 29;

通过对试验结果分析,对比系统

V

mmap()

映射普通文件实现共享内存通信,可以得出如下结论:

1

系统

V

共享内存中的数据,从来不写入到实际磁盘文件中去;而通过

mmap()

映射普通文件实现的共享内存通信可以指定何时将数据写入磁盘文件中。注:前面讲到,系统

V

共享内存机制实际是通过映射特殊文件系统

shm

中的文件实现的,文件系统

shm

的安装点在交换分区上,系统重新引导后,所有的内容都丢失。

2

系统

V

共享内存是随内核持续的,即使所有访问共享内存的进程都已经正常终止,共享内存区仍然存在(除非显式删除共享内存),在内核重新引导之前,对该共享内存区域的任何改写操作都将一直保留。

3

通过调用

mmap()

映射普通文件进行进程间通信时,一定要注意考虑进程何时终止对通信的影响。而通过系统

V

共享内存实现通信的进程则不然。注:这里没有给出

shmctl

的使用范例,原理与消息队列大同小异。

Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1006744

posted on 2007-08-11 01:00 旅途 阅读(1544) 评论(0)  编辑 收藏 引用 所属分类: Linux开发

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/539916.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

冲刺NO.8

Alpha冲刺第八天 站立式会议 项目进展 项目稳步进行&#xff0c;项目的基础部分如基本信息管理&#xff0c;信用信息管理等部分已相对比较完善。 问题困难 技术困难在短期内很难发生质的变化&#xff0c;而本项目由于选择了队员不太熟悉的程序框架&#xff0c;所以所以项目的交…

linux由众多微内核组成,什么是linux

大家对Linux这个词比较陌生吧&#xff0c;那么Linux是什么呢&#xff1f;Linux是什么Linux是一种自由和开放源码的类Unix操作系统。目前存在着许多不同的Linux&#xff0c;但它们都使用了Linux内核。Linux可安装在各种计算机硬件设备中&#xff0c;从手机、平板电脑、路由器和视…

SqlServer2008备份与还原(完整图示版)

一、备份 1、在需要备份的数据库上&#xff0c;右键——任务——备份&#xff0c;如下&#xff1a; 2、选择备份到哪个路径和备份名字&#xff1a; 点击“添加”&#xff0c;如下&#xff0c; 3、上面点击“确定”后&#xff0c;回到第一个页面&#xff0c;选中刚才添加的路径和…

Jquery mobile问题总汇

转载&#xff1a;http://www.wglong.com/main/artical!details?id4#q6 1页面缩放显示问题 问题描述&#xff1a; 页面似乎被缩小了&#xff0c;屏幕太宽了。 解决办法&#xff1a; 在head标签内加入&#xff1a; <meta name"viewport" content"widthdevice…

linux环境OpenRASP使用教程,集成openRASP与攻击测试

1.介绍openRASP是一个百度的安全框架&#xff0c;将其集成到我们的web项目中&#xff0c;就像是给web项目安装了一款“安全管家”的软件&#xff0c;它可以检测到攻击&#xff0c;并进行拦截。2.集成openRASP到项目中openRASP针对不同的服务器&#xff0c;提供了不同的安装方法…

ExtJs 备忘录(4)—— Form表单(四) [ 数据提交 ]

一、截图和示例共用Ext.FormPanel1.1  截图由于本文主要关注的是表单提交的几种方式&#xff0c;所以仅用了一个表单项以便于测试和减少示例代码。1.2  示例共用Ext.FormPanel <script type"text/javascript">Ext.onReady(function() { Ext.Qui…

struts2访问jsp页面404

问题描述 在搭建struts2环境的时候&#xff0c;拷贝了web.xml&#xff0c;拷贝了struts.xml&#xff0c;拷贝了jar包。运行&#xff0c;正常&#xff0c;访问jsp页面&#xff0c;报404错误。 web.xml <?xml version"1.0" encoding"UTF-8"?> <w…

centos7定制linux镜像,自定制Centos7.3系统镜像(ISO)

本文主要介绍如何根据官方的Centos镜像文件&#xff0c;在保留原有默认安装的RPM包的基础下&#xff0c;添加自己所需要的RPM包的&#xff0c;最终生成一个自定制版的ISO&#xff0c;节省了宝贵的时间并确保了安装的定制性。对于其他没有介绍的修改&#xff0c;后续在实践中会进…

调用打开另外一个APK

2019独角兽企业重金招聘Python工程师标准>>> Intent mIntent new Intent(); mIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); ComponentName comp new ComponentName("com.mm.android.direct.gdmssphoneLite", "com.mm.android.direct.gdmsspho…

Jquery Mobile dialog的生命周期

JQuery Mobile对htm5的移动开发绝对是个好用的东西&#xff0c;今天简单谈谈JQuery Mobile中的dialog的使用。 1.对话框的弹出。 2.对话框的生命周期。 3.对话框内事件的注册。 1&#xff09;第一个问题&#xff1a;对话框的弹出。 如果要弹出一个对话框&#xff0c;可以在页面…

c语言源程序最多可能由组成,一个C语言源程序由若干函数组成,其中至少应含有一个()。...

个C语言源由I am interested in the training course, which _____ at Hilton Hotel in Beijing from March 8 to 12, 2018.程序成“万物莫不有对”体现了中国传统哲学的矛盾观。用户在进行产品的三维设计时&#xff0c;干函可以采用以下( )的设计方法。数组少意识是人脑对客观…

Citrix Netscaler版本管理和选择

Citrix Netscaler版本管理和选择 来源 http://blog.51cto.com/caojin/1898164 随着Citrix Netscaler的快速发展&#xff0c;有很多人在维护设备时经常搞不懂Netscaler软件版本是如何查看和选择&#xff0c;当前软件是否需要升级&#xff0c;当前软件是否稳定等。基于以上问题&a…

oracle实例与数据库

一、名称 Oracle数据库服务器。单叫数据库或服务器都不全面。 二、组成 oracle数据库服务器由二部份组成&#xff1a;实例和数据库 实例&#xff1a; 可理解为对象&#xff0c;看不见。数据库&#xff1a; 理解为类&#xff0c;看得见的&#xff0c;E:\app\Administrator\…

WEB前端面试题汇总整理01

1.JS找字符串中出现最多的字符 例如&#xff1a;求字符串nininihaoa中出现次数最多字符 var str "nininihaoa"; var o {}; for (var i 0, length str.length; i < length; i) {var char str.charAt(i);if (o[char]) {o[char]; //次数加1} else {o[char] 1;…

c语言 文件游程统计,游程 码表 如何形成

游程 码表 如何形成求一个程序 将下表用huffman 树存储表示用传统的霍夫曼建立的树 好像不能形成此码表。我感觉应该有一个特等的算法。我也试了好几种方法&#xff0c;感觉都不行&#xff0c;希望大家给点建议。(码表 要利于编码和解码)(部分码表)白游程 码子 黑游程 码子(长…

2.ORM思想

一、ORM思想介绍 orm是一种思想&#xff0c;是dao层代码的一种编写思想。其中&#xff1a;o代表Object&#xff0c;java对象&#xff1b;r代表Relation&#xff0c;关系型数据库&#xff1b;m代表Map&#xff0c;java对象和关系型数据库表的映射关系。该思想主张&#xff1a;1、…

android 字体颜色选择,Android中颜色选择器和改变字体颜色的实例教程

易采站长站为您分析Android中颜色选择器和改变字体颜色的实例教程,其中改变字体颜色用到了ColorPicker颜色选择器,需要的朋友可以参考下1.构建一张七彩图:我们经常看到这种样子的颜色选择器吧..然后其实右边的亮度选择是:这样我们的代码就可以进行啦...// 创建七彩图片private …

新版vue-cli搭建多页面应用

2019独角兽企业重金招聘Python工程师标准>>> 折腾了好久&#xff0c;终于把坑踩完了&#xff0c;废话不多说&#xff0c;上教程~ github地址&#xff1a;https://github.com/guolihuaGitHub/vue-cli-multipage 另外推荐一下我另一篇博客&#xff0c;我觉得这篇好用&…

2017 Google 开发者大会直播入口

今年&#xff0c;Google 开发者大会再度来袭&#xff01;大会将于 12 月 13 日和 14 日在上海举办&#xff0c;主题涵盖机器学习 (Machine Learning)、Android、移动网络&#xff08;Mobile Web)、TensorFlow、Firebase、云服务 (Cloud)、AR/VR、设计 (Design) 以及更多开发者相…

日志组件slf4j介绍及配置详解

2019独角兽企业重金招聘Python工程师标准>>> 原文出自 1 基本介绍 每一个Java程序员都知道日志对于任何一个Java应用程序尤其是服务端程序是至关重要的&#xff0c;而很多程序员也已经熟悉各种不同的日志库&#xff0c;如java.util.logging、Apache log4j、logback。…