3.1课后习题-多进程拷贝命令的实现0724

时间:2022-08-27 16:43:19

多进程拷贝命令

分析:单进程拷贝和多进程拷贝相比,多进程拷贝速度快,因为单进程拷贝是CPU只给一个进程时间片,而多进程是CPU给多个进程分配时间片,那这个拷贝任务用到CPU的时间相对而言就会更频繁,会更快速度的执行完。

思路:假设把a文件拷贝给b文件,
第一部分:判断文件大小,确认磁盘空间有没有足够大的空间,并且创建b文件,根据输入的进程数分割文件,计算每个进程需要复制多少字节,关闭a文件
第二部分:建立子进程,在子进程中打开重新文件,子进程需要到父进程的参数有,a文件路径,b的文件描述符,每个进程复制文本时的指针位置,每个进程应该复制的字节数。
第三部分:把复制的功能实现,这个功能的接口的形参要有a文件路径,b的文件描述符,复制时文件指针的位置,需要复制块的大小

实现的过程是从下到上的

首先把复制的功能实现
mycp.c

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

void sys_err(const char *str)
{
perror(str);
exit(-1);
}

int main(int argc, char *argv[]) //argv[1] argv[2] argv[3] argv[4] 分别是源文件,目标文件,起始指针位置,块大小
{
int fdsrc;
int fddest;
int lse = atoi(argv[3]); //把起始指针位置从char类型转换成int类型,方便函数调用
int block_size = atoi(argv[4]); //把块大小从char类型转换成int类型,方便函数调用
int len;

char buff[block_size]; //用字符串定义的方式初始化一个block_size大小的缓存,用来读fdsrc里面的数据

if(argc < 5) { //判断参数时候完整
printf("./mycpy fdsrc fddest lseek block_size\n");
exit(0);
}

if((fdsrc = open(argv[1], O_RDONLY)) < 0) //打开源文件
sys_err("open fdsrc:");

if((fddest = open(argv[2], O_CREAT|O_RDWR|O_EXCL, 0644)) < 0) { //这里创建文件的时候加上了O_EXCL这个字段,只为了当以创建的文件存在时出错返回,而不会把原有的文件删除
//printf("destfile is exist\n");
if((fddest = open(argv[2], O_RDWR)) < 0) //文件存在时以读写方式打开,这是为了方便后面的多进程调用的时候不出错
sys_err("open destfile");
}
lseek(fdsrc, lse, SEEK_SET); //把fdsrc的指针调整到指针初始位置
lseek(fddest, lse, SEEK_SET); //把fddest的指针调整到fdsrc的相应位置
len = read(fdsrc, buff, sizeof(buff)); //从文件中进行读操作
write(fddest, buff, len); // 进行写操作,长度是len,而不是block_size,这是因为文件的最后一个分块可能没有block_size这么多的内容,有多少就写多少,

close(fdsrc); //关闭文件描述符
close(fddest); //关闭文件描述符
return 0;
}

生成mycp二进制可执行程序。

由于第二部分涉及到进程原语的操作,所以就先进行第一部分

mpcy.c (multiprocess copy)

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

void sys_err(const char *str)
{
perror(str);
exit(1);
}

int blocksize(const char *phsrc, const char *phdest, int *n) //得到分割成多少块,每个块有多大
//因为进程数传进来可能会进行更改,所以把地址传进来,方便修改,return只返回块的大小
{
int fdsrc;
int fddest;
int len_src;

if((fdsrc = open(phsrc, O_RDONLY)) < 0) //打开源文件
sys_err("open fdsrc");
len_src = lseek(fdsrc, 0, SEEK_END); //得到源文件的长度(字节数)

if((fddest = open(phdest, O_CREAT|O_RDWR, 0644)) < 0) //创建目标文件
sys_err("open fddest");

if(len_src < (*n)) //如果进程的个数大于源文件的长度,也就是一个进程只复制一个1字节,这就完全没有必要,所以就把进程数设定成5
*n = 5;
else if(len_src <= 5) //如果源文件的长度小于等于5个字节,那也没有必要用多个进程,所以把进程数设定成1
*n = 1;

if(len_src%(*n) == 0) //如果源文件的长度正好能被进程数整除,返回块大小
block_n = len_src/(*n);
else //如果源文件的长度不能被进程数整除,那就把得到的块大小+1,以免漏掉数据
block_n = len_src/(*n)+1;

close(fdsrc); //关闭源文件
close(fddest); //关闭目标文件

return block_n; //返回块大小

}

