Linux系统编程(二)

进程和程序

  • 程序:只占用磁盘空间
  • 进程:运行起来的程序,占用内存、CPU等系统资源

进程控制块PCB

  • 进程id:系统中每个进程有唯一的id,在C语言中用pid_t类型表示,其实就是一个非负整数
  • 进程的状态:有就绪、运行、挂起、停止等状态
  • 进程切换时需要保存和恢复的一些CPU寄存器
  • 描述虚拟地址空间的信息
  • 描述控制终端的信息
  • 当前工作目录
  • umask掩码
  • 文件描述符表,包含很多指向file结构体的指针
  • 和信号相关的信息
  • 用户id和组id
  • 会话(Session)和进程组
  • 进程可以使用的资源上限

进程控制

fork函数

创建一个新的进程

1
2
3
4
5
pid_t fork(void)

父子进程各自返回
父进程返回子进程pid
子进程返回0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include<stdio.h>
#include<string.h>
#include<pthread.h>
#include<unistd.h>
#include<dirent.h>
#include<stdlib.h>


int main(int argc,char *argv[])
{
printf("before fork-1---\n");
printf("before fork-2---\n");
printf("before fork-3---\n");
printf("before fork-4---\n");

pid_t pid = fork();
if (pid == -1) {
perror("fork error");
exit(1);
}else if(pid == 0) {
printf("----child is created\n");
}else if (pid > 0 ){
printf("----parent process: my child is %d\n",pid);
}

printf("=============end of file\n");
return 0;
}
getpid

获取子进程

getppid

获取父进程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include<stdio.h>
#include<string.h>
#include<pthread.h>
#include<unistd.h>
#include<dirent.h>
#include<stdlib.h>


int main(int argc,char *argv[])
{
printf("before fork-1---\n");
printf("before fork-2---\n");
printf("before fork-3---\n");
printf("before fork-4---\n");

pid_t pid = fork();
if (pid == -1) {
perror("fork error");
exit(1);
}else if(pid == 0) {
printf("----child is created,pid=%d,parent-pid=%d\n",getpid(),getppid());
}else if (pid > 0 ){
printf("----parent process: my child is %d,my pid:%d,my parent pid:%d\n",pid,getpid(),getppid());
}

printf("=============end of file\n");
return 0;
}

执行代码后发现结果如下,在子进程中得到的父进程的pid为1。按理应该得到的结果是21237才对。最后查阅相关信息发现,由于父进程先退出了,造成了子进程被init(ID=1)接管,所以得到的结果是1。这里采用的解决办法是在父进程中sleep几秒,让父进程晚于子进程结束即可。

1
2
3
4
5
6
7
8
before fork-1---
before fork-2---
before fork-3---
before fork-4---
----child is created,pid=21238,parent-pid=1
=============end of file
----parent process: my child is 21238,my pid:21237,my parent pid:15776
=============end of file
循环创建多个子进程
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include<stdio.h>
#include<string.h>
#include<pthread.h>
#include<unistd.h>
#include<dirent.h>
#include<stdlib.h>
int main(int argc,char *argv[])
{
int i;
pid_t pid;
for (i=0;i<5;i++) {
if(fork() == 0)
break;
}
if(i==5){
sleep(1);
printf("I'm parent\n");
}
else
printf("I'm %dth child\n",i+1);
return 0;
}
getuid

获取用户id

getgid

获取组id

进程共享

父子进程之间在fork后,有哪些异同

刚fork之后,相同之处,全局变量、.data.text、栈、堆、环境变量、用户ID、宿主目录、进程工作目录、信号处理方式

不同之处,进程ID,fork返回值,各自的父进程ID,进程运行时间,闹钟(定时器),未决信号集

通过程序判断父子进程是否共享全局变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include<stdio.h>
#include<string.h>
#include<pthread.h>
#include<unistd.h>
#include<dirent.h>
#include<stdlib.h>
int var = 100;

int main(int argc,char *argv[])
{
pid_t pid;
pid = fork();
if (pid == -1){
perror("fork error");
exit(1);
}else if(pid == 0) {
var = 200;
printf("I'm child pid=%d,ppid=%d\n",getpid(),getppid());
printf("child,var = %d\n",var);
}else if (pid > 0 ){
sleep(1);
var = 288;
printf("parent,var = %d\n",var);
printf("--I'm parent pid= %d,getppid = %d\n",getpid(),getppid());
}

printf("=======finish======\n");
return 0;
}

