Linux学习总结(十三)进程练习:大文件多进程和多线程拷贝

实现文件多进程拷贝

      假设有一个超大文件,需对其完成拷贝工作。为提高效率,可采用多进程并行拷贝的方法来实现。假设文件大小为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;
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

vegetablesssss

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值