进程和程序(二)

IPC(进程间通信)概念

IPC:InterProcess Communication,通过内核提供的缓冲区进行数据交换的机制。
在这里插入图片描述
IPC通信的方式有几种呢?
1、pipe 管道–最简单
只支持有血缘关系的进程进行通信。

2、fifo 有名管道

3、文件映射共享IO – 速度最快

4、本地socket – 最稳定

5、信号 – 携带信息量最小

6、共享内存

7、消息队列

这里主要介绍前三种。

管道

pipe函数

在这里插入图片描述
参数pipfd[2]为两个文件描述符。其中,pipfd[0]代表读,pipfd[1]代表写。
定义两个成员的pipfd数组,然后传入pipe函数,pipe函数会进行初始化,初始化完成之后就相当于在内核中开辟了缓冲区。

管道被称为半双工通信。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

父子进程间通信

1、子进程写,父进程读

#include <stdio.h>
#include <unistd.h>
#include <string.h>

int main(int argc,char* argv[])
{
    int fd[2];
    pipe(fd);
    //已建立好管道,fd[0]为读端,fd[1]为写端
    pid_t pid = fork();
    if(pid == 0)
    {
        //son
        write(fd[1],"hello",5);
    }
    else if(pid > 0)
    {
        int ret;
        //parent
        char buf[10];
        memset(buf,0,sizeof(buf));
        ret = read(fd[0],buf,sizeof(buf));

        if(ret>0)
        {
            write(STDOUT_FILENO,buf,ret);
        }
    }
    return 0;
}

2、利用父子进程的pipe通信,实现ps aux|grep bash功能

“|”是管道命令操作符

“|”是管道命令操作符,简称管道符。利用Linux所提供的管道符“|”将两个命令隔开,管道符左边命令的输出就会作为管道符右边命令的输入。连续使用管道意味着第一个命令的输出会作为 第二个命令的输入,第二个命令的输出又会作为第三个命令的输入,依此类推。
它仅能处理经由前面一个指令传出的正确输出信息,也就是 standard output 的信息,对于 standard error 信息没有直接处理能力。

在这里插入图片描述
用法示例:
ls -l | more

该命令的含义是列出当前目录中的文档信息,并把输出送给more命令作为输入,more命令分页显示文件列表。

利用父子进程的pipe通信,实现ps aux|grep bash功能

把"|"比作管道,将ps aux命令的输出写入管道,然后grep bash命令去读取管道,并把结果显示出来。
这里让子进程负责ps aux,然后将输出写入管道。
让父进程负责grep bash,然后去读取管道。
在这里插入图片描述
已知,ps aux会将结果输出到标准输出,而现在想要将结果写到管道,因此,需要进行输出重映射。同理,grep接收(读取)时是也是默认从标准输入中读取的,因此,也需要进行输入重映射。实现重映射使用dup2函数。

我们画图来分析:
在这里插入图片描述

#include <stdio.h>
#include <unistd.h>

int main(int argc,char* argv[])
{
    int fd[2];
    pipe(fd);

    pid_t pid = fork();

    if(pid == 0)
    {
        //son
        //son实现ps aux
        //需要将标准输出重定向到管道的写端
        dup2(fd[1],STDOUT_FILENO);//注意dup2的参数顺序
        execlp("ps","ps","aux",NULL);
    }
    else if(pid > 0)
    {
        //parent
        //parent实现grep bash
        //需要将标准输入重定向到管道的读端
        dup2(fd[0],STDIN_FILENO);
        execlp("grep","grep","bash",NULL);
    }

    return 0;
}

一般情况下,会在父进程中回收子进程,这里没有实现。但等父进程运行完毕退出,僵尸进程也就没有了。

运行程序:
在这里插入图片描述
注意:上图中的光标处,说明grep bash还在运行。
另开一个终端,然后使用ps aux|grep bash命令来验证下。
在这里插入图片描述
结果一致。
再使用ps aux命令来查看下。
在这里插入图片描述
会发现僵尸进程,因为我们在父进程还在运行,父进程也没有回收子进程。等父进程运行完毕退出,僵尸进程也就没有了。

