狂自私

导航

多线程拷贝文件(映射区通讯实现)

进程的一般创建我已经学完了,进程之间的通讯四种常用的我也学习了三种,就剩下本地套接字没有学习。现在想用这些知识来完成一个小程序:多线程拷贝文件。

理论上来说,多个线程共同做一件事比单线程做一件同样的事所需时间少。现在来思考一下整体流程是怎样的:

  1. 打开(创建)文件(open)
  2. 获取文件大小(stat.st_size)
  3. 拓展文件(ftruncate())
  4. 建立目标文件映射区(mmap())
  5. 确定创建多少个子程序
  6. 创建循环
  7. 创建原文件映射区
  8. 创建子进程进行拷贝(strbcpy())
  9. 释放原文件映射区(munmap())
  10. 释放目标文件映射区
  11. 回收进程(wait())

实际上这并不复杂,思路还是比较简单的,但是难点在于将思路转化为代码并实现它。这里有一些注意事项:比如mmap函数的使用极容易出错,返回值的检测,在哪儿回收子进程,在哪儿释放映射区等。

先上我自己的代码:

#include <stdio.h>

#include <sys/types.h>

#include <sys/stat.h>

#include <fcntl.h>

#include <sys/mman.h>

#include <sys/wait.h>

#include <string.h>

#include <stdlib.h>

#include <unistd.h>

 

//执行命令的格式是:cp 被复制文件名 新文件名

void sys_err(char p[])//处理错误的函数

{

    perror(p);

    exit(1);

}

 

int main(int argc, char *argv[])

{

    if (argc < 3)//我们需要两个参数

    {

        puts("paramentor too litile.");

        exit(1);

    }

 

    int o_ret_1 = open(argv[1], O_RDWR);//打开被复制文件

    if (-1 == o_ret_1)

    {

        sys_err("open copied file error ");

    }

 

    //获取文件大小

    struct stat st;

    int s_ret = stat(argv[1], &st);

    if (-1 == s_ret)

    {

        sys_err("stat error ");

    }

    int file_len = st.st_size;

 

    int o_ret_2 = open(argv[2], O_RDWR | O_APPEND | O_CREAT, 0644);//创建一个新文件

    if (-1 == o_ret_2)

    {

        sys_err("set up new file error ");

    }

    //————————————————————————————-

    int f_ret = ftruncate(o_ret_2, file_len);//拓展文件:这一步是很有必要的,复制文件,我们要保证两个文件大小一致。

    if (f_ret == -1)

    {

        sys_err("ftruncate error");

    }

 

    int i = 0, k = file_len / 20480 + 1;

    //我的思想是:以20480字节(即20k为单位,4k的5倍)为单位,为每一个20480字节创建一个进程进行复制操作。不足的也算为20480.一般来讲,没那个运气刚好是20480的整数倍

 

    //先为目标文件创建映射区,因为这个可以不用偏移,直接利用指针进行移位操作(偏移20480字节)

    char *const m_ret_2 = (char *const)mmap(NULL, file_len, PROT_WRITE | PROT_READ, MAP_SHARED, o_ret_2, 0);

    if (MAP_FAILED == m_ret_2)

    {

        sys_err("mmap error");

    }

 

    //开始循环创建子进程用于拷贝文件

    for (; i != k; i++)

    {

        //虽然文件有偏移量,但是映射区的大小依旧是文件的实际大小

        char *const m_ret_1 = (char *const)mmap(NULL, file_len, PROT_READ | PROT_WRITE, MAP_SHARED, o_ret_1, i * 20480);

        if (MAP_FAILED == m_ret_1)

        {

            sys_err("mmap error");

        }

 

        if (!(fork()))

        {

            char *p = m_ret_2;//目标文件的映射区的首地址

            p += i * 20480;//像数组那样操作映射区,这里偏移地址然后写入

            strncpy(p, m_ret_1, 20480);//保证每次(除了最后一次)能够写入20480字节的数据

 

            int mun_ret = munmap(m_ret_1, file_len);//子进程释放掉循环中创建的映射区,因为每次循环都会创建一个映射区。

            if (mun_ret == -1)

            {

                sys_err("munmap error");

            }

            break;

        }

    }

    int mun_ret = munmap(m_ret_2, file_len);

    if (mun_ret == -1)

    {

        sys_err("munmap error");

    }

    for (int i = 0; i != k; i++)//回收子进程

        wait(NULL);

 

    //printf("hello from Multi_process_copy_files!\n");

    return 0;

}

代码自我感觉还是写的不错。注释按我的眼光还是挺详细的,就是不知道别人都不读得懂。这段代码的核心是那个循环。遗憾的是,我的这个程序貌似只能拷贝文本文件,图片视频的都不行。希望谁能给我说一下,,,,算了,反正没人看的到。

posted on 2018-04-05 20:47  狂自私  阅读(507)  评论(0编辑  收藏  举报