Linux的进程控制

准备

博主:大大怪先森(记得关注,下次不要迷路哦)
编程环境:xshell(点击下载)
所示代码:码源
在这里插入图片描述


提示:写完文章后,目录可以自动生成


前言

本文将将讲解关于进程控制的基本知识!!!


提示:以下是本篇文章正文内容

一、fork()函数

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

初识fork()函数

头文件引用:#include <unistd.h>
格式:pid_t fork(void);
返回值:自进程中返回0,父进程返回子进程id,出错返回-1。

进程调用fork,当控制转移到内核中的fork代码后,内核做:

  • 分配新的内存块和内核数据结构给子进程
  • 将父进程部分数据结构内容拷贝至子进程
  • 添加子进程到系统进程列表当中
  • fork返回,开始调度器调度

当一个进程调用fork之后,就有两个二进制代码相同的进程。而且它们都运行到相同的地方。但每个进程都将可以开始它们自己的旅程。
代码如下:

#include<stdio.h>
  2 #include<unistd.h>
  3 int main()
  4 {
  5   pid_t id = fork();
  6   if(0 == id)
  7   {
  8     printf("I am a child,pid:%d\n",getpid());                                                                                                                                          
  9   }
 10   else{
 11   printf("I am father,pid:%d\n",getpid());
 12   }
 13   return 0;
 14 }


在这里插入图片描述

写时拷贝

通常,父子代码共享,父子再不写入时,数据也是共享的,当任意一方试图写入,便以写时拷贝的方式各自一份副本。
如见下图:
在这里插入图片描述

二、进程的终止

进程退出的场景

  • 代码运行完毕,结果正确
  • 代码运行完毕,结果不正确
  • 代码异常终止

进程退出的方法

正常终止(可以通过 echo $? 查看进程退出码)

一般正常进程正常终止的退出码为0

  • return 返回
  • exit() 函数返回
  • _exit()函数返回
    查看退出码的含义在这里插入图片描述
    在这里插入图片描述

异常终止:

  • ctrl + c :信号终止>代码如下(示例):

exit()和_exit()的区别

>

小结

1.main函数中的return返回的参数表示退出码
2.exit在任意位置调用,表示退出,参数是退出码
3._exit表示强制终止程序,不行进程后续的收尾工作,比如说刷新缓存区等。

三、进程等待

wait等待

#include<sys/types.h>
#include<sys/wait.h>
pid_t wait(int*status);
返回值:
成功返回被等待进程pid,失败返回-1。
参数:
输出型参数,获取子进程退出状态,不关心则可以设置成为NULL

waitpid等待

pid_ t waitpid(pid_t pid, int *status, int options);
返回值:
当正常返回的时候waitpid返回收集到的子进程的进程ID;
如果设置了选项WNOHANG,而调用中waitpid发现没有已退出的子进程可收集,则返回0;
如果调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误所在;
参数:
pid:
Pid=-1,等待任一个子进程。与wait等效。
Pid>0.等待其进程ID与pid相等的子进程。
status:
WIFEXITED(status): 若为正常终止子进程返回的状态,则为真。(查看进程是否是正常退出)
WEXITSTATUS(status): 若WIFEXITED非零,提取子进程退出码。(查看进程的退出码)
options:
WNOHANG: 若pid指定的子进程没有结束,则waitpid()函数返回0,不予以等待。若正常结束,则返回该子进
程的ID。

子进程的退出状态信息(status)

  • wait和waitpid,都有一个status参数,该参数是一个输出型参数,由操作系统填充。
  • 如果传递NULL,表示不关心子进程的退出状态信息。
  • 否则,操作系统会根据该参数,将子进程的退出信息反馈给父进程。
  • status不能简单的当作整形来看待,可以当作位图来看待,具体细节如下图(只研
    研究status低16比特位)
    在这里插入图片描述

代码