改进代码,让grep完成后就退出。

grep还需要进一步来理解。
grep:过滤内容。
grep “hello” [文件名]
含义是:在指定文件中查找带"hello"的内容,并显示出来。

如果不带文件名,比如执行
grep “hello”
就会堵塞住,然后等待标准输入,当匹配到输入的带有指定的字符串时,就会反馈出来。
在这里插入图片描述
要解决这个问题,就涉及到管道的特性。
在这里插入图片描述
从上图中,可以看到,父子进程都指向管道的读写两端。当子进程运行完毕退出后,就不再指向管道的读写两端了,但是父进程自己的还指向着管端的读写两端。
父进程认为还有写端没有关闭,还会有人(进程)在某个时刻会给它写入数据,所以会一直处在等待输入的状态。
在这里插入图片描述

因此,父进程只负责写,所以父进程应该在重映射之前把写端给关闭。
还有,子进程只负责读,所以子进程应该在重映射之前把读端给关闭。

管道的读写行为

在这里插入图片描述

示例一 父进程读管道,写端全部关闭。

#include <stdio.h>
#include <unistd.h>

int main(int argc,char* argv[])
{
    int fd[2];
    pipe(fd);

    pid_t pid = fork();

    if(pid == 0)
    {
        //son
        //关闭读端
        close(fd[0]);
        //沉睡三秒
        sleep(3);
        //写数据
        write(fd[1],"hello\n",6);
        //写完关闭写端
        close(fd[1]);
        //让子进程陷入沉睡
        while(1)
        {
            sleep(1);
        }
    }
    else if(pid > 0)
    {
        //parent
        //关闭写端
        close(fd[1]);
        char buf[10] = {0};
        //循环read
        while(1)
        {
            int ret = read(fd[0],buf,sizeof(buf));
            if(ret == 0)
            {
                printf("read over.\n");
                break;
            }
            if(ret > 0)
            {
                write(STDOUT_FILENO,buf,ret);
            }
        }
    }

    return 0;
}

运行结果:
在这里插入图片描述

示例二 子进程写管道,读端全部关闭。

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

int main(int argc,char* argv[])
{
    int fd[2];
    pipe(fd);

    pid_t pid = fork();

    if(pid == 0)
    {
        //son
        //关闭读端
        close(fd[0]);
        //沉睡三秒
        sleep(3);
        //写数据
        write(fd[1],"hello\n",6);
        //让子进程陷入沉睡
        while(1)
        {
            sleep(1);
        }
    }
    else if(pid > 0)
    {
        //parent
        //关闭写端
        close(fd[0]);
        close(fd[1]);
        //验证:读端全部关闭,产生一个信号SIGPIPE,
        //子进程因异常而退出,父进程使用wait函数回
        //收子进程,打印输出是否是被该信号杀死。
        int status;
          // 传入status的地址
        pid_t wait_id = wait(&status);
        printf("wait ok,wait_id = %d.\n",wait_id);

        // 如果WIFEXITED(status)为真,说明是正常死亡
        if(WIFEXITED(status))
        {
            printf("the reason of child die is %d.\n", WEXITSTATUS(status));
        }
        if(WIFSIGNALED(status))
        {
            printf("the reason of child die is %d signal.\n", WTERMSIG(status));
        }

        //让父进程陷入休眠
        while(1)
        {
            sleep(1);
        }
    }

    return 0;
}

使用kill -l命令来看看13号信号是什么
在这里插入图片描述

管道缓冲区大小

可以使用 ulimit -a命令来查看当前系统中创建管道文件所对应的内核缓冲区大小。通常为:8*512 byte。管道的大小和管道的容量还不一样,比如上面提到的管道已满,说的实际上是管道的容量,管道的容量比管道的大小要大一些。
在这里插入图片描述
在这里插入图片描述
对于缺点1的意思是:为了保证没有分叉路,通常只能由父进程写/读,子进程读/写。

FIFO

FIFO常被称为命名管道,以区分管道,管道只能用于有血缘关系的进程间,但通过FIFO,不相关的进程也能交换数据。

