前面的文章讨论了父进程创建了子进程以后,子进程会拷贝父进程代码段、数据段和堆、栈中的数据。那对于父子进程对文件进行基于缓存的写操作是怎么处理的呢?我们先看看下面的代码。
process_fork4.c
/*
* process_fork4.c
*
* Created on: 2016-11-13
* Author: river
*/
/*
* process_fork3.c
*
* Created on: 2016-11-13
* Author: river
*/
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
int g_v = 30;
int main()
{
int a_v = 30;
static int s_v = 30;
printf("pid: %d\n", getpid());
FILE *fp = fopen("s.txt", "w");
int fd = open("s_fd.txt", O_WRONLY | O_CREAT | O_TRUNC, S_IRWXU | S_IRWXG);
char *s = "hello iotek";
ssize_t size = strlen(s) * sizeof(char);
/*父进程调用*/
//标准IO函数(带缓存--->全缓存)
fprintf(fp, "s:%s, pid:%d", s, getpid());//写入缓存
//内核提供的IO系统调用(不带缓存)
write(fd, s, size);//直接写入文件
pid_t pid;
pid = fork();//创建子进程
//在fork之后会运行2个进程(父进程、子进程)
if(pid < 0)
{
perror("fork error");
}
else if(pid > 0)
{//父进程(在父进程中fork返回的是子进程的pid)
//父进程执行的代码...
g_v = 40;
a_v = 40;
s_v = 40;
printf("I am parent process pid is %d\n", getpid());
printf("g_v: %p, a_v: %p, s_v: %p\n", &g_v, &a_v, &s_v);
}
else
{//子进程(在子进程中fork返回的是0)
//子进程执行的代码...
printf("pid: %d, g_v:%d, a_v:%d, s_v:%d\n", getpid(), g_v, a_v, s_v);
g_v = 50;
a_v = 50;
s_v = 50;
printf("I am child process pid is %d\n", getpid());
printf("g_v: %p, a_v: %p, s_v: %p\n", &g_v, &a_v, &s_v);
}
//父子进程都要执行(写入缓存)
fprintf(fp, " pid: %d, g_v:%d, a_v:%d, s_v:%d\n", getpid(), g_v, a_v, s_v);//父子进程都执行
sleep(1);//睡眠1s使得父子进程可以交替运行
exit(0);
}
上面的代码中,我们创建了2个文件s.txt、s_fd.txt,在父进程中用基于缓存的标准IO函数fprintf()和不带缓存的write系统调用分别对这两个文件进行写操作(红色标示代码)。然后在父子进程都执行的代码段(绿色标示的代码)使用基于缓存的标准IO函数对s.txt进行写操作。
程序运行结果是在s_fd.txt写入了hello iotek,在s.txt中写入了如下内容:
s:hello iotek, pid:2863pid: 2863, g_v:40, a_v:40, s_v:40
s:hello iotek, pid:2863 pid: 2864, g_v:50, a_v:50, s_v:50
在s.txt文件中,蓝色部分是父进程第一次写入的,绿色部分是在父进程完成变量修改后写入到文件中的,棕色部分是子进程继承自父进程的数据(从父进程堆拷贝到子进程的数据,因为是使用的带缓存的函数,这部分缓存被保存到堆中,基于文件的缓存是全缓存,只要缓存没有满,就会等到程序结束再清缓存),黑色部分是子进程完成变量修改后,写入到文件中的。具体的原理参考下面的图:
注释:上图子进程框图中第二行内容应为:pid: 2864, g_v:50, a_v:50, s_v:50,笔误。