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

来源:互联网 发布:战狼2知乎 编辑:程序博客网 时间:2024/05/28 15:21

    今天有个朋友突然问我一个关于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){//创建进程,失败返回-1perror("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){//创建进程,失败返回-1perror("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是不带缓冲区的,它可以实时写入。

0 0
原创粉丝点击