多进程文件拷贝

本文介绍了一种利用双进程并行拷贝文件的方法,通过父进程和子进程分别拷贝文件的前后两部分来提高效率,并详细解释了如何避免文件描述符相互干扰的问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

下面用程序实现这样的功能:有一个文件的内容需要拷贝到另一个文件中,以前的做法是用一个进程来完成,下面要做的是,是两个进程同时做这件事,一个进程负责一半,如父进程负责拷贝文件的前半段,子进程负责拷贝文件的后半段。下面看一下程序的流程以及需要注意的问题:

image

其中 要注意的是当父进程调用fork函数创建子进程后,子进程同样也把父进程所打开的文件描述符fd拷贝过来,但是,在内核当中的struct file结构体只有一份。即:

image

这不是我们期望的,这样会造成两个进程之间的干扰,比如文件的当前位置就不好控制。对于文件描述符符可以这么理解:

在每个进程的进程控制块PCB当中有一个结构体指针数组 struct file *a[] = {0xa0,0xb0,……};子进程创建时,也拷贝了父进程的这个指针数组。每当一个进程p1调用open函数都返回一个整型的文件描述符fd,同时内核空间就会产生一个相应的struct file 类型的结构体,这个结构体的的地址就存放在该进程p1的PCB中的a[fd]中。而进程p1也是通过这个数组来找到相应的文件的,当进程p1执行close(fd)后,描述符a中的a[fd]不再保存相应的结构体的地址,注意:内核中那个结构体可能还存在,原因是虽然p1不再使用该文件,但是其父进程或者子进程还使用该文件,即只要有还指向该结构体的文件描述符存在,他就不会释放。还应该知道,同一个文件可以有多个struct file,即一个文件可以被打开多次,每次的fd可能不同,但是打开一次内核中就创建一个关于这个文件的struct file。

所以,在子进程中应该先将源文件a和目标文件b关闭,然后再打开,这样就不会相互干扰了。

即:

image

下面是程序:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>

#define N 64

int main(int argc, char *argv[])
{
    int fdr, fdw;  //定义文件描述符
    off_t len;    //文件长度
    pid_t pid;    //
    char buf[N] = {0};
    ssize_t n, sum = 0;
 
    if (argc != 3)    //检查输入参数是否合法
    {
        printf("usage:%s srcfile destfile\n", argv[0]);
        return 0;
    }

    if ((fdr = open(argv[1], O_RDONLY)) == -1)   //打开源文件
    {
        perror("open for reading");
        exit(-1);
    }

    if ((fdw = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, 0666)) == -1)   //打开要写的文件,即目标文件
    {
        perror("open for writing");
        exit(-1);
    }

    if ((len = lseek(fdr, 0, SEEK_END)) == (off_t)-1)  //求源文件的长度len
    {
        perror("lseek");
        exit(-1);
    }
    printf("len=%ld\n", len);

    if (-1 == ftruncate(fdw, len))  //将目标文件变成空洞文件,大小根源文件相同。
    {
        perror("ftruncate");
        exit(-1);
    }

    if ((pid = fork()) == -1)       //创建子进程
    {
        perror("fork");
        exit(-1);
    }

    if (pid == 0)//child last 1/2
    {
        close(fdr);  //将源文件先关闭
        close(fdw);  //将目标文件先关闭
        fdr = open(argv[1], O_RDONLY);  //将源文件打开
        fdw = open(argv[2], O_WRONLY);  //将目标问价打开
        lseek(fdr, len/2, SEEK_SET);    //将源文件当前位置定位到中间
        lseek(fdw, len/2, SEEK_SET);    //将目标文件当前位置定位到中间

        while ((n = read(fdr, buf, N)) > 0)  //拷贝,等于0意味着到了问价末尾
            write(fdw, buf, n);
    }
    else//parent first 1/2
    {
        lseek(fdr, 0, SEEK_SET);  //父进程重新将源文件当前位置定位到中间
        lseek(fdw, 0, SEEK_SET);  //父进程重新将目标文件当前位置定位到中间

        while ((n = read(fdr, buf, N)) > 0)
        {
            write(fdw, buf, n);
            sum += n;
            if (sum >= len / 2) //是否读够,注意:即使父进程写多了,也没关系,因为子进程会以同样的内容将其覆盖
                break;
        }
    }

    close(fdr);
    close(fdw);

    return 0;
}

拷贝完成了后,用 “diff 源文件 目标文件”进行检查。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值