结果如下

1
2
3
4
5
6
I'm child pid=23313,ppid=23312
child,var = 200
=======finish======
parent,var = 288
--I'm parent pid= 23312,getppid = 15776
=======finish======

我们可以得到结论,父子进程间遵循读时共享写时复制的原则,这样设计,无论子进程执行父进程的逻辑还是执行自己的逻辑都能节省内存开销。

父子进程共享

  • 文件描述符(打开文件的结构体)
  • mmap建立的映射区
gdb调试父子进程

注意以下两种设置在fork函数调用之前才有效

1
2
set follow-fork-mode child #命令设置gdb在fork之后跟踪子进程
set follow-fork-mode parent #设置跟踪父进程

exec函数族

该函数族可以将当前进程的.text.data替换为所要加载的程序的.text.data,然后让进程从新的.text第一条指令开始执行,但进程ID不变,换核不换壳。

execlp函数

加载一个进程,借助PATH环境变量

1
2
int execlp(const char *file,const char *arg,...) #成功:无返回 失败:返回-1
参数1:要加载的程序的名字

使用execlp函数执行ls命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include<stdio.h>
#include<string.h>
#include<pthread.h>
#include<unistd.h>
#include<dirent.h>
#include<stdlib.h>


int main(int argc,char *argv[])
{

pid_t pid = fork();
if (pid == -1) {
perror("fork error");
exit(1);
}else if(pid == 0) {
//execlp("ls","-l","-d","-h",NULL); #错误写法
execlp("ls","ls","-l","-h",NULL);
perror("exec error");
exit(1);
}else if (pid > 0 ){
sleep(1);
printf("I'm parent:%d\n",getpid());
}

return 0;
}
execl函数
1
2
int execl(const char *path,const char *arg,...)  #成功:无返回 失败:返回-1
参数1:绝对路径

使用execl函数执行一个a.out文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include<stdio.h>
#include<string.h>
#include<pthread.h>
#include<unistd.h>
#include<dirent.h>
#include<stdlib.h>


int main(int argc,char *argv[])
{

pid_t pid = fork();
if (pid == -1) {
perror("fork error");
exit(1);
}else if(pid == 0) {
execl("./a.out","./a.out",NULL);
perror("exec error");
exit(1);
}else if (pid > 0 ){
sleep(1);
printf("I'm parent:%d\n",getpid());
}

return 0;
}
组合练习

ps命令执行后的结果写入ps.out文件中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include<unistd.h>
#include<fcntl.h>
#include<stdio.h>
#include<stdlib.h>

int main(void)
{
int fd;

fd = open("ps.out",O_WRONLY|O_CREAT|O_TRUNC,0644);
if(fd < 0){
perror("open ps.out error");
exit(1);
}

dup2(fd,STDOUT_FILENO);
execlp("ps","ps","aux",NULL);
perror("execlp error");

return 0;
}
exec函数族一般规律

exec函数一旦调用成功即执行新的程序,不返回。只有失败才返回,错误值-1。所以通常我们直接在exec函数调用后直接调用perror()exit(),无需if判断。

1
2
3
4
l(list) 命令行参数列表
p(path) 搜索file时使用path变量
v(vector) 使用命令行参数数组
e(enviroment) 使用环境变量数组

回收子进程

孤儿进程

父进程先于子进程结束,则子进程称为孤儿进程,子进程的父进程称为init进程,称为init进程领养孤儿进程

僵尸进程

进程终止,父进程尚未回收,子进程残留资源(PCB)存放于内核中,变成僵尸(Zombie)进程

wait函数

一个进程在终止时会关闭所有文件描述符,释放在用户空间分配的内存,但他的PCB还保留着,内核在其中保留了一些信息,如果是正常终止则保存着退出状态,如果是异常终止则保留着导致该进程终止的信号是哪个,这个进程的父进程可以调用wait或waitpid获取这些信息,然后彻底清除掉这个进程。

父进程调用wait函数可以回收子进程终止信息,该函数有三个功能

  • 阻塞等待子进程退出
  • 回收子进程残留资源
  • 获取子进程结束状态
1
2
3
pid_t wait(int *status)
成功:清理掉的子进程ID
失败:-1(没有子进程)

可使用wait函数传出参数status来保存进程的退出状态。借助宏函数来进一步判断进程终止的具体原因,宏函数可分为如下三组

1
2
3
4
5
6
7
8
9
WIFEXITED(status) 为非0 进程正常结束
WEXITSTATUS(status) 如上宏为真,使用此宏 获取进程退出状态(exit的参数)

WIFSIGNALED(status) 为非0 进程异常终止
WTERMSIG(status)如上宏为真,使用此宏 取得使进程终止的那个信号的编号

WIFSTOPPED(status) 为非0 进程处于暂停状态
WSTOPSIG(status) 如上宏为真,使用此宏 取得使进程暂停的那个信号的编号
WIFCONTINUED(status) 为真 进程暂停后已经继续运行

如下代码可以判断子进程的退出状态,并获取相应的状态信号编号

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include<stdio.h>
#include<string.h>
#include<pthread.h>
#include<unistd.h>
#include<dirent.h>
#include<stdlib.h>


int main(int argc,char *argv[])
{
pid_t pid,wpid;
pid = fork();
int status;
if (pid == 0) {
printf("----child,my myid=%d,going to sleep 10s\n",getpid());
sleep(10);
printf("------------child die------------------\n");
return 73;
}else if(pid>0) {
wpid = wait(&status);
if (wpid == -1){
perror("wait error");
exit(1);
}
if(WIFEXITED(status)){
printf("child exit with %d\n",WEXITSTATUS(status));
}
if(WIFSIGNALED(status)){
printf("child kill with signal %d\n",WTERMSIG(status));
}
printf("--------parent wait finish:%d\n",wpid);
}else{
perror("fork");
return 1;
}

return 0;
}
waitpid函数
1
2
3
4
5
6
7
8
9
10
11
12
pid_t waitpid(pid_t pid,int *status,int options)
参数:
pid:指定回收的子进程pid
> 0:待回收的子进程pid
-1:任意子进程
0:同组的子进程
status:(传出)回收进程的状态
options:WNOHANG 指定回收方式为,非阻塞
返回值:
> 0:表成功回收的子进程pid
0:函数调用时,参数3指定了WNOHANG,并且没有子进程结束
-1:失败。errno

指定某一个进程进行回收

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include<stdio.h>
#include<string.h>
#include<pthread.h>
#include<unistd.h>
#include<dirent.h>
#include<stdlib.h>
#include<sys/wait.h>

int main(int argc,char *argv[])
{
int i;
pid_t pid,wpid,tmpid;

for (i=0;i<5;i++) {
pid = fork();
if(pid == 0){
break;
}
if(i==2) {
tmpid = pid;
printf("---------pid = %d\n",tmpid);
}
}
if(i==5){
//sleep(5); //配合WNOHANG 不阻塞
//wpid = waitpid(tmpid,NULL,WNOHANG);//指定一个进程回收,不阻塞
wpid = waitpid(tmpid,NULL,0); //指定一个进程回收,阻塞回收
if(wpid == -1){
perror("waitpid error");
exit(1);
}
printf("I'm parent,wait a child finish : %d \n",wpid);
}
else{
sleep(i);
printf("I'm %dth child,pid=%d\n",i+1,getpid());
}
return 0;
}

指定回收所有进程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include<stdio.h>
#include<string.h>
#include<pthread.h>
#include<unistd.h>
#include<dirent.h>
#include<stdlib.h>
#include<sys/wait.h>

int main(int argc,char *argv[])
{
int i;
pid_t pid,wpid,tmpid;

for (i=0;i<5;i++) {
pid = fork();
if(pid == 0){
break;
}
}
if(i==5){
while((wpid=waitpid(-1,NULL,WNOHANG))!=-1){ //使用非阻塞方式回收
if(wpid > 0){
printf("wait child %d\n",wpid);
}else if(wpid == 0){
sleep(1);
continue;
}
}
}
else{
sleep(i);
printf("I'm %dth child,pid=%d\n",i+1,getpid());
}
return 0;
}

管道

内核借助环形队列机制,使用内核缓冲区实现

pipe函数
1
2
3
4
5
6
int pipe(int fd[2])

参数:fd[0]:读端
fd[1]:写端
返回值:成功 0
失败 -1 error
pipe函数实现父进程写,子进程读的功能
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include<stdio.h>
#include<string.h>
#include<pthread.h>
#include<unistd.h>
#include<dirent.h>
#include<stdlib.h>

void sys_err(const char *str){
perror(str);
exit(1);
}

