实现文件多进程拷贝
假设有一个超大文件,需对其完成拷贝工作。为提高效率,可采用多进程并行拷贝的方法来实现。假设文件大小为len,共有n个进程对该文件进行拷贝。那每个进程拷贝的字节数应为len/n。但未必一定能整除,我们可以选择让最后一个进程负责剩余部分拷贝工作。可使用len % (len/n)将剩余部分大小求出。
为降低实现复杂度,可选用mmap来实现源、目标文件的映射,通过指针操作内存地址,设置每个进程拷贝的起始、结束位置。借助MAP_SHARED选项将内存中所做的修改反映到物理磁盘上。
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/wait.h>
void err_int(int ret, const char *err)
{
if (ret == -1) {
perror(err);
exit(1);
}
return ;
}
void err_str(char *ret, const char *err)
{
if (ret == MAP_FAILED) {
perror(err);
exit(1);
}
}
int main(int argc, char *argv[])
{
int fd_src, fd_dst, ret, len, i, n;
char *mp_src, *mp_dst, *tmp_srcp, *tmp_dstp;
pid_t pid;
struct stat sbuf;
if (argc < 3 || argc > 4) {
printf("Enter like this please: ./a.out file_src file_dst [process number]\n");
exit(1);
} else if (argc == 3) {
n = 5; //用户未指定,默认创建5个子进程
} else if (argc == 4) {
n = atoi(argv[3]);
}
//打开源文件
fd_src = open(argv[1], O_RDONLY);
err_int(fd_src, "open dict.txt err");
//打开目的文件, 不存在则创建
fd_dst = open(argv[2], O_RDWR | O_CREAT | O_TRUNC, 0664);
err_int(fd_dst, "open dict.cp err");
//获取文件大小
ret = fstat(fd_src, &sbuf);
err_int(ret, "fstat err");
len = sbuf.st_size;
if (len < n) //文件长度小于进程个数
n = len;
//根据文件大小拓展目标文件
ret = ftruncate(fd_dst, len);
err_int(ret, "truncate fd_dst err");
//为源文件创建映射
mp_src = (char *)mmap(NULL, len, PROT_READ, MAP_SHARED, fd_src, 0);
err_str(mp_src, "mmap src err");
//为目标文件创建映射
mp_dst = (char *)mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd_dst, 0);
err_str(mp_dst, "mmap dst err");
tmp_dstp = mp_dst;
tmp_srcp = mp_src;
//求出每个子进程该拷贝的字节数
int bs = len / n; //每个子进程应该拷贝的字节数
int mod = len % bs; //求出均分后余下的字节数,让最后一个子进程处理
//创建N个子进程
for (i = 0; i < n; i++) {
if ((pid = fork()) == 0) {
break;
}
}
if (n == i) { //父进程
for (i = 0; i < n; i++)
wait(NULL);
} else if (i == (n-1)){ //最后一个子进程,它多处理均分后剩余几个字节
memcpy(tmp_dstp+i*bs, tmp_srcp+i*bs, bs+mod);
} else if (i == 0) { //第一个子进程
memcpy(tmp_dstp, tmp_srcp, bs);
} else { //其他子进程
memcpy(tmp_dstp+i*bs, tmp_srcp+i*bs, bs);
}
munmap(mp_src, len);
munmap(mp_dst, len);
return 0;
}
实现文件多线程拷贝
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/mman.h>
#define T_NUM 5
#define ITEMS 50
void err_sys(void *str)
{
perror(str);
exit(1);
}
void err_usr(char *str)
{
fputs(str, stderr);
exit(1);
}
typedef struct{
int off, size, t_no;
}arg_t;
char *s, *d;
int *done;
int n = T_NUM;
//arg{off, size, t_no;}
void *tfn(void *arg)
{
arg_t *arg_p; int i;
char *p, *q;
arg_p = (arg_t *)arg;
p = s + arg_p->off, q = d + arg_p->off;
for(i = 0; i < arg_p->size; i++)
{
/* 逗号表达式的使用技巧*/
*q++ = *p++, done[arg_p->t_no]++;
usleep(10);
}
return NULL;
}
void *display(void *arg)
{
int size, interval, draw, sum, i, j;
size = (int)arg;
interval = size / (ITEMS - 1);
draw = 0;
while(draw < ITEMS){
for(i = 0, sum = 0; i < n; i++)
sum += done[i];
j = sum / interval + 1;
for(; j > draw; draw++){
putchar('='); fflush(stdout);
}
}
putchar('\n');
return NULL;
}
int main(int argc, char *argv[])
{
int src, dst, i, len, off;
struct stat statbuf;
pthread_t *tid;
arg_t *arr;
if(argc != 3 && argc != 4)
err_usr("usage : cp src dst [thread_no]\n");
if(argc == 4)
n = atoi(argv[3]);
src = open(argv[1], O_RDONLY);
if(src == -1)
err_sys("fail to open");
dst = open(argv[2], O_RDWR | O_CREAT | O_TRUNC, 0644);
if(dst == -1)
err_sys("fail to open");
if(fstat(src, &statbuf) == -1)
err_sys("fail to stat");
lseek(dst, statbuf.st_size - 1, SEEK_SET);
write(dst, "a", 1); //IO操作拓展文件大小,也可以使用truncate
s = (char *)mmap(NULL, statbuf.st_size, PROT_READ, MAP_PRIVATE, src, 0);
if(s == MAP_FAILED)
err_sys("fail to mmap");
d = (char *)mmap(NULL, statbuf.st_size, PROT_WRITE , MAP_SHARED, dst, 0);
if(d == MAP_FAILED)
err_sys("fail to mmap");
close(src); close(dst);
//pthread_t tid[n+1];
tid = (pthread_t *)malloc(sizeof(pthread_t) * (n + 1));
if(tid == NULL)
err_sys("fail to malloc");
//int done[n] 每个线程完成任务字节数
done = (int *)calloc(sizeof(int), n);
if(done == NULL)
err_sys("fail to malloc");
//arr[n] 每个线程的任务
arr = (arg_t *)malloc(sizeof(arg_t) * n);
if(arr == NULL)
err_sys("fail to malloc");
//构建线程任务数组,分配任务
len = statbuf.st_size / n, off = 0;
for(i = 0; i < n; i++, off += len)
arr[i].off = off, arr[i].size = len, arr[i].t_no = i;
arr[n - 1].size += (statbuf.st_size % n);
//创建执行拷贝任务线程
for(i = 0; i < n; i++)
pthread_create(&tid[i], NULL, tfn, (void *)&arr[i]);
//创建进度线程
pthread_create(&tid[n], NULL, display, (void *)statbuf.st_size);
for(i = 0; i < n + 1; i++)
pthread_join(tid[i], NULL);
#if 1
munmap(s, statbuf.st_size);
munmap(d, statbuf.st_size);
#endif
free(tid); free(done); free(arr);
return 0;
}