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
得到的结果是:
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
总结:
1、fork是拷贝值,包括缓冲区的值,而对于文件操作来说,父子进程是共享的,而且父子进程任意一个关闭文件时,另一个是不受其影响的,照样还可以访问。
2、fwrite是带缓冲区的,而write是不带缓冲区的,它可以实时写入。
问题:用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是不带缓冲区的,它可以实时写入。
相关文章推荐
- 文件操作fopen, fclose, fread, fwrite, fseek, ftell
- c文件操作之fopen、fclose、fread、fwrite及相关fseek、ftell、rewind例子
- 用fread和fwrite实现文件复制操作
- C文件操作——fopen/fseek/ftell/fread/fwrite/fclose等函数用法
- 文件编程中的两套操作:标准C库(fread, fwrite,fclose,fopen...)和 系统调用(open, read, write...)
- 文件操作:fread()和fwrite()
- C/C++文件的操作(fread() fwrite())
- Linux下C语言的文本文件读写(fputc,fgetc,fwrite,fread对文件读写操作)
- linux函数代码操练---文件操作-----fwrite和fread
- 文件操作之fread()和fwrite()函数
- C/C++文件的操作(fread() fwrite())
- 文件操作--标准I/O操作--fopen() fclose() fread() fwrite()
- [Linux流操作]使用fread和fwrite函数读写文件
- C语言文件操作之fread与fwrite
- C/C++文件的操作(fread() fwrite())
- c库 文件操作----fopen fread fwrite fseek fclose
- c语言中所有文件操作函数详解fopen、fwrite、fread、fgetc、fputc、fscanf、fprintf、ftell、fseek等函数
- [小结]C语言的文件操作函数fopen,fread,fwrite注意点
- linux编程之文件操作fseek,fwrite,fread,ftell使用
- 关于标准C文件流读写问题:fopen,fread,fwrite,fclose的一些注意事项