FIFO是Linux基础文件类型中的一种(p,管道文件)。但FIFO文件在磁盘上没有数据块,仅仅用来标识内核中一条通道。各进程可以打开这个文件进行read/write,实际上是在读写内核通道,这样就实现了进程间通信。另外,使用统一fifo文件,可以有多个读端和多个写端。实际上就是对文件进行读写。

创建fifo文件的方式有两种,一种是使用命令,一种是使用函数。
在这里插入图片描述
在这里插入图片描述
mkfifo函数在第三章。
在终端中输入命令 man 3 mkfifo 可查看。
在这里插入图片描述

写两个程序来实现下(一个运行的程序就是一个进程)

先写个写的

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

int main(int argc,char *argv[])
{
    if(argc!=2)
    {
        printf("%s filepath",argv[0]);
        return -1;
    }

    //当前目录有一个fifo文件,否则还要创建
    //以只写方式打开fifo文件
    int fd = open(argv[1],O_WRONLY);
    //写
    char buf[256];
    int num=1;
    while(1)
    {
        memset(buf,0x00,sizeof(buf));
        sprintf(buf,"hello world %04d\n",num++);
        //如果用sizeof,就是256。
        write(fd,buf,strlen(buf));
        sleep(1);
    }
    //关闭
    close(fd);

    return 0;
}

再写个读的

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

int main(int argc,char *argv[])
{
    if(argc!=2)
    {
        printf("%s filename.\n",argv[0]);
        return -1;
    }

    int fd = open(argv[1],O_RDONLY);
    //读,输出到屏幕
    char buf[256];
    int ret=0;
    do
    {
        //从fd中读,读到buf中,最多读sizeof(buf)个字节。
        //fd会递增的,最终指向文件末尾
        ret=read(fd,buf,sizeof(buf));
        //STDOUT_FILENO是标准输出的宏定义,值为1
        write(STDOUT_FILENO,buf,ret);
    }while(ret>0);//代表读到末尾了

    close(fd);

    return 0;
}

编译执行,那到底应该先执行哪一个呢?
通过man 2 open可以看到这么一段话。
在这里插入图片描述
在这里插入图片描述
补充:
使用man 7 fifo
可以查看FIFO的详细信息。

mmap共享映射区

mmap的说明

假设磁盘上有文件file.txt,mmap的作用是在内存中划分一块区域,然后将这个文件的某一段(部分)映射到内存。这样直接操作映射(内存)区域之后,就可以更改文件的内容。映射时,需要指定offset和length,因为使用内存,所以mmap这种进程间通信的方式的速度最快。
在这里插入图片描述

mmap函数

看看mmap函数的使用方法。
在这里插入图片描述
参数:
addr:传NULL。
length:映射区的长度。
prot:
在这里插入图片描述
一般使用PROT_READ(可读)和PROT_WRITE(可写)。
flags:
MAP_SHARED:映射区是共享的。对内存的修改会影响到源文件
MAP_PRIVATE:映射区是私有的。对内存的修改不会影响到源文件

fd:文件描述符,open打开一个文件。
offset:偏移量,从文件的哪部分开始映射。

返回值:

成功:返回可用的内存首地址。
在这里插入图片描述
失败:返回MAP_FAILED
在这里插入图片描述

munmap函数

释放映射区。
在这里插入图片描述
参数:
addr:传mmap的返回值。
length:mmap创建的映射区的长度。

返回值:
在这里插入图片描述

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

int main()
{
    //在当前目录下新建一个mem.txt的文件,然后在里面写一些内容
    int fd = open("mem.txt",O_RDWR);

    char* mem = mmap(NULL,8,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);

    if(mem == MAP_FAILED)
    {
        perror("mmap error.");
        return -1;
    }

    //修改内存区域相当于修改文件
    strcpy(mem,"hello" );

    //释放mmap
    munmap(mem,8);
    close(fd);

    return 0;
}

mmap注意事项(一)

1、如果更改mem变量的地址,释放的时候munmap,传入mem还能成功吗?
答:不能

2、如果对mem越界操作会怎么样,比如length传8个字节,修改的却不止8个?
答:文件的大小对映射区操作有影响,应该避免。
比如文件大小只有8个字节,而你要往里写10个字节的数据,结果是只能写进8个字节。

