写点什么

Linux 系统编程 - 进程创建 (fork)、外部程序调用 (exec)

作者:DS小龙哥
  • 2022 年 2 月 14 日
  • 本文字数:3342 字

    阅读完需:约 11 分钟

1. fork 函数介绍

在 linux 中 fork 函数是非常重要的函数,它可以从已存在进程中创建一个新进程。新进程为子进程,而原进程为父进程。


fork 函数的返回值如下:1、在父进程中,fork 返回新创建的子进程的 PID 号。2、在子进程中,fork 返回 0;3、如果出现错误,fork 返回一个负值。因此可以通过返回值来判断是父进程还是子进程。


fork 函数创建子进程的过程:使用 fork 函数得到的子进程是父进程的一个复制品,它从父进程继承了进程的所有资源,相当于就是父进程的一个副本。


#include <unistd.h>pid_t fork(void); 制作分身函数功能: 创建新的子进程. 子进程是父的进程一个副本. (分身)返回值:  >0表示父进程  ==0表示子进程
复制代码


示例代码:


#include <stdio.h>#include <signal.h>#include <stdlib.h>#include <unistd.h>#include <sys/types.h>int main(){    int a=888;    //创建新的子进程    pid_t pid=fork();    if(pid==0) //子进程    {        a=999;        printf("子进程_a=%d\n",a);        printf("子进程的PID号:%d\n",getpid());        printf("当前子进程的父进程PID号:%d\n",getppid());    }    else    {        sleep(1);    }    return 0;}
复制代码

2. wait 函数

#include <sys/types.h>#include <sys/wait.h>pid_t wait(int *status);函数功能: 随机等待一个子进程退出.如果等待的子进程正常结束,返回值就是该子进程的pid号如果父进程没有子进程正在运行,wait函数会立即返回,返回值为-1
pid_t waitpid(pid_t pid, int *status, int options);函数功能: 等待指定的子进程退出.
复制代码


正常的多进程并发设计,父进程要负责清理子进程的空间:


#include <stdio.h>#include <signal.h>#include <stdlib.h>#include <unistd.h>#include <sys/types.h>#include <sys/wait.h>
int main(){ pid_t pid=fork(); if(pid==0) { printf("子进程运行成功.pid=%d\n",getpid()); } else { pid=wait(NULL); //等待子进程退出,清理子进程的空间 printf("退出的子进程的pid=%d\n",pid); } return 0;}
复制代码

3. exec 系列函数

exec 系列函数是用于启动一个新的进程,将新的进程启动成功之后会覆盖原进程.


#include <unistd.h>extern char **environ;int execl(const char *path, const char *arg, ...);int execlp(const char *file, const char *arg, ...);int execle(const char *path, const char *arg,..., char * const envp[]);int execv(const char *path, char *const argv[]);int execvp(const char *file, char *const argv[]);
p表示支持从环境变量里搜索可执行文件.
复制代码


示例代码:


#include <stdio.h>#include <signal.h>#include <stdlib.h>#include <unistd.h>#include <sys/types.h>#include <sys/wait.h>#include <unistd.h>int main(){    //ls -l    //execl("/bin/ls","ls","-l",NULL);    //execlp("ls","ls","-l",NULL);
char *cmd[]={"ls","-l",NULL}; //execv("/bin/ls",cmd); //execvp("ls",cmd);
char *cmd_path[]={"wbyq=666","abcd=888",NULL}; execle("/bin/ls","ls","-l",NULL,cmd_path);
printf("exec函数执行失败.\n"); return 0;}
复制代码

4. system 函数

system 函数用于启动新的子进程,这个函数内部就是使用 fork+exec+wait 函数组合实现的。说明这个 system 函数是阻塞的,必须等待子进程执行完毕之后才会执行父进程的代码。


#include <stdlib.h>int system(const char *command);
复制代码


示例代码:


#include <stdio.h>#include <signal.h>#include <stdlib.h>#include <unistd.h>#include <sys/types.h>#include <sys/wait.h>#include <unistd.h>int main(){    printf("开始执行.\n");    system("sleep 5");    printf("执行结束.\n");    return 0;}
复制代码

5. popen 函数

#include <stdio.h>FILE *popen(const char *command, const char *type);
复制代码


函数功能: 启动一个新的进程--类似于 fopen --dup2 函数启动成功的返回值的指针就指向该进程的标准输出。


int pclose(FILE *stream);
复制代码


函数功能: 释放空间示例代码:#include <stdio.h>int main(){    FILE *fp;    char buff[1024+1];    int cnt;    fp=popen("/bin/ls *.sh","r");    cnt=fread(buff,1,1024,fp);    buff[cnt]='\0';    printf("buff=%s\n",buff);    pclose(fp);    return 0;}
复制代码

6. pkill 命令

(1) pkill 命令支持一次性杀死某用户的所有进程。$ pkill -u <用户名>(2) pkill 命令支持一次杀死指定名称的所有进程。$ pkill <应用名称>(3) 使用 killall 命令一次杀死指定名称的所有进程$ killall <应用名称>(4) 杀死父进程创建的所有子进程pkill -9 -P <父进程 PID>
复制代码

7. 案例: 使用 fork 函数创建 5 个子进程同时运行

#include <stdio.h>#include <unistd.h>
int main(){ int i; pid_t pid; for(i=0;i<5;i++) { pid=fork(); //创建一个子进程 if(pid==0)break; //如果是子进程就直接退出循环 } if(i==5) //父进程 { sleep(5); char cmd_buff[100]; sprintf(cmd_buff,"pkill -9 -P %d",getpid()); system(cmd_buff); while(1) { pid=wait(NULL); if(pid==-1)break; //当父进程没有子进程的时候该函数就返回-1 else printf("%d 子进程退出成功.\n",pid); } printf("父进程正常结束.\n"); } else //子进程 { while(1) { sleep(1); printf("当前运行的子进程pid=%d\n",getpid()); } } return 0;}
复制代码

8. 案例: 实现自动切换(2 秒)显示指定目录下的所有图片

利用 eog 命令,配合今天学习的进程知识点,做出一个 ppt 播放效果。思路: 父进程扫描目录,得到目录下的文件名称,在传递给子进程,子进程调用 eog 命令实现图片显示,父进程里 2 秒钟之后就杀死子进程,再读取目录下下一个文件,再传递给子进程………



#include <stdio.h>#include <unistd.h>#include <string.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <sys/types.h>#include <dirent.h>#include <stdlib.h>
int main(int argc,char **argv){ if(argc!=2) { printf("./a.out <图片目录/>\n"); return 0; } /*1. 打开目录*/ DIR *dir=opendir(argv[1]); if(dir==NULL) { printf("%s 目录打开失败.\n",argv[1]); return 0; } /*2. 创建管道文件*/ int fds[2]; pipe(fds);
pid_t pid; struct dirent *dir_info;AA: /*循环遍历目录*/ dir_info=readdir(dir); if(dir_info==NULL) { printf("图片显示完毕.\n"); exit(0); //表示图片显示完毕 } /*创建子进程*/ pid=fork(); if(pid==0) //子进程 { char file_name[100+1]; int cnt; //从管道的读端读取数据 cnt=read(fds[0],file_name,100); file_name[cnt]='\0'; printf("正在显示的图片:%s\n",file_name); //启动新的进程 execlp("eog","eog",file_name,NULL); } else //父进程 { //判断是否是图片 if(strstr(dir_info->d_name,".jpg")) { char *p; //组合图片的路径 p=malloc(strlen(argv[1])+strlen(dir_info->d_name)+1); sprintf(p,"%s%s",argv[1],dir_info->d_name); //向管道的写端写数据 write(fds[1],p,strlen(p)); //延时2秒 sleep(2); //杀死父进程创建的所有子进程 char cmd_buff[100]; sprintf(cmd_buff,"pkill -9 -P %d",getpid()); system(cmd_buff); //清理子进程的空间 wait(NULL); } //继续显示下一张图片 goto AA; }
return 0;}
复制代码


发布于: 刚刚阅读数: 2
用户头像

DS小龙哥

关注

之所以觉得累,是因为说的比做的多。 2022.01.06 加入

熟悉C/C++、51单片机、STM32、Linux应用开发、Linux驱动开发、音视频开发、QT开发. 目前已经完成的项目涉及音视频、物联网、智能家居、工业控制领域

评论

发布
暂无评论
Linux系统编程-进程创建(fork)、外部程序调用(exec)