int main(int argc,char *argv[]){
int ret;
int fd[2];
pid_t pid;
char *str = "hello pipe\n";
char buf[1024];

ret = pipe(fd);
if(ret == -1)
sys_err("pipe error");
pid = fork();
if (pid>0){
close(fd[0]);
write(fd[1],str,strlen(str));
close(fd[1]);
} else if(pid==0){
close(fd[1]);
ret = read(fd[0],buf,sizeof(buf));
write(STDOUT_FILENO,buf,ret);
close(fd[0]);
}

return 0;
}
父子进程实现ls | wc -l
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include<unistd.h>
#include<fcntl.h>
#include<stdio.h>
#include<stdlib.h>


void sys_err(const char *str){
perror(str);
exit(1);
}

int main(int argc,char *argv[]){
int fd[2];
int ret;
pid_t pid;

ret = pipe(fd);
if (ret == -1){
sys_err("pipe error");
}

pid = fork();
if (pid==-1){
sys_err("fork error");
} else if(pid > 0){
close(fd[0]);
dup2(fd[1],STDOUT_FILENO);
execlp("ls","ls",NULL);
} else if(pid == 0) {
close(fd[1]);
dup2(fd[0],STDIN_FILENO);
execlp("wc","wc","-l",NULL);
}
return 0;
}
兄弟进程实现ls | wc -l
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include<unistd.h>
#include<fcntl.h>
#include<stdio.h>
#include<stdlib.h>
#include<sys/wait.h>

void sys_err(const char *str){
perror(str);
exit(1);
}

int main(int argc,char *argv[]){
int fd[2];
int ret,i;
pid_t pid;

ret = pipe(fd);
if (ret == -1){
sys_err("pipe error");
}

for(i=0;i<2;i++){
pid = fork();
if (pid==-1)
sys_err("fork error");
if(pid==0)
break;
}
if(i == 2){
close(fd[0]);
close(fd[1]);
wait(NULL);
wait(NULL);
}else if(i == 0){
close(fd[0]);
dup2(fd[1],STDOUT_FILENO);
execlp("ls","ls",NULL);

}else if(i==1){
close(fd[1]);
dup2(fd[0],STDIN_FILENO);
execlp("wc","wc","-l",NULL);
}
return 0;
}
管道的优劣

优点:简单,相比信号,套接字实现进程间通信,简单的多

缺点:只能单向通信,双向通信需建立两个管道,只能用于父子,兄弟等有血缘关系之间的通信

FIFO

也被称为有名管道

创建fifo
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<errno.h>
#include<pthread.h>
#include<sys/stat.h>

void sys_err(const char *str)
{
perror(str);
exit(1);
}

int main(int argc,char *argv[])
{
int ret = mkfifo("mytestfifo",0644);
if(ret == -1)
sys_err("mkfifo error");
return 0;
}
fifo实现非血缘关系之间的进程通信

第一个文件是写入fifo文件的代码fifo_w.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<errno.h>
#include<pthread.h>
#include<sys/stat.h>
#include<fcntl.h>
void sys_err(const char *str)
{
perror(str);
exit(1);
}

int main(int argc,char *argv[])
{
int fd,i;
char buf[4096];

if(argc < 2){
printf("Enter like this: ./a.out fifoname\n");
return -1;
}
fd = open(argv[1],O_WRONLY);
if(fd < 0)
sys_err("open error");

i = 0;
while (1){
sprintf(buf,"hello elssm %d\n",i++);
write(fd,buf,strlen(buf));
sleep(1);

}
close(fd);
return 0;
}

第二个文件是写入fifo文件的代码fifo_r.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<errno.h>
#include<pthread.h>
#include<sys/stat.h>
#include<fcntl.h>
void sys_err(const char *str)
{
perror(str);
exit(1);
}

int main(int argc,char *argv[])
{
int fd,len;
char buf[4096];

if(argc < 2){
printf("Enter like this: ./a.out fifoname\n");
return -1;
}
fd = open(argv[1],O_RDONLY);
if(fd < 0)
sys_err("open error");


while (1){
len = read(fd,buf,sizeof(buf));
write(STDOUT_FILENO,buf,len);
sleep(3);

}
close(fd);
return 0;
}

存储映射

mmap函数

创建共享内存映射

1
2
3
4
5
6
7
8
9
10
11
12
13
14
void *mmap(void *addr,size_t length,int prot,int flags,int fd,off_t offset)

