您的位置:首页 > 运维架构 > Linux

fork创建子进时关于文件操作(fwrite、fread)的那些事

2016-11-03 00:42 363 查看
    今天有个朋友突然问我一个关于fork()创建子线程然后对文件操作的问题,当时我也楞了一下,好像没什么错,可是结果就是不如我所愿,看了一会才解决了那个问题,相信广大编程爱好者可能也会遇到该类问题,故现在把该问题写到微博。
    问题:用fork创建一个子进程,然后先在父进程将hello写入到test.txt文件中,然后在子进程中将world写入到test.txt中,最后在父进程将welcome写入test.txt文件中,最后结果应为:helloworldwelcome
    出现问题的解题程序是:
filename:fork_fwrite.c
#include<stdio.h>
#include<fcntl.h>//perror()的头文件
#include<string.h>
int main(){
FILE *fp;
int id;
char *hello="hello";
char *world="world";
char *welcome="welcome";
char *filename="test.txt";
if((fp=fopen(filename,"w+"))==NULL){
perror("fopen");
return ;
}
if(fwrite(hello,strlen(hello),1,fp)<1)//fwrite返回成功写入文件的块数
printf("fwrite hello error!\n");
if((id=fork())==-1){//创建进程,失败返回-1
perror("fork");
return;
}
if(id==0){//子进程
if(fwrite(world,strlen(world),1,fp)<1)//fwrite返回成功写入文件的块数
printf("fwrite world error!\n");
}
else{//父进程
sleep(1);
if(fwrite(welcome,strlen(welcome),1,fp)<1)//fwrite返回成功写入文件的块数
printf("fwrite welcome error!\n");
}
fclose(fp);
return 0;
}

得到的结果是:



ok,看到这个结果就知道是错,那么为什么错了呢?下面是我的解释:
        
         首先,先了解一下fork()工作的机制:对父进程的所有值都拷贝一份到子进程(包包括缓冲区的东西),但是拷贝过后,父/子进程对数据的操作是互相不影响,也就是说,他们是独立的,但是有一点就是:关于文件的操作有点特殊,对于文件的操作,他们是这样工作的,比如在父进程open一个文件,那么就会有一个文件描述符并且该文件描述符会有一个条目,并且在文件系统中也有相应的条目,当创建一个子进程时,文件描述符会自增一个条目,当在父/子进程调用了close函数时,文件描述符就会自减1,但是另一个的进程还可对该文件描述符进行操作,直到文件描述符的条目自减到0时,才关闭了文件描述符的作用。
所以上述代码的父/子进程都能执行fclose,但是当子进程先结束时,父进程还是可以对test.txt文件进行写入。
   对于文件描述符、条目这部分可以参考 http://blog.csdn.net/cws1214/article/details/9703743。     而fwrite是带缓冲的,write不带缓冲,这个会有什么影响呢?如果你是用open()、read()、write()函数替换相应的fopen()、fread()、fwrite()函数,那么该问题完全不会出现,因为他是实时写入文件的,而fwrite是带缓冲的,相当于说把hello先放在缓冲区,没有写入到文件里面。
    对于上述代码的执行顺序:
    1)先将hello放入父进程缓冲区;
    2)创建子进程,此时把父进程的缓冲区的值(hello)也拷贝过去,也就是说父子进程的缓冲区都有hello,但是还没有写入test.txt文件中。
    3)子进程将world写入子进程的缓冲区里,此时子进程的缓冲区的值变为:helloworld
    4)子进程执行fclose(fp),将子进程缓冲区的东西写入文件中,然后关闭文件(子进程就不能再访问该文件,但父进程还有访问读写权限),结束子进程。此时文件的内容变为:helloworld
    5)父进程将welcome写入缓冲区里父进程的缓冲区,此时父进程的缓冲区的值为:hellowelcome
    6)父进程调用fclose(fp)函数,此时会把缓冲区的值写入文件,所以才会得到文件值为:helloworldhellowelcome的结果。

对于使用write函数实现的代码如下:
filename:fork_write.c
#include<stdio.h>
#include<fcntl.h>//perror()的头文件
#include<string.h>
int main(){
int fd;
int id;
char *hello="hello";
char *world="world";
char *welcome="welcome";
char *filename="test.txt";
if((fd=open(filename,O_WRONLY))==-1){
perror("fopen");
return ;
}
if(write(fd,hello,strlen(hello))<1)//write返回成功写入文件的字节数
printf("write hello error!\n");
if((id=fork())==-1){//创建进程,失败返回-1
perror("fork");
return;
}
if(id==0){//子进程
if(write(fd,world,strlen(world))<1)//write返回成功写入文件的字节数
printf("write world error!\n");
}
else{//父进程
sleep(1);
if(write(fd,welcome,strlen(welcome))<1)//write返回成功写入文件的字节数
printf("write welcome error!\n");
}
close(fd);
return 0;
}
结果:



总结:
1、fork是拷贝值,包括缓冲区的值,而对于文件操作来说,父子进程是共享的,而且父子进程任意一个关闭文件时,另一个是不受其影响的,照样还可以访问。
2、fwrite是带缓冲区的,而write是不带缓冲区的,它可以实时写入。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  fwrite fork c c++ linux