int main(int argc, char *argv[]) //argv[1] argv[2] argv[3] 分别是源文件地址,目标文件地址,进程数(选填)
{
int pro; //接收进程数
int block_n; //块大小

if(argc < 3) {
printf("./mpcy fdsrc fddest\n");
exit(1);
}

if(argv[3] != 0) //当用户输入字节数的时候
pro = atoi(argv[3]);
else
pro = 5; //如果没有接收到用户输入,就默认进程数为5

block_n = blocksize(argv[1], argv[2], &pro); //得到块大小和进程数

printf("pro = %d, block_n = %d\n", pro, block_n); //打印

return 0;
}

下面进行第二部分,因为fork本人学习的不是太好,所以就只能用for循环控制次数,和子进程执行完printf之后直接exit掉来控制进程之间的关系和生长方向

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

int main(void)
{
pid_t pid;
int i = 0;
int lsek = 0; // 指针位置
int block_size = 50; //块大小
int block_n = 7;// 块个数

for(i = 0; i < block_n; i++) { 有几个块就循环几次,建立几个进程
pid = fork();
if(pid == 0) { //子进程
printf("I am child%d, lsek = %d\n", i, lsek); //
exit(1); //退出进程
}
else if(pid > 0) {
printf("I am parent i = %d\n", i);
lsek = lsek+block_size; //下一个子进程的指针起始位置
sleep(1);
} else {
perror("fork");
exit(1);
}
}
waitpid(0, 0, 0); // 用阻塞的方式回收和当前调用waitpid一个组的所有进程
return 0;
}

三个部分都单独实现的,下面是把他们组合起来,首先把这里面自己写的函数封一个函数库a.c,然后建立一个cp.h的头文件,其中mycp这个可执行文件就不用动了,其中有些变量的名字做了写更改,其他的没有什么

编译的时候命令$ gcc mpcy.c fork.c cp.h

mpcy.c

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


int main(int argc, char *argv[])
{
int block_n;
int block_size;

if(argc < 3) {
printf("./mpcy fdsrc fddest\n");
exit(1);
}

if(argv[3] != 0)
block_n = atoi(argv[3]);
else
block_n = 5;

block_size = blocksize(argv[1], argv[2], &block_n);

printf("block_n = %d, block_size = %d\n", block_n, block_size);
fork_cp(argv[1], argv[2], block_size, block_n);

return 0;
}

fork.c

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

void sys_err(const char *str)
{
perror(str);
exit(1);
}