3、如果文件偏移量随便填个数会怎么样?
现在,我们新建一个相对大一些的文件。
怎么做呢?

使用命令ps aux > mem.txt
这条命令的含义是将ps aux的结果输出到mem.txt这个文件。
再来看下这个文件的信息:
在这里插入图片描述
然后将程序中offset修改为1000试试:

char* mem = mmap(NULL,8,PROT_READ|PROT_WRITE,MAP_SHARED,fd,1000);

在这里插入图片描述
答:报错,原因是offset必须是 4K (4096) 的整数倍。

4、如果文件描述符先关闭,对mmap映射有没有影响?
答:没有影响。

5、open的时候,可以新创建一个文件来创建映射区吗?
将当前目录下的 “mem.txt” 给删除,然后在程序中写打开文件时创建。
在这里插入图片描述
结果运行时发生总线错误。
在这里插入图片描述
这说明,新建一个大小为0的文件是不可以的。
但是,如果在新建时给这个文件一定大小呢,再试试。
使用函数ftruncate()给这个文件一定的大小。
在这里插入图片描述
这里还需要注意的就是

1、mmap的length参数不能大于文件的大小,
2、mmap的offset参数不能大于文件的大小。

否则也会报错。
在这里插入图片描述
上面的mmap函数的offset参数改成0后,程序运行正常。

函数ftruncate()

函数功能:改变文件大小
函数原型:int ftruncate(int fd, off_t length)
函数说明:ftruncate()会将参数fd指定的文件大小改为参数length指定的大小。参数fd为已打开的文件描述词,而且必须是以写入模式打开的文件。如果原来的文件件大小比参数length大,则超过的部分会被删去。
返 回 值:0、-1
错误原因:
EBADF: 参数fd文件描述词为无效的或该文件已关闭。
EINVAL: 参数fd为一socket并非文件,或是该文件并非以写入模式打开。

mmap注意事项(二)

6、open的时候,只选择O_WRONLY可以吗?
在这里插入图片描述
运行程序:
在这里插入图片描述
所以,不可以。
7、open的时候,只选择O_RDONLY可以吗?
运行程序:
在这里插入图片描述
所以,也不可以。
8、当选择MAP_SHARED的时候,open文件选择O_RDONLY,prot选择PROT_READ|PROT_WRITE可以吗?
在这里插入图片描述
那怎样才能可以呢?
答:当选择 MAP_SHARED的时候,open文件时的权限必须大于或等于prot的权限。

注意:使用mmap这个函数时一定要判断返回值。

mmap实现父子进程通信

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