返回:成功 返回创建的映射区首地址
失败:MAP_FAILED宏

参数
addr:建立映射区的首地址,由Linux内核指定,使用时,直接传递NULL
length:欲创建映射区的大小
prot:映射区权限 PROT_READ,PROT_WRITE,PROT_READ|PROT_WRITE
flags:标志位参数(常用于设定更新物理区域,设置共享,创建匿名映射区)
MAP_SHARED:会将映射区所做的操作反映到物理设备(磁盘)上
MAP_PRIVATE:映射区所做的修改不会反映到物理设备
fd:用于创建共享内存映射区的那个文件的文件描述符
offset:偏移位置,需是4k的整数倍
mmap函数建立文件映射区
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<errno.h>
#include<pthread.h>
#include<sys/mman.h>
#include<fcntl.h>

void sys_err(const char *str)
{
perror(str);
exit(1);
}

int main(int argc,char *argv[])
{
char *p = NULL;
int fd;
fd = open("testmap",O_RDWR|O_CREAT|O_TRUNC,0644);
if(fd==-1)
sys_err("open err");
lseek(fd,20,SEEK_END);
write(fd,"\0",1);

int len = lseek(fd,0,SEEK_END);

p = mmap(NULL,len,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
if(p == MAP_FAILED){
sys_err("mmap error");
}

//使用p对文件进行读写操作
strcpy(p,"hello mmap"); //写操作
printf("------%s\n",p); //读操作
int ret = munmap(p,len);
if(ret == -1)
sys_err("munmap error");
return 0;
}
mmap注意事项
  • 创建映射区的过程中,隐含着一次对映射文件的读操作
  • MAP_SHARED时,要求:映射区的权限应<=文件打开的权限(出于对映射区的保护),而MAP_PRIVATE则无所谓,因为mmap中的权限是对内存的限制
  • 映射区的释放与文件关闭无关,只要映射建立成功,文件可以立即关闭
  • 特别注意,当映射文件大小为0时,不能创建映射区,所以,用于映射的文件必须要有实际大小
  • munmap传入的地址一定是mmap的返回地址,坚决杜绝指针++操作
  • 文件偏移量必须为4K的整数倍
  • mmap创建映射区出错概率非常高,一定要检查返回值,确保映射区建立成功在进行后续操作
父子进程间mmap通信

代码练习,父进程创建映射区,然后fork子进程,子进程修改映射区内容,之后父进程读区映射区内容,查看是否共享

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<errno.h>
#include<pthread.h>
#include<sys/mman.h>
#include<fcntl.h>

void sys_err(const char *str)
{
perror(str);
exit(1);
}
int var = 100;
int main(int argc,char *argv[])
{
int *p;
pid_t pid;
int fd;
fd = open("temp",O_RDWR|O_CREAT|O_TRUNC,0644);
if(fd==-1)
sys_err("open err");

ftruncate(fd,4);

p = (int *)mmap(NULL,4,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
if(p == MAP_FAILED){
sys_err("mmap error");
}
close(fd); //映射区建立完毕,即可关闭文件

pid = fork();
if(pid==0){
*p = 2000; //写共享内存
var = 1000;
printf("child,*p = %d,var = %d\n",*p,var); //读共享内存
}else {
sleep(1);
printf("parent,*p = %d,var = %d\n",*p,var);
wait(NULL);

int ret = munmap(p,4); //释放映射区
if(ret == -1)
sys_err("munmap error");
}
return 0;
}
非血缘关系进程间通信

代码练习,两个进程,一个对结构体进行修改后写入,另一个进程对写入后的结构体进行读取

第一个文件是写入文件的代码mmap_w.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<errno.h>
#include<pthread.h>
#include<sys/mman.h>
#include<fcntl.h>

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,"elssm",22};
struct student *p;
int fd;
fd = open("test_map",O_RDWR|O_CREAT|O_TRUNC,0644);
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");
while (1){
memcpy(p,&stu,sizeof(stu));
stu.id++;
sleep(1);
}
munmap(p,sizeof(stu));
close(fd);
return 0;
}

第二个文件是读取文件的代码mmap_r.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<errno.h>
#include<pthread.h>
#include<sys/mman.h>
#include<fcntl.h>

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");
while (1){
printf("id = %d,name = %s,age = %d\n",p->id,p->name,p->age);
sleep(1);
}
munmap(p,sizeof(stu));
close(fd);
return 0;
}