这段代码是一个模仿 Linux dd 命令的工具,它用于在不同文件之间复制数据。dd 是一个非常强盛的命令行工具,可以用于数据备份、转换和复制。下面我将详细解释这段代码的原理、实现方式以及如何运行和测试。
Linux dd 命令的工作原理
dd 命令是 Unix 和 Linux 系统中非常强盛的文件复制和转换工具。它通过指定块大小(block size)来读取和写入数据,实现高效的文件或设备复制。dd 命令不仅能够处理平凡文件,还能直接操纵设备文件,这使得它在系统备份、规复和数据克隆等使命中非常有用。
- 基本语法:dd if=输入文件 of=输出文件 [选项]
- if:指定输入文件名或设备名。
- of:指定输出文件名或设备名。
- [选项]:各种可选参数,用于控制复制活动。
- 常用选项:
- bs:设置读写的块大小。例如:bs=4M。
- count:复制的块数。例如:count=1。
- conv:指定数据转换选项,如 conv=sync 确保同步模式。
- status=progress:表现详细的进度信息。
- 工作流程:
- 打开源和目标文件/设备:dd 命令会先尝试打开指定的输入和输出文件或设备。
- 按块读取和写入:根据指定的块大小(bs),dd 从输入文件或设备读取数据,并写入到输出文件或设备中。
- 数据处理:在读写过程中,dd 可以根据指定的转换选项对数据进行处理,如大小写转换、字节顺序转换等。
- 重复操纵:上述过程会根据指定的块数(count)反复进行,直到完成全部数据的复制或转换。
命令利用用
dd 的基本命令行格式如下:
- dd if=<input_file> of=<output_file> [其他参数]
复制代码
以下是一些常用的参数示例:
- bs:设置块大小,例如 bs=1024。
- count:设置复制的块数,例如 count=10。
- skip:设置跳过的块数,例如 skip=5。
- seek:设置输出文件中跳过的块数,例如 seek=5。
示例
复制一个文件的前 10MB 到另一个文件:
- dd if=/dev/zero of=example.img bs=1M count=10
复制代码 将一个磁盘分区备份到另一个磁盘分区:
- dd if=/dev/sda of=/dev/sdb
复制代码 基本文件复制:
- dd if=inputfile of of=outputfile bs=64K count=1
复制代码 将 inputfile 复制到 outputfile,每次读取和写入 64KB 的数据块,只复制一个块。
备份和还原硬盘:
- dd if=/dev/sda of=/path/to/backup.img bs=4M
- dd if=/path/to/backup.img of=/dev/sdb bs=4M
复制代码 将整个硬盘 /dev/sda 备份到 backup.img 文件中,然后将 backup.img 还原到 /dev/sdb。
创建镜像文件:
- dd if=/dev/zero of=imagefile.img bs=1G count=10
复制代码 创建一个名为 imagefile.img 的 10GB 镜像文件,内容全为零。
制作启动盘:
- dd if=boot.img of=/dev/sdb bs=4M
复制代码 将 boot.img 写入到 USB 设备 /dev/sdb,制作可启动的 USB 盘。
擦除硬盘数据:
- dd if=/dev/urandom of of=/dev/sda bs=4M
复制代码 使用随机数据覆盖整个硬盘,确保数据无法规复。
日常定位分析
dd 命令在系统规复、数据规复和磁盘克隆等场景中非常有用。例如,当你需要从一个破坏的文件系统中规复数据时,可以使用 dd 来复制文件系统的一部分到另一个康健的磁盘上,然后对复制的数据进行分析和规复。
三、dd 命令在实际工作中的定位与分析
dd 命令因其强盛的功能和灵活性,在系统管理和运维工作中有着广泛的应用场景。以下是几个典型的应用场景及其分析:
- 系统备份与规复:
- 场景:定期备份服务器上的硬盘或分区,以防数据丢失或系统故障。
- 分析:通过 dd 命令,可以创建整个硬盘或分区的镜像文件,方便存储和快速规复。别的,还可以使用压缩工具(如 gzip)进一步减小镜像文件的大小。
- 数据克隆和迁移:
- 场景:在更换硬盘或迁移数据时,需要将旧硬盘上的数据完整复制到新硬盘。
- 分析:dd 命令可以直接操纵设备文件,无需颠末文件系统,从而提高数据复制的效率和可靠性。这对于大规模数据迁移尤其有用。
- 制作启动盘和规复盘:
- 场景:需要制作可启动的 USB 盘或 CD/DVD,用于系统安装或故障排查。
- 分析:dd 命令可以将 ISO 镜像文件直接写入到 USB 或光盘设备,操纵简单且高效。这在紧急情况下尤为重要,如系统崩溃后的规复工作。
- 安全删除数据:
- 场景:需要彻底删除敏感数据,确保无法通过规复工具找回。
- 分析:通过用随机数据覆盖硬盘,可以有用防止数据被规复。这种方法比单纯的文件删除更为安全,实用于处理包罗敏感信息的硬盘。
- 性能测试:
- 场景:测试磁盘的读写速率,评估存储设备的性能。
- 分析:dd 命令可以天生大规模的测试数据,并通过计时等方式测量磁盘的读写速率。这对于存储设备的选型和性能优化具有重要参考价值。
Linux dd 命令详解:工作原理与实用指南(C/C++代码实现)
- size_t free_mem()
- {
- uint64_t n = 0;
- char buf[1024], found = 0;
- FILE *f = fopen("/proc/meminfo", "r");
- if (!f)
- return 1024*1024;
- memset(buf, 0, sizeof(buf));
- for (;!feof(f);) {
- fgets(buf, sizeof(buf), f);
- if (strstr(buf, "MemFree:")) {
- found = 1;
- break;
- }
- }
- fclose(f);
- if (!found)
- return 1024*1024;
- n = strtoul(buf + 9, NULL, 10);
- if (!n)
- return 1024*1024;
- /* kB? */
- if (strchr(buf + 9, 'k'))
- n <<= 10;
- else if (strchr(buf + 9, 'M'))
- n <<= 20;
- return n/2;
- }
- ...
- #ifdef ANDROID
- int copy_splice(struct dd_config *);
- int copy_splice_cores(struct dd_config *ddc)
- {
- return copy_splice(ddc);
- }
- #else
- int copy_splice_cores(struct dd_config *ddc)
- {
- int ifd, ofd, p[2] = {-1, -1};
- ssize_t r = 0, cpu_size = 0;
- size_t n = 0, min_bs = 4096;
- cpu_set_t *cpu_set = NULL;
- if (prepare_copy(ddc, &ifd, &ofd) < 0)
- return -1;
- if ((cpu_set = CPU_ALLOC(2)) == NULL) {
- close(ifd); close(ofd);
- return -1;
- }
- cpu_size = CPU_ALLOC_SIZE(2);
- CPU_ZERO_S(cpu_size, cpu_set);
- if (pipe(p) < 0) {
- ddc->saved_errno = errno;
- close(ifd); close(ofd);
- close(p[0]); close(p[1]);
- return -1;
- }
- #ifdef F_SETPIPE_SZ
- for (n = 29; n >= 20; --n) {
- if (fcntl(p[0], F_SETPIPE_SZ, 1<<n) != -1)
- break;
- }
- #endif
- n = ddc->bs;
- if (fork() == 0) {
- /* bind to CPU#0 */
- CPU_SET_S(ddc->cores - 1, cpu_size, cpu_set);
- sched_setaffinity(0, cpu_size, cpu_set);
- close(p[0]);
- for (;ddc->b_in != ddc->count && !sigint;) {
- if (n > ddc->count - ddc->b_in)
- n = ddc->count - ddc->b_in;
- r = splice(ifd, NULL, p[1], NULL, n, SPLICE_F_MORE|SPLICE_F_NONBLOCK);
- if (r == 0)
- break;
- if (r < 0) {
- if (errno != EAGAIN)
- break;
- /* If running out of pipe buffer, decrease bs */
- r = 0;
- n = min_bs;
- }
- ddc->b_in += r;
- }
- exit(0);
- }
- /* bind to CPU#1 */
- CPU_SET_S(ddc->cores - 2, cpu_size, cpu_set);
- sched_setaffinity(0, cpu_size, cpu_set);
- for (;ddc->b_out != ddc->count;) {
- r = splice(p[0], NULL, ofd, NULL, n, SPLICE_F_MORE);
- if (r <= 0) {
- ddc->saved_errno = errno;
- break;
- }
- ddc->b_out += r;
- ++ddc->rec_out;
- }
- ddc->rec_in = ddc->rec_out;
- close(ifd);
- close(ofd);
- close(p[0]);
- close(p[1]);
- wait(NULL);
- if (r < 0)
- return -1;
- return 0;
- }
- #endif
- int copy_splice(struct dd_config *ddc)
- {
- ...
- if (prepare_copy(ddc, &ifd, &ofd) < 0)
- return -1;
- if (pipe(p) < 0) {
- ddc->saved_errno = errno;
- close(ifd); close(ofd);
- close(p[0]); close(p[1]);
- return -1;
- }
- #ifdef F_SETPIPE_SZ
- for (n = 29; n >= 20; --n) {
- if (fcntl(p[0], F_SETPIPE_SZ, 1<<n) != -1)
- break;
- }
- #endif
- n = ddc->bs;
- for (;ddc->b_out != ddc->count && !sigint;) {
- if (n > ddc->count - ddc->b_out)
- n = ddc->count - ddc->b_out;
- r = splice(ifd, NULL, p[1], NULL, n, SPLICE_F_MORE);
- if (r <= 0) {
- ddc->saved_errno = errno;
- break;
- }
- ++ddc->rec_in;
- r = splice(p[0], NULL, ofd, NULL, r, SPLICE_F_MORE);
- if (r <= 0) {
- ddc->saved_errno = errno;
- break;
- }
- ddc->b_out += r;
- ++ddc->rec_out;
- }
- close(ifd);
- close(ofd);
- close(p[0]);
- close(p[1]);
- if (r < 0)
- return -1;
- return 0;
- }
- int copy_mmap(struct dd_config *ddc)
- {
- ...
- if (prepare_copy(ddc, &ifd, &ofd) < 0)
- return -1;
- if (ddc->fsize != (off_t)-1) {
- if (ftruncate(ofd, ddc->fsize) < 0) {
- ddc->saved_errno = errno;
- close(ifd);
- close(ofd);
- return -1;
- }
- }
- for (;ddc->b_out != ddc->count && !sigint;) {
- n = ddc->mmap;
- bs = ddc->bs;
- if (n > ddc->count - ddc->b_out)
- n = ddc->count - ddc->b_out;
- if (bs > n)
- bs = n;
- if (ddc->fsize == (off_t)-1) {
- if (ftruncate(ofd, ddc->b_out + n) < 0) {
- ddc->saved_errno = errno;
- break;
- }
- }
- addr = mmap(NULL, n, PROT_WRITE, MAP_SHARED, ofd, ddc->b_out + ddc->skip);
- if (addr == MAP_FAILED) {
- ddc->saved_errno = errno;
- break;
- }
- for (i = 0; i < n; i += r) {
- if (i + bs > n)
- bs = n - i;
- r = read(ifd, addr + i, bs);
- if (r <= 0) {
- ddc->saved_errno = errno;
- munmap(addr, n);
- break;
- }
- ddc->b_out += r;
- ++ddc->rec_in;
- }
- ...
- }
- if (ddc->fsize != ddc->b_out)
- ftruncate(ofd, ddc->b_out);
- close(ifd);
- close(ofd);
- if (r < 0 || addr == MAP_FAILED)
- return -1;
- return 0;
- }
- int copy_sendfile(struct dd_config *ddc)
- {
- ...
- off = ddc->skip;
- n = ddc->bs;
- for (;ddc->b_out < ddc->count && !sigint;) {
- if (n > ddc->count - ddc->b_out)
- n = ddc->count - ddc->b_out;
- r = sendfile(ofd, ifd, &off, n);
- if (r < 0) {
- ddc->saved_errno = errno;
- ret = -1;
- break;
- }
- ++ddc->rec_in; ++ddc->rec_out;
- ddc->b_in += r;
- ddc->b_out += r;
- }
- close(ifd);
- close(ofd);
- return ret;
- }
- int copy(struct dd_config *ddc)
- {
- ...
- if (ddc->cores)
- r = copy_splice_cores(ddc);
- else if (ddc->mmap)
- r = copy_mmap(ddc);
- else if (ddc->sf)
- r = copy_sendfile(ddc);
- else
- r = copy_splice(ddc);
- ddc->t_end = time(NULL);
- /* 避免div为零 */
- if (ddc->t_start == ddc->t_end)
- ++ddc->t_end;
- return r;
- }
- void print_stat(const struct dd_config *ddc)
- {
- ...
- #ifdef ANDROID
- fprintf(stderr, "%llu records in\n%llu records out\n%llu bytes (%llu MB) copied, %lu s, %f MB/s [%f mB/s]\n",
- ddc->rec_in, ddc->rec_out, ddc->b_out, ddc->b_out/(1<<20),
- ddc->t_end - ddc->t_start,
- ((double)(ddc->b_out/(1<<20)))/(ddc->t_end - ddc->t_start),
- ((double)(ddc->b_out/(1000*1000)))/(ddc->t_end - ddc->t_start));
- #else
- fprintf(stderr, "%lu records in\n%lu records out\n%lu bytes (%lu MB) copied, %lu s, %f MB/s [%f mB/s]\n",
- ddc->rec_in, ddc->rec_out, ddc->b_out, ddc->b_out/(1<<20),
- ddc->t_end - ddc->t_start,
- ((double)(ddc->b_out/(1<<20)))/(ddc->t_end - ddc->t_start),
- ((double)(ddc->b_out/(1000*1000)))/(ddc->t_end - ddc->t_start));
- #endif
- }
- void sig_int(int x)
- {
- fprintf(stderr, "SIGINT! Aborting ...\n");
- sigint = 1;
- return;
- }
- int main(int argc, char **argv)
- {
- ...
- config.bs = 1<<16;
- config.in = "/dev/stdin";
- config.out = "/dev/stdout";
- /* 模拟“dd”参数解析 */
- for (i = 1; i < argc; ++i) {
- if (strcmp(argv[i], "-h") == 0 ||
- strcmp(argv[i], "--help") == 0)
- usage(argv[0]);
- memset(buf, 0, sizeof(buf));
- if (sscanf(argv[i], "if=%1023c", buf) == 1)
- config.in = strdup(buf);
- else if (sscanf(argv[i], "of=%1023c", buf) == 1)
- config.out = strdup(buf);
- else if (sscanf(argv[i], "skip=%1023c", buf) == 1)
- config.skip = strtoul(buf, NULL, 10);
- else if (sscanf(argv[i], "seek=%1023c", buf) == 1)
- config.seek = strtoul(buf, NULL, 10);
- else if (sscanf(argv[i], "count=%1023c", buf) == 1)
- config.count = strtoul(buf, NULL, 10);
- else if (sscanf(argv[i], "mmap=%1023c", buf) == 1) {
- if (!config.cores) {
- /* Size in MB */
- config.mmap = strtoul(buf, NULL, 10);
- config.mmap <<= 20;
- }
- } else if (sscanf(argv[i], "cores=%1023c", buf) == 1) {
- config.cores = strtoul(buf, NULL, 10);
- if (config.cores < 2)
- config.cores = 2;
- config.mmap = 0;
- } else if (strcmp(argv[i], "send") == 0) {
- config.sf = 1;
- } else if (strcmp(argv[i], "direct") == 0) {
- config.direct = 1;
- } else if (sscanf(argv[i], "bs=%1023c", buf) == 1) {
- config.bs = strtoul(buf, NULL, 10);
- } else if (strcmp(argv[i], "bs") == 0) {
- config.bs = 0;
- } else if (strcmp(argv[i], "quiet") == 0) {
- config.quiet = 1;
- } else if (strcmp(argv[i], "nosync") == 0) {
- config.nosync = 1;
- }
- }
- ...
- return 0;
- }
复制代码 If you need the complete source code, please add the WeChat number (c17865354792)
编译完成后,你可以使用以下命令行参数来测试它:
./linux_dd if=<input_file> of=<output_file> [其他参数]
例如,你可以使用以下命令来复制一个文件:
./linux_dd if=/dev/stdin of=/dev/stdout bs=1024 count=1024
这个命令会从尺度输入读取数据,并将数据写入到尺度输出,每次复制 1024 字节,总共复制 1024 次。
代码中提供了多种复制计谋:
- splice(2):使用 Linux 的 splice 系统调用来在管道和文件之间传输数据,这种方式可以有用地利用内核缓冲区,减少数据复制过程中的上下文切换。
- mmap(2):通过内存映射的方式直接在内存中操纵文件数据,这种方式实用于大块数据的复制。
- sendfile(2):在内核层面直接将数据从一个文件描述符传输到另一个,减少了数据在用户空间的拷贝。
- 多核处理:代码还支持将数据复制使命分配到多个 CPU 焦点上,以提高复制效率。
增补内容
- 安全性:在使用 dd 时,肯定要小心指定正确的输入和输出文件,错误的命令大概会导致数据丢失。
- 效率:选择合适的块大小可以显著影响 dd 的性能。通常,较大的块大小可以提高复制速率。
- 错误处理:dd 命令在实行过程中大概会遇到错误,了解如何解读错误信息对于办理问题至关重要。
- 日志记载:dd 命令实行时可以重定向输出到日志文件,以便于过后分析。
- 进度监控:可以通过 status=progress 参数来监控 dd 命令的实行进度。
结语
dd 命令是一个功能强盛的工具,它在 Linux 系统中扮演偏重要的角色。了解其工作原理和使用方法,可以资助你更有用地管理和操纵数据。在使用时,务必谨慎,以克制不必要的数据丧失。
Welcome to follow WeChat official account【程序猿编码】
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |