多线程拷贝文件(映射区通讯实现)
进程的一般创建我已经学完了,进程之间的通讯四种常用的我也学习了三种,就剩下本地套接字没有学习。现在想用这些知识来完成一个小程序:多线程拷贝文件。
理论上来说,多个线程共同做一件事比单线程做一件同样的事所需时间少。现在来思考一下整体流程是怎样的:
- 打开(创建)文件(open)
- 获取文件大小(stat.st_size)
- 拓展文件(ftruncate())
- 建立目标文件映射区(mmap())
- 确定创建多少个子程序
- 创建循环
- 创建原文件映射区
- 创建子进程进行拷贝(strbcpy())
- 释放原文件映射区(munmap())
- 释放目标文件映射区
- 回收进程(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;
}
代码自我感觉还是写的不错。注释按我的眼光还是挺详细的,就是不知道别人都不读得懂。这段代码的核心是那个循环。遗憾的是,我的这个程序貌似只能拷贝文本文件,图片视频的都不行。希望谁能给我说一下,,,,算了,反正没人看的到。