2 进程间通信相关概念
2.1 什么是进程间通信
Linux环境下,进程地址空间相互独立,每个进程各自有不同的用户地址空间。任何一个进程的全局变量在另一个进程中都看不到,所以进程和进程之间不能相互访问,要交换数据必须通过内核,在内核中开辟一块缓冲区,进程1把数据从用户空间拷到内核缓冲区,进程2再从内核缓冲区把数据读走,内核提供的这种机制称为进程间通信(IPC,InterProcess Communication)。
2.2 进程间通信的方式
在进程间完成数据传递需要借助操作系统提供特殊的方法,如:文件、管道、信号、共享内存、消息队列、套接字、命名管道等。随着计算机的蓬勃发展,一些方法由于自身设计缺陷被淘汰或者弃用。现今常用的进程间通信方式有:
3 管道-pipe
3.1管道的概念
管道就是 ***(输入到管道) | ***(从管道读出)
管道是一种最基本的IPC(进程间通信)机制,也称匿名管道,应用于有血缘关系的进程之间,完成数据传递。调用pipe函数即可创建一个管道。
有如下特质:
3.2管道的原理
[holo@holocom 0406]$ ulimit -a …… pipe size (512 bytes, -p) 8 ……
3.3管道的局限性
3.4创建管道-pipe函数
创建一个管道
int pipe(int fd[2]); //与int pipe(int *fd); 等价
若函数调用成功,fd[0]存放管道的读端,fd[1]存放管道的写端
函数调用成功返回读端和写端的文件描述符,其中fd[0]是读端, fd[1]是写端,向管道读写数据是通过使用这两个文件描述符进行的,读写管道的实质是操作内核缓冲区。
管道创建成功以后,创建该管道的进程(父进程)同时掌握着管道的读端和写端。如何实现父子进程间通信呢?
3.5父子进程使用管道通信
一个进程在由pipe()创建管道后,一般再fork一个子进程,然后通过管道实现父子进程间的通信(因此也不难推出,只要两个进程中存在血缘关系,这里的血缘关系指的是具有共同的祖先,都可以采用管道方式来进行通信)。父子进程间具有相同的文件描述符,且指向同一个管道pipe,其他没有关系的进程不能获得pipe()产生的两个文件描述符,也就不能利用同一个管道进行通信。
第一步:父进程创建管道(在fork之前)
第二步:父进程fork出子进程
第三步:父进程关闭fd[0](读),子进程关闭fd[1](写)
创建步骤总结:
3.6 管道练习
//1. 实现父子进程通信 [holo@holocom 0410]$ cat pipe.c #include#include #include #include #include #include int main() { //创建管道 int fd[2]; int ret = pipe(fd); //不是pipe(fd[2]); if(ret < 0) { perror("pipe error"); return -1; } //创建子进程 pid_t child_pid = fork(); if(child_pid < 0) { perror("fork error"); return -1; } else if(child_pid>0) //父进程关闭读端 { close(fd[0]); sleep(5); //验证read函数是阻塞的 write(fd[1] , "hello world" , strlen("hello world")); //write写满时阻塞 wait(NULL); //wait()是阻塞函数,回收子进程资源,确保子进程先退出 } else if(child_pid == 0) //子进程关闭写端 { close(fd[1]); char buf[64]; memset(buf , 0x00 , sizeof(buf)); //对数组初始化 int n = read(fd[0],buf , sizeof(buf)); //read没数据时阻塞,如果没写入数据,就会等待写入. printf("read over , n == [%d] , buf == [%s]\n",n,buf); } return 0; }
[holo@holocom 0410]$ ./pipe
//等待五秒
read over , n == [11] , buf == [hello world]
ps aux : 原来结果会写到标准输出(终端) ,更改为写到管道写端,使用dup2函数(可以指定第二个参数)(不可使用dup)
grep bash :原来从标准输入读, 从管道读端读 , 读到标准输出.
使用execlp函数和dup2函数
// 模拟ps aux | grep bash操作 #include#include #include #include #include #include int main() { //创建管道 int fd[2]; int ret = pipe(fd); if(ret < 0) { perror("pipe error"); return -1; } //创建子进程 pid_t child_pid = fork(); if(child_pid < 0) { perror("fork error"); return -1; } else if(child_pid>0) //父进程关闭读端 { close(fd[0]); dup2(fd[1],STDOUT_FILENO); execlp("ps" , "ps" , "aux" , NULL); perror("execlp error"); //异常处理,只有execlp函数执行失败后,才输出 //wait(NULL); //wait()是阻塞函数,确保子进程先退出 //不写wait函数也可以,因为1. 即使父进程先执行结束,子进程变为了孤儿进程,会被1号进程领养,结束后会释放进程资源 // 2. execlp执行成功后,就执行不到这里了. } else if(child_pid == 0) //子进程关闭写端 { //如果子进程先执行grep bash , 会阻塞等待 close(fd[1]); dup2(fd[0],STDIN_FILENO); execlp("grep","grep","--color=auto","bash",NULL); //执行execlp后,新的进程将替换数据段,代码段,栈,堆 //并且不会执行execlp后面的代码了。 //--color=auto :让bash变成红色,从ps aux | grep bash 的执行结果参考到的。 perror("execlp error"); return 0; }
[holo@holocom 0410]$ ./pipeps_aux
root 6511 0.0 0.0 115304 960 ? S 11:29 0:00 /bin/bash /usr/sbin/ksmtuned
holo 77697 0.0 0.0 72312 776 ? Ss 12:14 0:00 /usr/bin/ssh-agent /bin/sh -c exec -l /bin/bash -c "env GNOME_SHELL_SESSION_MODE=classic gnome-session --session gnome-classic"
holo 98736 0.0 0.1 116356 2968 pts/0 Ss 12:37 0:00 -bash
holo 99139 0.0 0.0 112712 972 pts/0 S+ 13:03 0:00 grep --color=auto bash
[holo@holocom 0410]$ ps aux | grep bash
root 6511 0.0 0.0 115304 960 ? S 11:29 0:00 /bin/bash /usr/sbin/ksmtuned
holo 77697 0.0 0.0 72312 776 ? Ss 12:14 0:00 /usr/bin/ssh-agent /bin/sh -c exec -l /bin/bash -c "env GNOME_SHELL_SESSION_MODE=classic gnome-session --session gnome-classic"
holo 98736 0.0 0.1 116356 2968 pts/0 Ss 12:37 0:00 -bash
holo 99141 0.0 0.0 112712 972 pts/0 S+ 13:03 0:00 grep --color=auto bash
使用execlp函数和dup2函数
父进程要调用waitpid函数完成对子进程的回收
// 模拟兄弟进程间 ps aux | grep bash操作 #include#include #include #include #include #include int main() { //创建管道 int fd[2]; int ret = pipe(fd); int child_pid; if(ret < 0) { perror("pipe error"); return -1; } //创建子进程 int i=0; int n=2; for(i=0;i 0) { if(WIFEXITED(status)) { printf("子进程正常退出,status == [%d] \n",WEXITSTATUS(status)); } else if(WIFSIGNALED(status)) { printf("子进程被信号[%d]杀死了",WTERMSIG(status)); } } } } if(i==0) //哥哥进程写 { close(fd[0]); //sleep(5); //验证read函数是阻塞的 dup2(fd[1],STDOUT_FILENO); execlp("ps" , "ps" , "aux" , NULL); perror("execlp error"); //异常处理,只有execlp函数执行失败后,才输出 close(fd[1]); //wait(NULL); //wait()是阻塞函数,确保子进程先退出 //不写wait函数也可以,因为即使父进程先执行结束,子进程变为了孤儿进程,会被1号进程领养,结束后会释放进程资源 } else if(i==1) //哥哥进程读 { printf("儿子:fpid==[%d],child_pid==[%d]\n",getppid(),getpid()); close(fd[1]); //关闭写端 dup2(fd[0],STDIN_FILENO); execlp("grep","grep","--color=auto","bash",NULL); //执行execlp后,新的进程将替换数据段,代码段,栈,堆 //并且不会执行execlp后面的代码了。 //--color=auto :让bash变成红色,从ps aux | grep bash 的执行结果参考到的。 perror("execlp error"); //char buf[64]; //memset(buf , 0x00 , sizeof(buf)); //对数组初始化 //int n = read(fd[0],buf , sizeof(buf)); //read没数据时阻塞,如果没写入数据,就会等待写入. //printf("read over , n == [%d] , buf == [%s]\n",n,buf); close(fd[0]); } return 0; }
[holo@holocom 0410]$ ./pipebrother
儿子:fpid==[66948],child_pid==[66950]
root 6561 0.0 0.0 115304 964 ? S 12:17 0:00 /bin/bash /usr/sbin/ksmtuned
holo 65714 0.0 0.1 116356 2956 pts/0 Ss 17:05 0:00 -bash
holo 65882 0.0 0.1 116356 2932 pts/1 Ss 17:05 0:00 -bash
holo 66682 0.1 0.0 113184 1620 ? Ss 17:07 0:00 bash -c while true; do sleep 1;head -v -n 8 /proc/meminfo; head -v -n 2 /proc/stat /proc/version /proc/uptime /proc/loadavg /proc/sys/fs/file-nr /proc/sys/kernel/hostname; tail -v -n 16 /proc/net/dev;echo '==> /proc/df <==';df -l;echo '==> /proc/who <==';who;echo '==> /proc/end <==';echo '##Moba##'; done
holo 66950 0.0 0.0 112712 968 pts/0 S+ 17:08 0:00 grep --color=auto bash
子进程正常退出,status == [0]
子进程正常退出,status == [0]
子进程死光了,wpid == [-1]
3.7 管道的读写行为
#include#include #include #include #include #include int main() { //创建管道 int fd[2]; int ret = pipe(fd); //创建管道,成功返回0,失败返回-1,并设置error值 fd[0]读端,fd[1]写端 if(ret == -1) { perror("pipe error"); return -1; } else if(ret == 0) //创建管道成功 { char buf[64]; memset(buf , 0x00 ,sizeof(buf)); //close(fd[1]); //关闭写端 int i = 1; while(1) { write(fd[1], "hello world" , strlen("hello world")); if(i++%1000 == 0) { printf("正在写入数据--[第%d条]\n",i); } } close(fd[0]); //关闭读端 int n = read(fd[0] , buf , sizeof(buf)); printf("读到了[%d]个字节,内容是[%s]\n",n,buf); } return 0; }
read正常读,返回读出的字节数
[holo@holocom 0410]$ ./pipe_wr 读到了[11]个字节,内容是[hello world]
写端全部关闭
read解除阻塞,立刻返回0, 相当于读文件读到了尾部
[holo@holocom 0410]$ ./pipe_wr 读到了[0]个字节,内容是[]
没有全部关闭
read阻塞
[holo@holocom 0410]$ ./pipe_wr
读端全部关闭
管道破裂,进程终止, 内核给当前进程发SIGPIPE(13)信号
[holo@holocom 0410]$ ./pipe_wr 读到了[-1]个字节,内容是[] //读不到数据,read返回-1
读端没全部关闭
缓冲区写满了
write阻塞
……(省略了好多条写入数据) 正在写入数据--[第5918条]正在写入数据--[第5919条]正在写入数据--[第5920条]正在写入数据--[第5921条]正在写入数据--[第5922条]正在写入数据- (一直按回车,没反应,说明write在这里阻塞了)
缓冲区没有满
继续write
3.8 如何设置管道为非阻塞
默认情况下,管道的读写两端都是阻塞的,若要设置读或者写端为非阻塞,则可参
考下列三个步骤进行:
第1步: int flags = fcntl(fd[0], F_GETFL, 0);
第2步: flags |= O_NONBLOCK;
第3步: fcntl(fd[0], F_SETFL, flags);
若是读端设置为非阻塞:
3.9 如何查看管道缓冲区大小
ulimit -a
long fpathconf(int fd, int name); //fd文件描述符,可以是读端或写端
printf("pipe size==[%ld]\n", fpathconf(fd[0], _PC_PIPE_BUF)); //_PC_PIPE_BUF获取管道大小的宏
printf("pipe size==[%ld]\n", fpathconf(fd[1], _PC_PIPE_BUF));
//利用函数查看管道缓冲区大小 #include#include #include #include #include int main(int argc,char * argv[]) { int fd[2]; int ret = pipe(fd); printf("管道大小(读端):[%ld]\n",fpathconf(fd[0],_PC_PIPE_BUF)); printf("管道大小(写端):[%ld]\n",fpathconf(fd[1],_PC_PIPE_BUF)); //fd[0]和fd[1]都指向管道,所以值应该是相同的 return 0; }
[holo@holocom 0410]$ ./pipesize
管道大小(读端):[4096]
管道大小(写端):[4096]
4 FIFO
4.1 FIFO介绍
FIFO常被称为命名管道,以区分管道(pipe,匿名管道)。管道(pipe)只能用于“有血缘关系”的进程间通信。但通过FIFO,不相关的进程也能交换数据。
FIFO是Linux基础文件类型中的一种(文件类型为p,可通过ls -l查看文件类型)。但FIFO文件在磁盘上没有数据块,文件大小为0,仅仅用来标识内核中一条通道。进程可以打开这个文件进行read/write,实际上是在读写内核缓冲区,这样就实现了进程间通信。
利用fifo进行通信, 必须创建一个fifo文件.
有血缘关系的进程 , 使用pipe更简单 ; 没血缘关系的进程, 使用fifo
4.2 创建管道
命令格式: mkfifo 管道名
例如:mkfifo myfifo
int mkfifo(const char *pathname, mode_t mode);
参数说明和返回值可以查看man 3 mkfifo
当创建了一个FIFO,就可以使用open函数打开它,常见的文件I/O函数都可用于FIFO。如:close、read、write、unlink等。
FIFO严格遵循先进先出(first in first out),对FIFO的读总是从开始处返回数据,对它们的写则把数据添加到末尾。它们不支持诸如lseek()等文件定位操作。(因为这个FIFO文件是一个标签,里面没有内容,操作标签相当于操作内存缓冲区)
4.3 使用FIFO完成两个进程通信
思路:
进程A先启动,进程B后启动
fifo_write.c
#include#include #include #include #include #include #include int main() { //创建FIFO文件 //int mkfifo(const char *pathname, mode_t mode); int ret = mkfifo("./myfifo" , 0777); if(ret < 0) { perror("mkfifo error"); return -1; } //打开文件 int fd = open("./myfifo" , O_RDWR); if(fd < 0) { perror("open error"); return -1; } //写fifo文件 write(fd , "hello world" , strlen("hello world")); sleep(10); // getchar(); //相当于c++中system("pause"); //关闭文件 close(fd); return 0; }
fifo_read.c
#include#include #include #include #include #include #include int main() { /* //创建FIFO文件 //int mkfifo(const char *pathname, mode_t mode); int ret = mkfifo("./myfifo" , 0777); if(ret < 0) { perror("mkfifo error"); return -1; } */ //打开文件 int fd = open("./myfifo" , O_RDWR); if(fd < 0) { perror("open error"); return -1; } //读fifo文件 char buf[64]; memset(buf,0x00,sizeof(buf)); int n = read(fd , buf , sizeof(buf)); printf("n == [%d] , 读到的内容buf == [%s]\n",n,buf); //关闭文件 close(fd); //getchar(); //相当于c++中system("pause"); return 0; }
先在标签1执行写
[holo@holocom 0410]$ rm myfifo [holo@holocom 0410]$ ./fifo_write
再复制一个标签2,执行读(10秒内)
[holo@holocom 0410]$ ./fifo_read n == [11] , 读到的内容buf == [hello world]
注意:myfifo文件是在进程A中创建的,如果先启动进程B会报错。思考一下如何解决这个问题呢???
access 检测文件是否存在,也可以判断文件权限
如果不存在就创建, 如果存在就不创建
返回值 : =0存在 !=0不存在
int ret = access("./myfifo" , F_OK);
完整demo:
fifo_write.c #include #include #include #include #include #include #include #include int main(int argc , char * argv[]) { int acc_ret = access("./myfifo" , F_OK); if(acc_ret != 0) //没有创建 { int ret = mkfifo("./myfifo" , 0777); //创建一个 if(ret == -1) { perror("error"); } }
int fd = open("./myfifo", O_RDWR); if(fd < 0) { perror("open error"); return -1; } write(fd , "hello world", strlen("hello world")); sleep(10); getchar(); close(fd); return 0; } |
fifo_read.c #include #include #include #include #include #include #include #include #include int main(int argc , char * argv[]) { int ret = access("./myfifo" , F_OK); if(ret != 0) //没有创建 { int fifo_ret = mkfifo("./myfifo" , 0777); //创建一个 if(fifo_ret == -1) { perror("error"); return -1; } } int fd = open("./myfifo", O_RDWR); if(fd < 0) { perror("open error"); return -1; } char buf[64]; memset(buf , 0x00 , sizeof(buf)); read(fd , buf , sizeof(buf) ); printf("read: [%s]\n",buf); close(fd); return 0; } |
标签1: holo@holo:~/test/fifo$ ./fifo_write 标签2: holo@holo:~/test/fifo$ ./fifo_read read: [hello world] |
5 内存映射区
5.1 存储映射区介绍
存储映射I/O (Memory-mapped I/O) 文件IO/设备IO 使一个磁盘文件与存储空间中的一个缓冲区相映射。从缓冲区中取数据,就相当于读文件中的相应字节;将数据写入缓冲区,则会将数据写入文件。这样,就可在不使用read和write函数的情况下,使用地址(指针)完成I/O操作。
使用存储映射这种方法,首先应通知内核,将一个指定文件映射到存储区域中。这个映射工作可以通过mmap函数来实现。
操作内存快 , 相比操作文件提高了效率
从文件区到内存区的映射
5.2 mmap函数
建立存储映射区
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
具体用哪个, 看实际需求, 只需要读 用第二个,
如果一个文件有2k,可以只把其中的1k映射到内存中去.
5.3 munmap函数
释放由mmap函数建立的存储映射区
int munmap(void *addr, size_t length);
成功:返回0
失败:返回-1,设置errno值
5.4 mmap注意事项
[holo@holocom 0410]$ ls -ltr test.log -rw-rw-r--. 1 holo holo 29 Apr 15 19:19 test.log [holo@holocom 0410]$ ./mmap1 buf = [0123456789] [holo@holocom 0410]$ chmod u-wr test.log [holo@holocom 0410]$ ls -ltr test.log ----rw-r--. 1 holo holo 29 Apr 15 19:19 test.log [holo@holocom 0410]$ ./mmap1 open error: Permission denied
添加close(fd)测试即可,亲测可用。
[holo@holocom 0410]$ cat test.log 0123456789d66666666666666666 [holo@holocom 0410]$ make mmap cc mmap.c -o mmap [holo@holocom 0410]$ ./mmap [hello world66666666666666666 ][holo@holocom 0410]$ cat test.log hello world66666666666666666
由于映射区已经建立,文件即使关闭,也不影响读写映射区操作,并且可以反应到文件中去。
填222 报错
void * addr = mmap(NULL , len , PROT_READ | PROT_WRITE , MAP_SHARED , fd , 222); [holo@holocom 0410]$ ./mmap mmap error: Invalid argument
填4096 报错(因为文件大小没有超过4096,越界了)
[holo@holocom 0410]$ ./mmap Bus error (core dumped)
一般填0就可以,如果文件大小超过4096,文件偏移量可以设为4096
5.5 有关mmap函数的使用总结
5.6 mmap函数相关思考题
不可以,必须建立文件并对文件进行写操作,保证文件大小不等于0.才可以创建映射区
不可以,open的权限要大于mmap的权限
无影响
报错,无效参数。文件偏移量是4K的整数倍(0、4096、……)
报错
不会
文件大小=0,open权限 < mmap权限,文件偏移量不是4K整数倍……
有可能调用mmap失败,返回map failed ,此时操作内存时会报错。
只要返回指针,就要检测返回值
5.7 mmap应用练习
#include#include #include #include #include #include #include int main() { //共享映射区的建立在fork之前 //使用mmap建立共享映射区 // // void *mmap(void *addr, size_t length, int prot, int flags, // int fd, off_t offset); int fd = open("./test.log" , O_RDWR); if(fd < 0) { perror("open error"); return -1; } int len = lseek(fd , 0 , SEEK_END); //文件大小 也可以用stat函数获取 //需要用lseek函数获取文件大小 void * addr = mmap(NULL , len , PROT_READ | PROT_WRITE , MAP_SHARED , fd , 0); //void * addr = mmap(NULL , len , PROT_READ | PROT_WRITE , MAP_PRIVATE , fd , 0); //mmap函数有可能失败 if(addr == MAP_FAILED) { perror("mmap error"); return -1; } //创建子进程 pid_t child_pid = fork(); if(child_pid < 0) { } else if(child_pid>0) //父进程 { memcpy(addr , "hello world" , strlen("hello world")); wait(NULL); //确保子进程先退出,父进程后退出 } else if(child_pid == 0) //子进程 { sleep(1); //保证父进程中的memcpy先完成 char *p = (char *)addr; printf("[%s]",p); } return 0; }
[holo@holocom 0410]$ vim test.log
[holo@holocom 0410]$ cat test.log //映射前文件大小必须大于0 , 等于0没法映射
11111
sssss66666666666666666
[holo@holocom 0410]$ ./mmap
[hello world66666666666666666
][holo@holocom 0410]$ cat test.log
hello world66666666666666666
[holo@holocom 0410]$
MAP_SHARED 文件会覆盖
MAP_PRIVATE 修改内存后不会写入文件里 适合进行读操作
父子进程可以共享共享映射区,文件描述符,不可以共享堆、栈
思路:两个进程都打开相同的文件,然后调用mmap函数建立存储映射区,这样两个进程共享同一个存储映射区。
//读 #include#include #include #include #include #include #include int main() { //共享映射区的建立在fork之前 //使用mmap建立共享映射区 // // void *mmap(void *addr, size_t length, int prot, int flags, // int fd, off_t offset); int fd = open("./test.log" , O_RDWR); if(fd < 0) { perror("open error"); return -1; } int len = lseek(fd , 0 , SEEK_END); //文件大小 //建立共享映射区 void * addr = mmap(NULL , len , PROT_READ | PROT_WRITE , MAP_SHARED , fd , 0); //void * addr = mmap(NULL , len , PROT_READ | PROT_WRITE , MAP_PRIVATE , fd , 0); //mmap函数有可能失败 if(addr == MAP_FAILED) { perror("mmap error"); return -1; } char buf[64]; //只读文件前10个 memset(buf , 0x00 , sizeof(buf)); memcpy(buf , addr , 10); //拷贝10个 printf("buf = [%s]\n",buf); return 0; }
//写 #include#include #include #include #include #include #include int main() { //共享映射区的建立在fork之前 //使用mmap建立共享映射区 // // void *mmap(void *addr, size_t length, int prot, int flags, // int fd, off_t offset); int fd = open("./test.log" , O_RDWR); if(fd < 0) { perror("open error"); return -1; } int len = lseek(fd , 0 , SEEK_END); //文件大小 //建立共享映射区 void * addr = mmap(NULL , len , PROT_READ | PROT_WRITE , MAP_SHARED , fd , 0); //void * addr = mmap(NULL , len , PROT_READ | PROT_WRITE , MAP_PRIVATE , fd , 0); //mmap函数有可能失败 if(addr == MAP_FAILED) { perror("mmap error"); return -1; } memcpy(addr , "0123456789" , 10); //写10个 return 0; }
[holo@holocom 0410]$ vim mmap2.c
[holo@holocom 0410]$ make mmap2
cc mmap2.c -o mmap2
[holo@holocom 0410]$ vim mmap1.c
[holo@holocom 0410]$ ./mmap2
[holo@holocom 0410]$ ./mmap1
buf = [0123456789]
[holo@holocom 0410]$ cat test.log
0123456789d66666666666666666
[holo@holocom 0410]$
匿名映射(不建立文件)
使用mmap函数建立匿名映射:
mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
MAP_SHARED必须与MAP_ANONYMOUS一起使用,
匿名映射不使用文件,anonymous匿名.
文件描述符固定为 -1
匿名映射没有文件,所以只能用于有血缘关系的进程间通讯
文档 MAP_ANONYMOUS The mapping is not backed by any file; its contents are initialized to zero. The fd and offset arguments are ignored; however, some implementations require fd to be -1 if MAP_ANONYMOUS (or MAP_ANON) is specified, and portable applications should ensure this. The use of MAP_ANONYMOUS in conjunction with MAP_SHARED is supported on Linux only since kernel 2.4.
案例代码
//mmap匿名映射完成父子进程通讯 #include#include #include #include #include #include #include int main() { //使用mmap建立共享映射区 void * addr = mmap(NULL , 4096 , PROT_READ | PROT_WRITE , MAP_SHARED | MAP_ANONYMOUS, -1 , 0); //void * addr = mmap(NULL , len , PROT_READ | PROT_WRITE , MAP_PRIVATE , fd , 0); //创建子进程 pid_t child_pid = fork(); if(child_pid < 0) { } else if(child_pid>0) //父进程 { memcpy(addr , "hello world" , strlen("hello world")); wait(NULL); //确保子进程先退出,父进程后退出 } else if(child_pid == 0) //子进程 { sleep(1); //保证父进程中的memcpy先完成 char *p = (char *)addr; printf("[%s]",p); } return 0; } [holo@holocom 0410]$ make mmap_anonymous cc mmap_anonymous.c -o mmap_anonymous [holo@holocom 0410]$ ./mmap_anonymous [hello world][holo@holocom 0410]$