Linux下利用文件IO函数完成多进程复制图片,父进程复制前一半,子进程复制后一半
一 、概述
- 在Linux环境下,利用多进程完成图片的复制操作
- 本demo用到了两个进程,一个是主函数所在的父进程,一个在主函数里面创建的子进程
- 父进程负责复制图片的前一半,子进程复制图片的后一半
1.1 思路
-  最开始我想到的是一个字节一个字节复制,等复制完前一半的内容后再创建子进程,然后子进程负责后面的复制, 
-  写完后进行了改进,在父进程里面创建一个buff(char buff[128]),然后每次复制时判断是否复制到了一半,这样的效率比一个字节复制要快 
-  后来又想到了,既然是多进程,那么最好上来就创建子进程来练习,不然多进程就没有了意义 - 而这种上来就创建子进程,后面发现还是需要父进程先完成复制,再让子进程进行复制,否则会出现对文件操作的光标紊乱
- 假设:父的光标到了5,切换到子进程,子进程追加复制了20字节,当前子进程的光标就保留在了25的位置,切回到父进程,父进程从5后面开始继续复制,假设复制了30字节,再切到子进程的时候,子进程光标还处于25的位置,因此会出现光标位置错乱
 
-  由于会出现光标错乱的问题,然后想到了通过 lseek 函数 调整光标到最后,发现还是不可以,因为我用的是两个文件描述符表,而且指向的系统文件表也不是一个位置 
-  目前的想法是,如同 CPU 在终端、异常、系统调用时保留断点一样,父子进程每次复制完分别更新自己的断点,下一次更换进程后再回来时也能读取到这个断点 
-  目前的难点在于:每次复制的操作并不是一个原子操作,如何能够确保每次复制时都能够完完整整的执行完一次复制操作后再切换进程 
-  由于文件 IO 函数 lseek 在文件的尾部向后继续偏移会补充0,且这些0在填充到此位置时会变成填充的数据,那么可以直接先想复制后的文件向后偏移被复制的文件的一般字节数,然后直接向后填充,那么就可以做到多进程同时运行填充 
1.2 文件分布
- 只有一个代码文件,且功能都写在了主函数中
- 一个已经存在的图片文件,是被复制的对象
- 一个准备创建的图片文件,是复制完成后的对象
1.3 运行环境
- 直接gcc编译即可
1.4 运行环境
- 本代码是在Linux----ubuntu18.04中运行的,
- 如果报错之类的,可以检查一下是不是环境不同导致
二、上代码
picture_copy.c
#include <my_head.h>
//  图片拷贝,父进程拷贝前半部分,子进程拷贝后半部分
int main(int argc, const char *argv[])
{
    //  清空图片
    int dest = open("2.png", O_RDWR | O_CREAT | O_TRUNC, 0664);
    close(dest);
    //  打开俩文件
    int src1 = open("1.png", O_RDONLY);
    int dest1 = open("2.png", O_RDWR | O_CREAT, 0664);
    //  获取src的文件大小
    off_t size = lseek(src1, 0, SEEK_END);
    lseek(src1, 0, SEEK_SET);
    //  获取src的一半偏移量
    off_t offset = size / 2;
    ssize_t res = 0;
    int wstatus;
    //  创建子进程
    int cpid = fork();
    if (0 < cpid)
    {
        //  父进程
#if 1
        //  父进程拷贝,从头开始拷贝前一半,放到头部
        char buff[128];
        while (offset >= 0)
        {
            bzero(buff, sizeof(buff));
            //  这个地方不能用 (offset - sizeof(buff) ) >= 0 判断,
            //  因为offset是有符号数,sizeof(buff)是无符号数
            //  有符号数和无符号数运算,会转换成无符号数
            if (offset >= sizeof(buff))
            {
                /*
                    如果前半部分剩余的比设定的缓冲区大
                    直接读取,读写完再改变剩余的计数量
                */
                res = read(src1, &buff, sizeof(buff));
                if (res > 0)
                {
                    write(dest1, &buff, res);
                }
                else
                {
                    if (res == -1)
                    {
                        ERR_MSG("read");
                    }
                    break;
                }
            }
            else
            {
                /*
                    如果前半部分剩余的比设定的缓冲区小或者相等
                    读取剩余的量
                    要注意,这个地方只需要读一次,因为到中间了
                */
                // printf("offset = %ld\n", offset);
                res = read(src1, &buff, offset);
                write(dest1, &buff, res);
            }
            //  更新offset
            offset -= sizeof(buff);
        }
        wait(&wstatus);
        if (WEXITSTATUS(wstatus) == 1)
        {
            close(src1);
            close(dest1);
        }
#endif
    }
    else if (0 == cpid)
    {
#if 1
        // 子进程拷贝,从中间向后拷贝
        int src2 = open("1.png", O_RDONLY);
        int dest2 = open("2.png", O_RDWR | O_CREAT, 0664);
        // printf("offset ==== %ld\n", offset);
        lseek(src2, offset, SEEK_SET);
        lseek(dest2, offset, SEEK_SET);
        char buff[128];
        while (1)
        {
            bzero(buff, sizeof(buff));
            res = read(src2, buff, sizeof(buff));
            if (res > 0)
            {
                write(dest2, buff, res);
            }
            else
                break;
        }
#endif
        exit(1);
    }
    else
    {
        ERR_MSG("fork");
    }
    return 0;
}
运行结果
















![[刷题记录]牛客面试笔刷TOP101(一)](https://img-blog.csdnimg.cn/a570e15940af478982b3da48593005dd.png)



