多线程拷贝文件(映射区通讯实现)
进程的一般创建我已经学完了,进程之间的通讯四种常用的我也学习了三种,就剩下本地套接字没有学习。现在想用这些知识来完成一个小程序:多线程拷贝文件。
理论上来说,多个线程共同做一件事比单线程做一件同样的事所需时间少。现在来思考一下整体流程是怎样的:
- 打开(创建)文件(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;
}
代码自我感觉还是写的不错。注释按我的眼光还是挺详细的,就是不知道别人都不读得懂。这段代码的核心是那个循环。遗憾的是,我的这个程序貌似只能拷贝文本文件,图片视频的都不行。希望谁能给我说一下,,,,算了,反正没人看的到。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?