int main()
{
    //先创建映射区
    int fd =open("mem.txt",O_RDWR);
    int *mem = mmap(NULL,4,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
    if(mem == MAP_FAILED)
    {
        perror("mmap error");
        return -1;
    }

    //fork进程
    pid_t pid = fork();
    if(pid == 0)
    {
        //son
        *mem=100;
        printf("child,*mem = %d.\n",*mem);
        sleep(3);
        printf("child,*mem = %d.\n",*mem);
    }
    else if(pid>0)
    {
        //parent
        sleep(1);
        printf("parent,*mem = %d.\n",*mem);
        *mem = 1001;
        printf("parent,*mem = %d.\n",*mem);
        wait(NULL);
    }

    munmap(mem,4);
    close(fd);
    return 0;
}

匿名映射

通过使用我们发现,使用映射区来完成文件读写操作十分方便,父子进程间通信也较容易。但缺陷是,每次创建映射区一定要依赖一个文件才能实现。通常为了建立映射区要open一个temp文件,创建好了再unlink、close掉,比较麻烦。 可以直接使用匿名映射来代替。

Linux系统给我们提供了创建匿名映射区的方法,无需依赖一个文件即可创建映射区。但需要借助标志位参数flags来指定。

这个标志位参数为:MAP_ANONYMOUS (或它的简写MAP_ANON)。并且此时fd传参-1。

用法举例如下:
int *p = mmap(NULL, 4, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0);

使用匿名映射将上个代码进行改写。

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

int main()
{
    int *mem = mmap(NULL,4,PROT_READ|PROT_WRITE,MAP_SHARED|MAP_ANON,-1,0);

    if(mem == MAP_FAILED)
    {
        perror("mmap error");
        return -1;
    }

    //fork进程
    pid_t pid = fork();
    if(pid == 0)
    {
        //son
        *mem=100;
        printf("child,*mem = %d.\n",*mem);
        sleep(3);
        printf("child,*mem = %d.\n",*mem);
    }
    else if(pid>0)
    {
        //parent
        sleep(1);
        printf("parent,*mem = %d.\n",*mem);
        *mem = 1001;
        printf("parent,*mem = %d.\n",*mem);
        wait(NULL);
    }

    munmap(mem,4);
    return 0;
}

需注意的是,MAP_ANONYMOUS和MAP_ANON这两个宏是Linux操作系统特有的宏。在unix和类Unix操作系统中如无该宏定义。

其实可以在unix和类Unix操作系统中使用如下两步来完成匿名映射区的建立:

fd = open("/dev/zero", O_RDWR);
p = mmap(NULL, size, PROT_READ|PROT_WRITE, MMAP_SHARED, fd, 0);

Linux系统中的两个设备文件:
/dev/zero,聚宝盆,可以随意映射。
/dev/null,无底洞,一般错误信息重定向到该文件中。

mmap实现无血缘关系的进程间通信

进程之间要想通信的话,mmap函数中的flags参数必须设置为MMAP_SHARED。
在这里插入图片描述

写(文件)进程
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <sys/wait.h>

typedef struct _Student{
    int sid;
    char sname[20];
}STU;

int main(int argc,char* argv[])
{
    if(argc!=2)
    {
        printf("%s\n",argv[0]);
        return -1;
    }

    //1.open file
    int fd =open(argv[1],O_RDWR|O_CREAT|O_TRUNC,0666);
    int length = sizeof(STU);
    ftruncate(fd,length);

    //2.mmap
    STU *stu = mmap(NULL,length,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);

    if(stu == MAP_FAILED)
    {
        perror("mmap error");
        return -1;
    }

    int num = 1;
    //3.修改内存数据
    while(1)
    {
        stu->sid =num;
        sprintf(stu->sname,"child-%03d",num++);
        sleep(1);
    }
    //4.释放映射区和关闭文件描述符
    munmap(stu,length);
    close(fd);
    return 0;
}
读(文件)进程
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <sys/wait.h>

typedef struct _Student{
    int sid;
    char sname[20];
}STU;

int main(int argc,char* argv[])
{
    if(argc!=2)
    {
        printf("%s\n",argv[0]);
        return -1;
    }

    //1.open file
    int fd =open(argv[1],O_RDWR);
    int length = sizeof(STU);

    //2.mmap
    STU *stu = mmap(NULL,length,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);

    if(stu == MAP_FAILED)
    {
        perror("mmap error");
        return -1;
    }

    //3.读取内存数据
    while(1)
    {
        printf("sid = %d,sname = %s.\n",stu->sid,stu->sname);
        sleep(1);
    }
    //4.释放映射区和关闭文件描述符
    munmap(stu,length);
    close(fd);
    return 0;
}

作业

1、通过命名管道传输数据,进程A和进程B,进程A将一个文件(MP3)发送给进程B。

程序设计思路大概是这样的.
1、先在当前目录下创建一个FIFO文件;
2、复制一个MP3文件到当前路径下。
3、write.c中读取该MP3文件并写入到FIFO中。与此同时,read.c中读取FIFO并写到新建的mp3文件中(用open函数打开或创建的方式)

读取mp3文件并写入到FIFO文件中

write.c
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>

//通过命名管道传输数据,进程A和进程B,进程A将一个文件(MP3)发送给进程B。
int main(int argc,char *argv[])
{
    if(argc!=3)
    {
        printf("%s mp3_filepath fifo_filepath",argv[0]);
        return -1;
    }

    //读取mp3文件内容然后写入FIFO中,边读边写。
    //当前目录有一个fifo文件,否则还要创建

    //以只读方式打开mp3文件
    int fd_mp3 = open(argv[1],O_RDONLY);
    //以只写方式打开fifo文件
    int fd_fifo = open(argv[2],O_WRONLY);

    //写到FIFO中
    char buf[4096];
    int ret=0;

    do
    {
        //从fd_mp3中读,读到buf中,最多读sizeof(buf)个字节。
        //fd_mp3会递增的,最终指向文件末尾
        ret=read(fd_mp3,buf,sizeof(buf));
        //测试是否不管文件中还剩多少字节,每次都读count
        write(fd_fifo,buf,ret);
    }while(ret>0&&ret==sizeof(buf));//代表读到末尾了

    //关闭fifo
    close(fd_fifo);
    //关闭mp3
    close(fd_mp3);

    return 0;
}

读取FIFO文件并写入到新建的mp3文件中

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

int main(int argc,char *argv[])
{
    if(argc!=3)
    {
        printf("%s mp3_filename fifo_filename.\n",argv[0]);
        return -1;
    }

    int fd_mp3 = open(argv[1],O_RDWR|O_CREAT,0666);

    int fd_fifo = open(argv[2],O_RDONLY);

    char buf[4096];
    int ret=0;
    do
    {
        ret=read(fd_fifo,buf,sizeof(buf));
        write(fd_mp3,buf,ret);
    }while(ret>0);//代表读到末尾了

    close(fd_fifo);
    close(fd_mp3);

    return 0;
}

2、实现多进程拷贝文件。

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

int main(int argc,char* argv[])
{
    //默认5个进程
    int n=5;
    //输入参数至少是3,第四个参数可以是进程数
    if(argc<3)
    {
        printf("%s sourcefilepath destinationfilepath pidnum.\n",argv[0]);
        return -1;
    }
    if(argc == 4)
    {
        n = atoi(argv[3]);
    }

    //open source file
    int srcfd =open(argv[1],O_RDONLY);
    if(srcfd < 0)
    {
        perror("open source error.\n");
        exit(1);
    }

    //open destination file
    int dstfd = open(argv[2],O_RDWR|O_CREAT|O_TRUNC,0666);
    if(dstfd<0)
    {
        perror("open destination error.\n");
        exit(1);
    }
    
	//使用stat函数从源文件获得文件大小
    struct stat sb;
    stat(argv[1],&sb);
    int length = sb.st_size;
    truncate(argv[2],length);

    //将源文件映射到缓冲区
    //mmap
    char* psrc = mmap(NULL,length,PROT_READ,MAP_SHARED,srcfd,0);
    if(psrc == MAP_FAILED)
    {
        perror("mmap src error.\n");
        exit(1);
    }

    //将目标文件映射到缓冲区
    //mmap
    char* pdst = mmap(NULL,length,PROT_READ|PROT_WRITE,MAP_SHARED,dstfd,0);
    if(pdst == MAP_FAILED)
    {
        perror("mmap src error.\n");
        exit(1);
    }

    //创建多个进程
    int i;
    for(i=0;i<n;i++)
    {
        if(fork()==0)
            break;
    }

    //计算子进程需要拷贝的起点和大小
    int cpsize = length / n;
    int mod    = length % n;
	
	//数据拷贝,memcpy
    if(i<n)//子进程
    {
        if(i == n-1)//最后一个子进程
        {
            //void *memcpy(void *dest, const void *src, size_t n);
            //从存储区str2复制n个字节到存储区str1
            memcpy(pdst+i*cpsize,psrc+i*cpsize,cpsize+mod);
        }
        else
        {
            memcpy(pdst+i*cpsize,psrc+i*cpsize,cpsize);
        }
    }
    else//父进程回收子进程
    {
        for(i=0;i<n;i++)
        {
            wait(NULL);
        }
    }

    //释放映射区和关闭文件描述符
    if(munmap(psrc,length)<0)
    {
        perror("munmap source error.\n");
        exit(1);
    }

    if(munmap(pdst,length)<0)
    {
        perror("munmap destination error.\n");
        exit(1);
    }
    close(srcfd);
    close(dstfd);

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xuechanba

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值