轉載:http://www.eefocus.com/ayayayaya/blog/10-07/193194_0d80b.html
在我們學習IO的時候,曾經利用文件IO函數,標准IO函數都實現了對文件的拷貝,那么在我們學習過進程間通信后,就可以創建多個進程來完成對同一個文件的讀寫。例如讓父進程寫文件的前半部分,子進程來寫文件的后半部分,因為兩個進程間是可以並發執行的,所以將會節約一部分時間,提高執行的效率。那么怎樣才能實現這個功能?
我們以文件IO為例,邊講述如何實現的同時,也給大家說下為什么這樣寫的原因,希望能給大家得到些啟發。
首先來看下用文件IO函數實現拷貝文件的程序:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#define maxsize 256
int main(int argc,char *argv[])
{
if(argc!=3) //如果命令格式不正確
{
printf("command error!\n");
return -1;
}
int fd1,fd2;
if ((fd1= open(argv[1],O_RDONLY))< 0)
{
perror("error");
return -1;
}
if ((fd2 = open(argv[2],O_WRONLY|O_CREAT|O_TRUNC,0644))< 0)
{
perror("error");
return -1;
}
char s[maxsize ];
int t=0;
while ((t=read(fd1,s,maxsize ))==maxsize )
{
write(fd2,s,t);
}
write(fd2,s,t);
close(fd1);
close(fd2);
return 0;
}
這樣就實現了文件的拷貝。那么在我們學習完fork函數之后是不是只要父進程和子進程分別寫一部分就可以了?
if((src=open(argv[1],O_RDONLY))<0)
{
……
}
if((des=open(argv[2],O_WRONLY|O_CREAT|O_TRUNC,0644))<0)
{
……
}
len=lseek(src,0,SEEK_END);
int pid;
if((pid=fork())<0)
{
fprintf(stderr,"fork error!:%s\n",strerror(errno));
return -1;
}
else if(pid ==0)
{
lseek(src,len/2,SEEK_SET);
lseek(des,len/2,SEEK_SET);
while((n=read(src,buf,sizeof(buf)))!=0)
{
write(des,buf,n);
}
exit(0);
}
else
{
lseek(src,0,SEEK_SET);
lseek(des,0,SEEK_SET);
while((n=read(src,buf,sizeof(buf)))!=0)
{
write(des,buf,n);
if(lseek(src,0,SEEK_CUR) > len/2)
break;
}
wait(NULL);
exit(0);
}
是不是只要這樣寫就可以實現功能?實踐證明這樣寫不行的,那么為什么這樣寫不可以呢?
子進程和父進程繼續執行fork之后的指令。子進程是父進程的復制品。子進程獲得父進程數據空間、堆棧的復制品。注意,這是子進程所擁有的拷貝,父、子進程並不共享這些存儲空間部分。那么跟本例相關的一條fork特性就是由父進程打開的文件描述符也被復制到子程序中。父、子進程的每個相同的打開描述符共享一個文件表項。
在unix高級環境編程中有這樣一幅圖
這種共享文件的方式使父、子進程對同一個文件使用了一個文件位移量。當父進程是其標准輸出重新定向,那么子進程寫到該標准輸出時,它將更新與父進程共享的該文件的位移量。當程序中,父、子進程寫到同一個描述符文件,因為沒有任何形式的同步,因為它們的輸出都混在一起,所以復制后的文件就是錯的。那么為了解決該問題,我們應該對所寫的程序加以改進。
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <stdlib.h> #include <strings.h>
#define BUFFSIZE 30
//copy file
int main(int argc, char* argv[]) { int src; int dst; int ret; char buff[BUFFSIZE]; pid_t pid; int len; int n; if (argc != 3) { printf("have no enough parameter\n"); exit(0); } src = open(argv[1], O_RDONLY); dst = open(argv[2], O_CREAT|O_WRONLY|O_TRUNC, S_IRWXU); if(-1==src || -1==dst) { printf("open file error\n"); exit(0); } len = lseek(src, 0, SEEK_END); //創建空洞文件
ftruncate(dst,len); pid = fork(); if (-1 == pid) { printf("fork error\n"); exit(0); } else if (0 == pid) { close( src ); close( dst ); src = open(argv[1], O_RDONLY); dst = open(argv[2], O_WRONLY); lseek(src,len/2,SEEK_SET); lseek(dst,len/2,SEEK_SET); while( (n = read(src, buff, BUFFSIZE)) > 0) { write(dst, buff, n); } close( src ); close( dst ); exit( 0 ); } else { lseek(src, 0, SEEK_SET); lseek(dst, 0, SEEK_SET); while( (n = read(src, buff, BUFFSIZE)) > 0) { write(dst, buff, n); if ( (n = lseek(src, 0, SEEK_CUR)) > len/2 ) { break; } } } wait(NULL); close( src ); close( dst ); return 0; }
這樣父、子進程就不共享同一個文件位移量,雖然打開的是同一個文件,寫的也是同一個文件,但是再實際操作中,就不會讓父子進程使用同一張文件表了。