01.思维导图
02.创建一个进程扇
#include <25051head.h>
int main(int argc, const char *argv[])
{pid_t pid;int i;for(i=0;i<4;++i){pid=fork();if(pid==0){//printf("子进程:pid=[%d]\n",pid);printf("子进程%d:子进程pid=[%d],父进程pid=[%d]\n",i+1,getpid(),getppid());//子进程输出信息后退出循环,不再创建新进程break;}else if(pid<0){ERRLOG("fork_error..\n");exit(EXIT_FAILURE);}}if(pid>0){sleep(1); //父进程等待所有子进程结束int status;pid_t wpid;i=1;while((wpid=wait(&status))>0){//打印子进程结束信息printf("子进程%d:%d已结束,退出状态:%d\n",i,wpid,WEXITSTATUS(status));i++;}}//while(1);return 0;
}
25051head.h
#ifndef __25051HED_H__
#define __25051HED_H__
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <ctype.h>
#include <sys/types.h>//引入open函数
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>//引入 getdtablesize函数,获取文件描述符个数,包含close函数
#include <time.h>
#include <sys/wait.h>#define ERRLOG(msg) do{printf("__%d__",__LINE__);fflush(stdout);perror(msg);return -1;}while(0)
#endif
03.创建一个进程扇
#include <25051head.h>
int main(int argc, const char *argv[])
{pid_t pid;int i;//打印自定义的进程编号的pid_t child_pids[4];int child_index[4];for (i = 0; i<4;++i) {pid = fork();if (pid < 0) {perror("fork");exit(EXIT_FAILURE);} else if (pid == 0) {// 子进程继续循环创建下一个子进程printf("进程 %d,进程 ID: %d,父进程 ID: %d\n", i+1, getpid(), getppid());} else {// 父进程退出循环 记录子进程对应的PID和对应的序号child_pids[i]=pid;child_index[i]=i+1;break;}}if (pid > 0) {// 父进程等待子进程结束int status;pid_t wpid;while((wpid= wait(&status))>0){for(int j=0;j<4;++j){if(child_pids[j]==wpid){printf("父进程%d: %d 等待子进程结束后退出\n",child_index[j],getpid());}}}} else {// 最后一个子进程不需要等待,直接退出if (i == 3) {printf("最后一个子进程 %d 退出\n", getpid());}}//while(1);return 0;
}
学习总结:
1. 进程结构
链式进程:呈现出线性的层次结构。每个进程(除了最后一个子进程)只创建一个子进程,形成类似链条的结构。例如,进程 A 创建进程 B,进程 B 创建进程 C,进程 C 创建进程 D,以此类推。
扇形进程:是一种扁平的结构。由一个父进程一次性创建多个子进程,所有子进程都是该父进程的直接子进程。例如,进程 A 同时创建进程 B、C、D、E 等。
2. 创建逻辑
链式进程:代码里,子进程会继续循环创建下一个子进程,而父进程创建子进程后就退出循环。一般通过在 fork 后,子进程不跳出循环,父进程跳出循环来实现。示例代码如下:
// 链式进程示例for (i = 0; i < 4; ++i) {pid = fork();if (pid < 0) {perror("fork");exit(EXIT_FAILURE);} else if (pid == 0) {// 子进程继续循环创建下一个子进程} else {// 父进程退出循环break;} }
扇形进程:父进程通过循环多次调用 fork 函数创建多个子进程,每个子进程创建后直接退出循环,不再创建新的子进程。示例代码如下:
// 扇形进程示例// 扇形进程示例 for (i = 0; i < 4; ++i) {pid = fork();if (pid < 0) {perror("fork");exit(EXIT_FAILURE);} else if (pid == 0) {// 子进程输出信息后退出循环,不再创建新进程break;}// 父进程继续循环创建子进程 }
3. 资源管理
链式进程:每个进程只需要管理自己创建的一个子进程,资源管理相对简单。父进程只需等待自己的直接子进程结束,就能避免僵尸进程。
扇形进程:父进程需要管理多个子进程,要确保等待所有子进程结束,防止出现多个僵尸进程。通常使用循环调用 wait 或 waitpid 函数来等待所有子进程。
4. 复杂度和应用场景
链式进程:结构简单,逻辑清晰,适用于任务需要按顺序依次执行,每个任务依赖前一个任务结果的场景。
扇形进程:可以充分利用多核 CPU 的并行计算能力,适用于多个独立任务可以同时执行的场景,比如并行处理多个文件、同时进行多个网络请求等。
0.4.解析代码
代码解析结果:
#include <25051head.h>
int main(int argc, const char *argv[])
{pid_t pid1,pid2;//返回值->父进程:pid>0;子进程:pid=0;运行失败:pid<0if((pid1=fork())==0){sleep(3);printf("info1 from child process_1\n");exit(0);printf("info2 from child process_1\n");}else{if((pid2=fork())==0){sleep(1);printf("info1 from child process_2\n");exit(0);}else{sleep(7);printf("info1 from parent process\n");printf("info2 from parent process");_exit(0);} }while(1);return 0;
}
输出结果:
info1 from child process_2
info1 from child process_1
info1 from parent process
05.实现文件的拷贝,父进程拷贝前一部分,子进程拷贝后一部分
方法一:标准IO
#include <25051head.h>
int main(int argc, const char *argv[])
{//1.打开文件FILE* src_fp=fopen("/home/ubuntu/25041/linux/lj/day21_IO/hm/my.txt","a+");if(NULL==src_fp){ERRLOG("src_fopen_error");}printf("src_fopen_success..\n");FILE* dest_fp=fopen("/home/ubuntu/25041/linux/lj/day21_IO/hm/my2.txt","w+");if(NULL==dest_fp){ERRLOG("dest_fopen_error");}printf("dest_fopen_success..\n");//2.循环写入数据#if 1//循环输入char buf[128]="";while(1){memset(buf,0,sizeof(buf));printf("请输入buf:");if(scanf("%s",buf)!=1){ERRLOG("scanf_error");}if(strcmp(buf,"#")==0){printf("输入字符#结束输入.\n");break;}
#if 1if(EOF==fputs(buf,src_fp)){printf("src_fputs_error..\n");return -1;}//写入换行符,方便读取时按行处理if(EOF==fputc('\n',src_fp)){printf("src_fputs_error..\n");return -1;}
#endif}
#if 0snprintf(buf,sizeof(buf)-1,"%s",buf);size_t res=fwrite(buf,1,strlen(buf),src_fp);if(res<strlen(buf)){printf("src_fwrite_error");return -1;}rewind(src_fp);
#endif#endiffflush(src_fp);rewind(src_fp);//3.读取文件size_t res;while(1){memset(buf,0,sizeof(buf));res=fread(buf,1,sizeof(buf)-1,src_fp);if(res>0){//printf("%s\n",buf);}if(feof(src_fp)){printf("读取到文件的结尾..\n");break;}if(ferror(src_fp)){printf("函数运行失败..\n");break;}}//获取文件大小fseek(src_fp,0,SEEK_END);long file_size=ftell(src_fp);printf("file_size=%ld\n",file_size);rewind(src_fp);//创建子进程pid_t pid=fork();if(pid<0){ERRLOG("fork_error");}else if(pid==0){//sleep(1);FILE* child_src_fp=fopen("/home/ubuntu/25041/linux/lj/day21_IO/hm/my.txt","r");FILE* child_dest_fp=fopen("/home/ubuntu/25041/linux/lj/day21_IO/hm/my2.txt","r+");if(child_src_fp==NULL||child_dest_fp==NULL){ERRLOG("child_open_error");}//子进程处理文件后半部分的内容fseek(child_src_fp,file_size/2,SEEK_SET);fseek(child_dest_fp,file_size/2,SEEK_SET);while((res=fread(buf,1,sizeof(buf),child_src_fp))>0){if(fwrite(buf,1,res,child_dest_fp)!=res){printf("子进程:dest_fwrite_error\n");fclose(child_src_fp);fclose(child_dest_fp);return -1;}}fclose(child_src_fp);fclose(child_dest_fp);exit(EXIT_SUCCESS);}else{//sleep(1);//父进程处理文件前半部分while(ftell(src_fp)<file_size/2){res=fread(buf,1,sizeof(buf),src_fp);if(res>0){if(fwrite(buf,1,res,dest_fp)!=res){printf("父进程:dest_fwrite_error\n");return -1;}}}//等待子进程结束int status;wait(&status);}//4.关闭文件if(EOF==fclose(src_fp)){ERRLOG("src_fclose_error");}printf("src_fclose_success..\n");if(EOF==fclose(dest_fp)){ERRLOG("dest_fclose_error");}printf("dest_fclose_success..\n");return 0;
}
方法二:文件IO(不需要考虑缓冲区的问题,不需要重新再打开文件在子进程中)
#include <25051head.h>
int main(int argc, const char *argv[])
{umask(0);//1.打开文件int src_fd = open("/home/ubuntu/25041/linux/lj/day21_IO/hm/my.txt", O_RDWR | O_CREAT | O_APPEND, 0777);if (src_fd == -1) {ERRLOG("src_open_error");}printf("src_open_success..\n");int dest_fd = open("/home/ubuntu/25041/linux/lj/day21_IO/hm/my2.txt", O_RDWR | O_CREAT | O_TRUNC, 0777);if (dest_fd == -1) {ERRLOG("dest_open_error");}printf("dest_open_success..\n");//2.循环写入数据char buf[128] = "";while (1) {memset(buf, 0, sizeof(buf));printf("请输入buf:");if (scanf("%s", buf) != 1) {ERRLOG("scanf_error");}if (strcmp(buf, "#") == 0) {printf("输入字符#结束输入.\n");break;}ssize_t write_res = write(src_fd, buf, strlen(buf));if (write_res == -1) {printf("src_write_error..\n");close(src_fd);close(dest_fd);return -1;}// 写入换行符,方便读取时按行处理write_res = write(src_fd, "\n", 1);if (write_res == -1) {printf("src_write_error..\n");close(src_fd);close(dest_fd);return -1;}}// 获取文件大小off_t file_size = lseek(src_fd, 0, SEEK_END);printf("file_size=%ld\n", (long)file_size);lseek(src_fd, 0, SEEK_SET);// 创建子进程pid_t pid = fork();if (pid < 0) {ERRLOG("fork_error");} else if (pid == 0) {// 子进程处理文件后半部分的内容lseek(src_fd, file_size / 2, SEEK_SET);lseek(dest_fd, file_size / 2, SEEK_SET);ssize_t res;while ((res = read(src_fd, buf, sizeof(buf))) > 0) {if (write(dest_fd, buf, res) != res) {printf("子进程:dest_write_error\n");close(src_fd);close(dest_fd);exit(EXIT_FAILURE);}}close(src_fd);close(dest_fd);exit(EXIT_SUCCESS);} else {// 父进程处理文件前半部分off_t pos = 0;while (pos < file_size / 2) {ssize_t res = read(src_fd, buf, sizeof(buf));if (res > 0) {if (write(dest_fd, buf, res) != res) {printf("父进程:dest_write_error\n");close(src_fd);close(dest_fd);return -1;}pos += res;}}// 等待子进程结束int status;wait(&status);}//4.关闭文件if (close(src_fd) == -1) {ERRLOG("src_close_error");}printf("src_close_success..\n");if (close(dest_fd) == -1) {ERRLOG("dest_close_error");}printf("dest_close_success..\n");return 0;
}