int main()
  8 {
  9   pid_t id = fork();
 10   if(id == 0)
 11   {
 12     //child
 13     int cnt = 3;
 14     while(cnt)
 15     {
 16       printf("child[%d] is runing;cnt is %d\n",getpid(),cnt);
 17       cnt--;
 18       sleep(1);                                                                                      
 19     }
 20     exit(1);
 21   }
 22   sleep(5);
 23   int status = 0;
 24   while(1)
 25   {
 26     //wait(NULL)
 27     //waitpid(-1,NULL,0) 和上面的等待进程一样,父进程等待任意一个子进程
 28     pid_t ret = waitpid(id,&status,WNOHANG);//0表示阻塞等待,WNOHANG表示非阻塞等待
 29     if(ret == 0)
 30     {
 31       //子进程没有退出,但是waitpid等待是成功的,需要父进程重复等待
 32       printf("Do father things\n");
 33     }
 34     else if(ret > 0)
 35     {
 36       //子进程退出了,waitpid也等待成功了,获得了对应的结果
 37     printf("fahter wait: %d, success, status exit code: %d, status exit signal: %d\n", ret, (status>>    8)&0xFF, status&0x7F);
 38     //WIFEXITED(status) 若为正常终止子进程返回的状态,则为真。(查看进程是否是正常退出)
 39     //WEXITSTATUS(status): 若WIFEXITED非零,提取子进程退出码。(查看进程的退出码)
 40     break;
 41     }
 42     else{
 43       perror("waitpid");
 44       break;
 45      }
 46 }   

四、进程替换

替换原理

用fork创建子进程后执行的是和父进程相同的程序(但有可能执行不同的代码分支),子进程往往要调用一种exec函数以执行另一个程序。当进程调用一种exec函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序的启动例程开始执行。调用exec并不创建新进程,所以调用exec前后该进程的id并未改变
在这里插入图片描述

替换函数

其实有六种以exec开头的函数,统称exec函数:

#include <unistd.h>`

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[]);

int execve(const char *path, char *const argv[],char *const envp[]);

这些函数原型看起来很容易混,但只要掌握了规律就很好记

命名理解:

  • l(list):参数使用命令行参数的形式
  • v(verctor):参数使用数组的形式
  • p(path):自动索引环境变量PATH,只需要传递指定的文件即可
  • e(env):表示维护自己的变量

代码案例:

	1 #include <stdio.h>                                                                        
    2 #include <unistd.h>
    3 #include <stdlib.h>
    4 #include <sys/wait.h>
    5 int main()
    6 {
    7   if(fork() == 0)
    8   {
    9     //child
   10     printf("command begin ...\n");
   11     //char* argv[] = {
   12     //  "ls",
   13     //  "-a",
   14     //  "-l",
   15     //  "-i",
   16     //  NULL
   17     //};
W> 18     char* argv[] = {
W> 19       "myexe",
   20       NULL
   21     };
   22     char* env[] = {
W> 23       "MYENV=hhhhhhhhhhh",
W> 24       "MYENV1=dhjalkdhlad",
   25       NULL
   26     };
   27  // execv("/usr/bin/ls",argv);
   28  //execvp("ls",argv);
   29   //execl("./myexe","myexe",NULL);
   30  // execve("./myexe",argv,env);
   31   execle("./myexe","myexe",NULL,env);
   32   printf("command end ...\n");
   33   exit(1);
   34   }
   35   waitpid(-1,NULL,0);
   36   printf("wait child success\n");
   37   return 0;
   38 }                        

在这里插入图片描述
输出结果:
在这里插入图片描述

五、简易版Xshell的实现

#include<stdio.h>                                                                         
    2 #include<string.h>
    3 #include<unistd.h>
    4 #include<stdlib.h>
    5 #include<sys/wait.h>
    6 
    7 #define NUM 128
    8 #define CMD_NUM 64
    9 
   10 int main()
   11 {
   12   char command[NUM];
   13   for(;;)
   14   {
   15     char* argv[CMD_NUM] = {NULL};
   16     //打印提示符
   17     command[0] = 0;//用这种方式可以清空字符串
   18     printf("[who@myhostname mydir]# ");
   19     fflush(stdout);
   20     //获取字符串
   21     fgets(command,NUM,stdin);
   22     command[strlen(command) - 1] = '\0';
   23     //ls -l -a \n 最后敲得时候会有一个回车符号需要去掉
   24     
   25     //ls -a -l
   26     //3.解析代码
   27     const char* sep = " ";
   28     argv[0] = strtok(command,sep);
   29     int i = 1;
W> 30     while(argv[i] = strtok(NULL,sep))
   31     {
   32       i++;
   33     }
   34      //检查命令是否是需要shell本身取执行,内键命令
   35      if(strcmp(argv[1],"cd") == 0)
   36      {
   37        if(argv[1] != NULL)
   38        {
   39          chdir(argv[1]);
   40          continue;
   41        }
   42      }
   43      //5.执行第三方命令
   44      if(fork() == 0)
   45      {
   46        //child
   47        execvp(argv[0],argv);
   48        exit(1);
   49      }
   50      int status = 0;
   51      waitpid(-1,NULL,0);
   52     printf("exit code: %d\n", (status >> 8)&0xFF);
   53   }
   54   return 0;
   55 }


在这里插入图片描述


结语

希望本篇文章能给各位带来帮助,如有不足还请指正!!!
码字不易,各位大大给个收藏点赞吧!!!

宝子们,点赞,支持。
三连走一波!!!
在这里插入图片描述

  • 20
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 24
    评论
评论 24
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值