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;
}