如何利用多進程,來實現文件的拷貝?
在我們學習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高級環境編程中有這樣一幅圖

這種共享文件的方式使父、子進程對同一個文件使用了一個文件位移量。當父進程是其標準輸出重新定向,那么子進程寫到該標準輸出時,它將更新與父進程共享的該文件的位移量。當程序中,父、子進程寫到同一個描述符文件,因為沒有任何形式的同步,因為它們的輸出都混在一起,所以復制后的文件就是錯的。那么為了解決該問題,我們應該對所寫的程序加以改進。
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);
ftruncate(des,len);//創建空洞文件
printf("len=%ld\n",len);
int pid;
if((pid=fork())<0)
{
……
}
else if(pid ==0)
{
close(src);
close(des);
if((src=open(argv[1],O_RDONLY))<0)
{
fprintf(stderr,"open %s failed!:%s\n",argv[1],strerror(errno));
return -1;
}
if((des=open(argv[2],O_WRONLY))<0)
{
fprintf(stderr,"open %s failed!:%s\n",argv[2],strerror(errno));
return -1;
}
lseek(src,len/2,SEEK_SET);
lseek(des,len/2,SEEK_SET);
while((n=read(src,buf,sizeof(buf)))!=0)
{
write(des,buf,n);
}
close(src);
close(des);
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);
close(src);
close(des);
exit(0);
}
這樣父、子進程就不共享同一個文件位移量,雖然打開的是同一個文件,寫的也是同一個文件,但是再實際操作中,就不會讓父子進程使用同一張文件表了。
