这段时间一直在跟着实验室学习,终于算是把《UNIX环境高级编程》的重点章节看完了,就差最后一个小作业了,就是多进程聚集写(Multi-Writing),要求如下:
多进程聚集写(multi-writing)
利用单生产者和多消费者模型,利用管道实现一个多进程协作拷贝文件的程序。一个进程读文件另外n个进程写,完成1G的文件并行拷贝。
要求:
不能出现数据丢失
n个消费者进程是子进程
每个消费者进程采用pwrite()去写文件(如何确定写入偏移量?)
程序运行退出后,使用md5sum命令验证源文件(1GB)与目标文件的内容是否一致。
自己思考了一下再加上学长的一些意见,大概想出三种实现方式吧。分三次写完,第一个最简单。
一般多进程问题都需要同步,这里有两个地方需要考虑。一是多个消费者读取管道数据时,这里我们利用Unix管道自身的一些特性(即当管道中没有数据时阻塞读进程,当管道中数据满时阻塞写进程)就省去这个麻烦了。二是多个写进程往文件中写数据时,需要分开来写,不然会出现相互覆盖导致数据丢失,所以只需考虑第二个问题。
不过如果多个写进程都知道自己要往文件的那一块写、写多少,而不是按顺序写下去,就可以避免这个问题。由此我们可以在生产者从文件中读取数据时就把数据长度、偏移量等信息保存下来,写在一个结构里。然后让写进程根据自己得到的数据块的偏移量写入文件中,那么他们之间的工作就不会干扰,也不存在先写后写结果不同的问题。
然而真正写代码的时候发现还需要考虑一些问题:
1.怎么把那个结构字符串化写到管道里,怎么从子进程恢复结构信息
2.父进程怎么判定所有子进程全部终结了
考虑清楚这两个问题就可以写出代码了。下面代码不是最终的版本,有好多东西没优化,只作参考。
在测试的时候还发现了一个问题,就是写入管道时串的大小问题。如果长度超过4096,那么复制的较大文件(比如超过1G)就会有损失,不影响使用,但是md5值不同。如果少于4096就不会有这个问题,个人猜想应该和文件系统最小块为4K有关系。很巧合的是昨天下午看《Unix网络编程:卷1》时,作者也提到了这个问题(P72 3.9),但并未给解释,待有时间再来探索吧。
/*************************************************************************
> File Name: multiProcess.c
> Author: R_Kevin
> Mail: 561705440@qq.com
> Created Time: 2014年03月05日 星期三 13时40分03秒
************************************************************************/
#include<stdio.h>
#include<unistd.h>
#include<signal.h>
#include<fcntl.h>
#include<string.h>
#include<stdlib.h>
#include<sys/wait.h>
#include<errno.h>
#define MAXLENGTH 409
#define TILE 50
typedef struct {
int length;
char buf[MAXLENGTH];
off_t offset;
} node;
pid_t r_wait(int *stat_loc);
void mysprintf(char*,char*,int);
char inpipe[MAXLENGTH+TILE],outpipe[MAXLENGTH+TILE];
int main(){
pid_t pid;
int i = 10;
int infiled,outfiled;
int fd[2];
off_t oldoffset = 0;
infiled = open("/home/kevin/Desktop/Test/infile",O_RDONLY);
outfiled = open("/home/kevin/Desktop/Test/outfile",O_WRONLY|O_TRUNC);
if(infiled == -1)
puts("wrong1");
if(outfiled == -1)
puts("wrong2");
if( pipe(fd) == -1)
puts("Error(pipe)");
while(i--){
pid = fork();
if(pid == 0){
//child;
node outnode;
int n;
close(fd[1]);
while(1){
n = read(fd[0],outpipe,MAXLENGTH+TILE);
if(n == -1){
puts("Reading abandon");
exit(0);
}
if(!n){
break;
}
sscanf(outpipe,"%d+%ld+",&outnode.length,&outnode.offset);
pwrite(outfiled,outpipe+32,outnode.length,outnode.offset);
}
exit(0);
}
}
if(pid>0){
node innode;
close(fd[0]);
while( innode.length = read(infiled,innode.buf,MAXLENGTH)){
if(innode.length == -1){
puts("Writing abandon");
return 0;
}
innode.offset = oldoffset;
oldoffset += innode.length;
sprintf(inpipe,"%10d+%20ld+",innode.length,innode.offset);
mysprintf(inpipe,innode.buf,innode.length);
write(fd[1],inpipe,MAXLENGTH+TILE);
}
close(fd[1]);
while(r_wait(NULL)>0);
close(infiled);
close(outfiled);
// printf("PIPE_BUFER IS %d\n",pathconf(_PC_PIPE_BUF));
}
return 0;
}
void mysprintf(char *obj,char *source,int len){
int i=0;
for(;i<len;i++)
obj[32+i]=source[i];
}
pid_t r_wait(int *stat_loc){
int retval;
while(((retval = wait(stat_loc)) == -1) && (errno == EINTR));
return retval;
}