int fork_cp(char *fdsrc, char *fddest, int block_size, int block_n)
{
pid_t pid;
int i = 0;
int lsek = 0;
char lsek_s[100];
char block_size_s[100];

for(i = 0; i < block_n; i++) {
pid = fork();
if(pid == 0) {
sprintf(lsek_s, "%d", lsek);
sprintf(block_size_s, "%d", block_size);
execlp("./mycp", "mycp", fdsrc, fddest, lsek_s, block_size_s, NULL);
exit(1);
}
else if(pid > 0) {
lsek = lsek+block_size;
} else {
pid = fork();
if(pid == 0) {
sprintf(lsek_s, "%d", lsek);
sprintf(block_size_s, "%d", block_size);
execlp("./mycp", "mycp", fdsrc, fddest, lsek_s, block_size_s, NULL);
exit(1);
}
else if(pid > 0) {
lsek = lsek+block_size;
perror("fork");
exit(1);
}
}
waitpid(0, 0, 0);
return 0;
}
int blocksize(const char *phsrc, const char *phdest, int *n)
{
int fdsrc;
int fddest;
int len_src;
int block_n;

if((fdsrc = open(phsrc, O_RDONLY)) < 0)
sys_err("open fdsrc");
len_src = lseek(fdsrc, 0, SEEK_END);

if((fddest = open(phdest, O_CREAT|O_RDWR, 0644)) < 0)
sys_err("open fddest");

if(len_src < (*n))
*n = 5;
else if(len_src <= 5)
*n = 1;

if(len_src%(*n) == 0)
block_n = len_src/(*n);
else
block_n = len_src/(*n)+1;

close(fdsrc);
close(fddest);
return block_n;
}

cp.h

#ifndef __CP_H_
#define __CP_H_
void sys_err(const char *);
int fork_cp(char *, char *, int ,int);
int blocksize(const char*, const char*, int *);
#endif

我的子进程回收做的非常不好,不知道各位能不能帮忙完善下

经实际验证发现在拷贝之后的文件上权限有问题,做了一点更改,把fork里面blocksize函数的
if((fddest = open(phdest, O_CREAT|O_RDWR, 0644)) < 0)
sys_err("open fddest");

改成
struct stat st;
stat(phsrc, &st);
if((fddest = open(phdest, O_CREAT|O_RDWR, st.st_mode&0777)) < 0)
sys_err("open fddest");

又加了回收子进程的程序,只更改的fork.c的fork_cp这个函数

重新整理之后,全部代码

mycp.c

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

void sys_err(const char *str)
{
perror(str);
exit(-1);
}

int main(int argc, char *argv[])
{
int fdsrc;
int fddest;
int lse = atoi(argv[3]);
int block_size = atoi(argv[4]);
int len;

char buff[block_size];

if(argc < 5) {
printf("./mycpy srcfile_fd destfile_fd lseek block_size\n");
exit(0);
}

if((fdsrc = open(argv[1], O_RDONLY)) < 0)
sys_err("open srcfile:");

if((fddest = open(argv[2], O_CREAT|O_RDWR|O_EXCL, 0644)) < 0)
// printf("destfile is exist\n");
if((fddest = open(argv[2], O_RDWR)) < 0)
sys_err("open destfile");


lseek(fdsrc, lse, SEEK_SET);
lseek(fddest, lse, SEEK_SET);
len = read(fdsrc, buff, sizeof(buff));
write(fddest, buff, len);

close(fdsrc);
close(fddest);
return 0;
}

fork.c

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

void sys_err(const char *str)
{
perror(str);
exit(1);
}

int fork_cp(char *fdsrc, char *fddest, int block_size, int block_n)
{
pid_t pid;
int i = 0;
int lsek = 0;
char lsek_s[100];
char block_size_s[100];
int n = block_n;

for(i = 0; i < block_n; i++) {
pid = fork();
if(pid == 0) {
sprintf(lsek_s, "%d", lsek);
sprintf(block_size_s, "%d", block_size);
execlp("./mycp", "mycp", fdsrc, fddest, lsek_s, block_size_s, NULL);
exit(1);
}
else if(pid > 0) {
lsek = lsek+block_size;
} else {
perror("fork");
exit(1);
}
}
if (i == n)
while(n > 0) {
wait(NULL);
n--;
printf("=");
}

return 0;
}

int blocksize(const char *phsrc, const char *phdest, int *n)
{
int fdsrc;
int fddest;
int len_src;
int block_n;
struct stat st;

if((fdsrc = open(phsrc, O_RDONLY)) < 0)
sys_err("open fdsrc");
len_src = lseek(fdsrc, 0, SEEK_END);

stat(phsrc, &st);

if((fddest = open(phdest, O_CREAT|O_RDWR, st.st_mode&0777)) < 0)
sys_err("open fddest");

if(len_src < (*n))
*n = 5;
else if(len_src <= 5)
*n = 1;

if(len_src%(*n) == 0)
block_n = len_src/(*n);
else
block_n = len_src/(*n)+1;

close(fdsrc);
close(fddest);
return block_n;
}

mpcy.c

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


int main(int argc, char *argv[])
{
int block_n;
int block_size;

if(argc < 3) {
printf("./mpcy fdsrc fddest\n");
exit(1);
}

if(argv[3] != 0)
block_n = atoi(argv[3]);
else
block_n = 5;

block_size = blocksize(argv[1], argv[2], &block_n);

fork_cp(argv[1], argv[2], block_size, block_n);

printf("\n");
return 0;
}

cp.h

#ifndef __CP_H_
#define __CP_H_
void sys_err(const char *);

int fork_cp(char *, char *, int ,int);

int blocksize(const char*, const char*, int *);
#endif

以上。