目录
linux进程间通信的主要方式
存储映射
存储映射相关API函数
mmap()
mummap()
匿名映射
存储映射注意事项
存储映射相关例程
例程分析
例程分享
共享内存
共享内存相关API函数
shmget()
shmat()
shmdt()
shmctl()
共享内存相关例程
例程分析
例程分享
存储映射与共享内存的关系
此外,Linux也支持其他的一些IPC方式,比如信号(Signal)、文件(File)、命名管道(Named pipe)等。这些IPC机制为进程间通信提供了不同的选择,它们各有特点,使用场景也不尽相同。合理选用可以提高多进程协同工作的效率。
在Linux中,存储映射是一种将文件或设备映射到进程地址空间的机制。这意味着进程可以使用指针(地址)直接访问文件或设备的内容,就好像它是在内存中一样。存储映射通常使用mmap
系统调用来实现。
存储映射的一些常见用途包括:
使用mmap
系统调用创建存储映射时,需要指定要映射的文件描述符(如果是文件映射),映射的起始地址,映射的长度,映射的权限等参数。mmap
调用返回的是映射区域的起始地址,进程可以通过该地址直接访问映射的内容。
存储映射提供了一种高效的I/O操作方式,并且可以简化进程间通信的实现。但是,需要注意的是,使用存储映射时需要谨慎处理内存访问权限、同步和互斥等问题,以避免潜在的安全风险和数据一致性问题。
功能
将文件或设备映射到进程地址空间
头文件
sys/mman.h
原型
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(无权限)等。
flags:指定映射的选项,例如MAP_SHARED表示映射区域可以共享,MAP_PRIVATE表示映射区域是私有的。
fd:指定要映射的文件描述符,如果是匿名映射则设为-1。
offset:指定文件的偏移量,对于文件映射,表示从文件的哪个位置开始映射;对于匿名映射,通常设为0。
返回值
成功 映射区域的起始地址
失败 返回MAP_FAILED宏
使用mmap()函数可以实现如下功能:
将文件映射到内存中,实现高效的文件I/O操作。
实现共享内存,允许多个进程访问同一块物理内存空间。
实现匿名映射,用于进程间通信或共享数据。
需要注意的是,在使用mmap()函数时,需要注意对映射区域的访问权限、同步和互斥的处理,以避免潜在的安全风险和数据一致性问题。同时,在不再需要映射时,应该使用munmap()函数来解除映射。
功能
取消共享映射区
头文件
sys/mman.h
原型
int munmap(void *addr, size_t length)
参数
addr:指定要解除映射的起始地址,通常是mmap()函数返回的映射区域的起始地址。
length:指定要解除映射的长度,单位是字节。
返回值
成功 0
失败 -1
在解除映射时,应该确保不再有任何指针指向该映射区域,以免产生悬空指针的问题。
通过使用我们发现,使用映射区来完成文件读写非常方便,父子间进程通信也比较容易,但缺陷时每次创建映射区一定要依赖一个文件才能实现,通常要建立映射区要open一个temp文件,创建好了再unlink,close掉,比较麻烦,可以直接使用匿名映射俩代替,起始Linux系统给我们提供了创建匿名映射区的方法,无需依赖一个文件即可创建映射区,同样需要借助标志位参数flags来指定。
使用 MAP_ANONYMOUS(或MAP_ANON)如:
int *p = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0)
其中 size 表示大小,可依实际需求填写
需要注意的是,MAP_ANONUMOUS和MAP_ANON都是Linux操作系统特有的宏,再类Unix系统中如果没有该宏定义,可使用以下两步完成宏定义
fd = open("/dev/zero", O_RDWR);
p = mmap(NULL, size, PROT_WRITE, MMAP_SHARED, FD, 0);
这个例程展示了使用mmap()
函数进行内存映射的方式进行进程间通信。其中,进程1将一个struct student
类型的数据写入到共享内存中,而进程2则从共享内存中读取并打印出这个数据。
mmap_w.c:
struct student
类型的变量stu
,并初始化其成员。fd
,用于后续的内存映射。ftruncate()
函数设置文件大小为sizeof(stu)
,以确保共享内存的大小与数据结构的大小一致。mmap()
函数将共享内存映射到进程的地址空间中,指定了PROT_READ|PROT_WRITE
权限和MAP_SHARED
标志,以允许读写共享内存。fd
。memcpy()
函数将stu
复制到共享内存中,然后修改stu
的id
并等待2秒。mmap_r.c:
struct student
类型的变量stu
。fd
,用于后续的内存映射。mmap()
函数将共享内存映射到进程的地址空间中,指定了PROT_READ
权限和MAP_SHARED
标志,以允许读取共享内存。fd
。/*mmap_w.c*/
#include
#include
#include
#include
#include
#include
#include
struct student {
int id;
char name[256];
int age;
};
void sys_err(const char *str)
{
perror(str);
exit(1);
}
int main(int argc, char *argv[])
{
struct student stu = {1, "xiaoming", 18};
struct student *p;
int fd;
// fd = open("test_map", O_RDWR|O_CREAT|O_TRUNC, 0664);
fd = open("test_map", O_RDWR);
if (fd == -1)
sys_err("open error");
ftruncate(fd, sizeof(stu));
p = mmap(NULL, sizeof(stu), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
if (p == MAP_FAILED)
sys_err("mmap error");
close(fd);
while (1) {
memcpy(p, &stu, sizeof(stu));
stu.id++;
sleep(2);
}
munmap(p, sizeof(stu));
return 0;
}
/*mmap_r.c*/
#include
#include
#include
#include
#include
#include
#include
struct student {
int id;
char name[256];
int age;
};
void sys_err(const char *str)
{
perror(str);
exit(1);
}
int main(int argc, char *argv[])
{
struct student stu;
struct student *p;
int fd;
fd = open("test_map", O_RDONLY);
if (fd == -1)
sys_err("open error");
p = mmap(NULL, sizeof(stu), PROT_READ, MAP_SHARED, fd, 0);
if (p == MAP_FAILED)
sys_err("mmap error");
close(fd);
while (1) {
printf("id= %d, name=%s, age=%d\n", p->id, p->name, p->age);
usleep(10000);
}
munmap(p, sizeof(stu));
return 0;
}
需要注意的是,进程1和进程2都需要使用相同的文件描述符和文件名来进行内存映射,以确保它们使用的是同一个共享内存区域。此外,实际使用时需要注意进程间的同步和互斥问题,以确保数据的正确性。
在Linux中,进程间通信的一种常见方式是通过共享内存进行通信。共享内存允许多个进程访问同一块物理内存空间,从而实现进程间的数据共享。这种通信方式通常比管道或消息队列等方式更快,因为数据直接在内存中传递,而不需要进行内核空间和用户空间之间的数据拷贝。
在Linux中,使用共享内存进行进程间通信通常涉及以下步骤:
shmget
来创建一个新的共享内存段,或者获取一个已经存在的共享内存段的标识符。shmat
系统调用将共享内存段连接到进程的地址空间中,这样进程就可以访问共享内存中的数据。shmdt
系统调用将共享内存段从进程的地址空间中分禿。shmctl
系统调用来删除共享内存段,从而释放相关的系统资源。共享内存通信是一种高效的进程间通信方式,但也需要谨慎使用,因为共享内存的数据访问需要进程之间进行同步和互斥,以避免数据的不一致性和竞争条件。
功能
用于创建或获取一个共享内存标识符
头文件
#include
#include
原型
int shmget(key_t key, size_t size, int shmflg)
参数
key:共享内存的键值,用于标识共享内存对象。
size:共享内存的大小,单位是字节。
shmflg:创建或获取共享内存的标志位。
返回值
成功 返回 int 型共享内存标识符, 用来操作共享内存
失败 -1
功能
用于将共享内存附加到进程的地址空间中
头文件
#include
#include
原型
void *shmat(int shmid, const void *shmaddr, int shmflg);
参数
shmid:共享内存的标识符。
shmaddr:指定共享内存的附加地址,通常为NULL,表示由系统选择合适的地址。
shmflg:共享内存的附加标志位。
返回值
成功 共享内存的地址
失败
功能
结束映射 不释放共享内存
头文件
#include
#include
原型
int shmdt(const void *shmaddr);
参数
shmaddr:指定要分离的共享内存的起始地址
返回值
成功 0
失败 -1
功能
修改或删除共享内存,常用作删除共享内存
头文件
#include
#include
原型
int shmctl(int shmid, int cmd, struct shmid_ds *buf)
参数
int shmid shnget 返回值
int cmd 宏定义 删除 IPC_RMID
struct shmid_ds *buf 结构体指针,修改用
返回值
成功 0
失败 -1
在这个例程中,进程1创建了一个共享内存并向其中写入数据,而进程2则获取了这个共享内存并读取其中的数据。这样,两个不同的进程就通过共享内存实现了通信。需要注意的是,实际使用时需要考虑进程间的同步和互斥问题,以确保数据的正确性。
/*进程1:写入数据到共享内存*/
#include
#include
#include
#include
#include
#define SHM_SIZE 1024
int main() {
int shmid;
key_t key;
char *shmaddr;
// 生成共享内存的键值
key = ftok(".", 'S');
if (key == -1) {
perror("ftok");
exit(1);
}
// 创建共享内存
shmid = shmget(key, SHM_SIZE, IPC_CREAT | 0666);
if (shmid == -1) {
perror("shmget");
exit(1);
}
// 将共享内存附加到进程的地址空间
shmaddr = (char *)shmat(shmid, NULL, 0);
if (shmaddr == (char *)-1) {
perror("shmat");
exit(1);
}
// 在共享内存中写入数据
strcpy(shmaddr, "Hello, shared memory from Process 1!");
// 分离共享内存
if (shmdt(shmaddr) == -1) {
perror("shmdt");
exit(1);
}
return 0;
}
/*进程2:读取共享内存中的数据*/
#include
#include
#include
#include
#include
#define SHM_SIZE 1024
int main() {
int shmid;
key_t key;
char *shmaddr;
// 生成共享内存的键值
key = ftok(".", 'S');
if (key == -1) {
perror("ftok");
exit(1);
}
// 获取共享内存
shmid = shmget(key, SHM_SIZE, 0666);
if (shmid == -1) {
perror("shmget");
exit(1);
}
// 将共享内存附加到进程的地址空间
shmaddr = (char *)shmat(shmid, NULL, 0);
if (shmaddr == (char *)-1) {
perror("shmat");
exit(1);
}
// 从共享内存中读取数据并打印
printf("Message from shared memory in Process 2: %s\n", shmaddr);
// 分离共享内存
if (shmdt(shmaddr) == -1) {
perror("shmdt");
exit(1);
}
return 0;
}
共享内存和存储映射是两种不同的概念,但它们可以有一定的关联和联系。
共享内存用于实现多个进程之间共享数据的方式。它允许多个进程可以访问同一块物理内存空间,从而实现进程间的数据共享。在共享内存中,多个进程可以直接读写同一块内存区域,而无需进行复制或通过其他通信机制。
存储映射用于将文件或设备映射到进程的地址空间中,从而实现对文件内容或设备内存的直接访问。通过存储映射,进程可以将文件内容或设备内存映射到自己的地址空间中,然后通过对内存的读写操作来实现对文件或设备的访问。
共享内存和存储映射的关系在于,共享内存可以通过存储映射的方式实现。通过将共享内存区域映射到各个进程的地址空间中,多个进程可以直接访问同一块内存区域,从而实现数据的共享。在这种情况下,使用mmap()
函数可以将共享内存映射到进程的地址空间中,然后各个进程可以通过对映射区域的读写操作来实现共享数据的访问。
共享内存和存储映射是不同的概念,共享内存更侧重于进程间的数据共享,而存储映射更侧重于对文件或设备的访问。但在某些情况下,可以通过存储映射来实现共享内存的功能。