Linux进程控制


前言

在本篇文章中我们将会学习到有关进程控制的内容,具体包括进程的创建(fork),进程的终止(exit),进程等待(waitpid),进程程序替换(exec*)相关的内容以及相关函数的使用方法。

一、进程创建

1.fork函数

我们调用系统调用函数fork创建进程。

#include <unistd.h>
pid_t fork(void);
返回值:子进程中返回0,父进程返回子进程id,出错返回-1

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

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

在这里插入图片描述

fork之前,父进程独立执行,fork之后,父子两个执行流分别执行。
fork之后,谁先执行完全由调度器决定

fork常规用法

1.一个父进程希望复制自己,使父子进程同时执行不同的代码。例如:父进程等待客户端请求,生成子进程来处理请求。
2.一个进程要执行一个不同的程序。例如:子进程从fork返回后,调用exec函数。

fork调用失败原因

1.系统中有太多进程,内存不足
2.实际用户的进程数超出了限制

2.写时拷贝

通常,父子代码共享,父子不在进行写入时,数据也是共享的。
当任意一方试图写入,便会以写时拷贝的方式各自备份一份数据。

在这里插入图片描述

const作用:运行时报错提前在编译时就发现。

二、进程终止

1.main函数返回值

我们之前学习过的main函数有返回值,return 0;

main函数也是要被调用的,当main函数调用结束后应该给操作系统返回相应的退出信息,这个退出信息就是以退出码的形式作为main函数返回值返回。
一般0,表示成功。非零表示失败。
这是为什莫呢??
因为代码执行成功只有一种情况,代码执行错误会与很多原因。例如内存空间不足,非法访问,栈溢出等等。我们就可以用非0的数字分别表示代码执行错误的原因。

main函数return返回表示进程退出。
其他函数退出,表示函数调用完毕。

进程退出场景
1.代码运行完毕,结果正确
2.代码运行完毕,结果不正确
3.代码没有运行完毕,代码异常终止

对于异常终止,进程收到了异常信号,其中每个信号都有自己不同的编号,不同的编号表示不同的异常原因。

正常中止:exit或者main函数返回
异常退出:信号中止,CTRL+C

异常退出也可能是代码错误导致的,比如:野指针访问,除0操作等。

我们可以用下面这个查看进程的退出码

echo $ ? 查看最近一次进程退出的退出码信息。

C语言中sterror函数可以通过错误码,获取该错误码在C语言中对应的错误信息。
在这里插入图片描述
我们可以打印简单看一下

在这里插入图片描述
实际上Linux中的ls,pwd等命令也是可执行程序,使用这些命令我们也可以查看对应的退出码
在这里插入图片描述

退出码都有对应的字符串含义,帮助用户确认执行失败的原因,退出码具体含义是人为规定的,不同环境下相同的退出码的字符串含义可能不同

2.exit和_exit

我们先看一下库里的介绍

在这里插入图片描述
在这里插入图片描述
我们可以看到,exit和_exit本质没有区别,区别就是一个是系统调用,一个是c语言函数调用。
本质exit就是对_exit进行了封装,我们为什么要这么做呢??
Linux和Win系统调用接口可能不同,我们对系统调用进行封装,函数在两个平台下都可以正常运行。通过库屏蔽底层差异,可移植性好,跨平台性.

其中status定义了进程的终止状态,父进程通过wait获取。
echo $?得到的数据以unsigned int看待,所以是255.

int main()
{
 printf("hello");
 exit(-1);
}

在这里插入图片描述

我们再来看一下_exit()

int main()
{
 printf("hello");
 _exit(-1);
}

在这里插入图片描述
我们发现打印还是有差别的!!!
exit调用时:

1.执行用户通过atexit或者on_exit定义的清理函数
2.关闭所有打开的流,所有的缓存数据被写入
3.调用_exit;

在这里插入图片描述
创建进程:操作系统创建PCB,地址空间,页表,将程序代码加载到内存中,构建映射关系。

只有在main函数中,return才可以退出进程,子函数中return 不能退出进程。exit在任何函数任何地方都可以退出程序。

执行return n等同于执行exit(n),因为调用main的运行时函数会将main的返回值当做 exit的参数。

三.进程等待

1.概述

我们之前讲过子进程退出,PCB还没有被父进程读取,就一直处于僵尸状态。如果父进程不进行读取就退出了,子进程的PCB就一直在内存中,不会被释放,就会发生内存泄漏。
父进程派给子进程的任务完成的如何了,我们需要知道,比如:子进程运行完成,结果对还是不对,或者是否正常退出等等。

父进程通过进程等待的方式
1.回收子继承资源
2.获取子进程的退出信息

2.wait方法

在这里插入图片描述
🌟返回值:成功返回被等待进程pid,失败返回-1.
🌟参数:输出型参数,由操作系统填充。获取子进程退出状态,不关心可以设置成为NULL
默认进行阻塞等待,等待任意一个子进程

创建子进程后,父进程可使用wait函数一直等待子进程,直到子进程退出后读取子进程的退出信息。

2.waitpid方法

在这里插入图片描述
可以等待任意子进程或者指定子进程

🌟返回值:
    当正常返回的时候waitpid返回收集到的子进程的id
    如果设置了选项WNOHANG,而调用中waitpid发现没有已退出的子进程可以收集,返回0
    如果调用中出错,返回-1,这时errno会设置成相应的值表示错误所在
🌟参数pid
    pid等于-1,等待任意一个子进程,与wait等效
    pid大于0,等待其进程id与pid相等的子进程
🌟参数status
    WIFEXITED(status):如果为正常终止子进程返回的状态,则为真。(查看进程是否是正常退出)
    WEXITSTATUS(status):如果WEXITSTATUS非0,提取子进程退出码。(查看进程退出码)
    简单说,就是反馈子进程的退出情况
🌟参数options
    WNOHANG:如果pid指定的子进程没有结束,则waitpid()函数返回0,不予等待。如果正常退出,则返回该子进程的id

如果子进程已经退出,调用wait/waitpid,会立即返回,并且释放资源,获得子进程退出信息。
如果不存在该子进程,这就立即出错返回。

我们看一下下面这段代码

int main()
{
    pid_t id = fork();
    if(id == 0)
    {
        // child
        int cnt = 5;
        while(cnt)
        {
            printf("Child is running, pid: %d, ppid: %d\n", getpid(), getppid());
            sleep(1);
            cnt--;
        }
        exit(1);
    }
    int status = 0;
    pid_t rid = waitpid(id, &status, 0); // 阻塞等待
    if(rid > 0)
    {
        printf("wait success, rid: %d, status: %d\n", rid, status);
    }

    return 0;
}

在这里插入图片描述
这个status为什么返回的是256呢·??
status如果传递NULL,表示不关心子进程的退出信息。否则,操作系统会根据该整数,将子进程的退出信息反馈给父进程。
status不能简单看作一个整数,,需要当成位图来看待。

在这里插入图片描述

我们进程的退出码时1
所以32个比特位应该是
0000 0000 0000 0000 0000 0001 0000 0000
也就是256.

任何进程最终执行情况,可以用两个数字表明具体执行情况。

我们首先看进程是否出现异常,再看退出码。

我们也可以采用位运算符单独查看正常退出和异常退出。


int main()
{
         pid_t id = fork();
         if (id == 0)
         {
               
                 int cnt = 5;
                 while (cnt)
                  {
                        printf("Child is running, pid: %d, ppid: %d\n", getpid(), getppid());
                  //      sleep(1);                                                                                                          
                       cnt--;
                  }
                 exit(1);
         }
         int status = 0;
         pid_t rid = waitpid(id, &status, 0);
         if (rid > 0)
         {
                printf("父进程等待成功,status:%d,signo:%d,exit_code:%d\n", status, status & 0x7f, (status >> 8) & 0xff);
         }
     return 0;
}

在这里插入图片描述
我也可以查看异常退出情况
就比如空指针访问情况

在这里插入图片描述

在这里插入图片描述
系统中也为我们提供了相应的宏,进行查看。
其中WIFEXITED(status),查看是否正常退出,正常退出返回1,否则返回0.
WEXITSTATUS(status): 若WIFEXITED非零,提取子进程退出码。

当一个进程非正常退出,说明该进程是被信号所杀了,那么进程的退出码也就没有意义了

我们一般这样使用

int main()
{
         pid_t id = fork();
         if (id == 0)
         {
               
                 int cnt = 5;
                 while (cnt)
                  {
                        printf("Child is running, pid: %d, ppid: %d\n", getpid(), getppid());                                                                                    
                       cnt--;
                  }
                 exit(1);
         }
         int status = 0;
         pid_t rid = waitpid(id, &status, 0);
         if (rid > 0)
         {
                if (WIFEXITED(status))
                {
                        printf("父进程等待成功,status:%d,exit_code:%d\n", status, WEXITSTATUS(status));
                }
         }

     return 0;
}

3.非阻塞等待

如果子进程没有退出,父进程一直在等待子进程退出,在等待期间,父进程不能做任何事情,这种等待称为阻塞等待。

我们也可以让子进程没有退出时让父进程做一些自己的事情,当子进程退出时再读取子进程的退出信息,也就是非阻塞等待。

我们只需要将waitpid函数第三个参数传WNOANG。等待的子进程如果没有结束,那么waitpid函数将直接返回0,不进行等待。而等待的子进程如果正常结束,返回子进程的id.

父进程可以隔一段时间调用依次waitpid函数,若是等待的子进程没有退出,父进程可以先做一些其他事情,过一段时间在调用waitpid函数读取子进程的退出信息。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main()
{
    pid_t id = fork();
    if (id == 0) {
        //child
        int count = 3;
        while (count--) {
            printf("child do something...PID:%d, PPID:%d\n", getpid(), getppid());
            sleep(3);
        }
        exit(0);
    }
    //father
    while (1) {
        int status = 0;
        pid_t ret = waitpid(id, &status, WNOHANG);
        if (ret > 0) {
            printf("wait child success...\n");
            printf("exit code:%d\n", WEXITSTATUS(status));
            break;
        }
        else if (ret == 0) {
            printf("father do other things...\n");
            sleep(1);
        }
        else {
            printf("waitpid error...\n");
            break;
        }
    }
    return 0;
}

运行结果:父进程每隔一段时间就查看子进程是否退出,如果没有退出,父进程先做自己的事。过段时间再来查看。直到子进程推出后读取子进程的退出信息。

在这里插入图片描述

四.进程程序替换

1.替换原理

当folk创建子进程后执行的是和父进程相同的程序(但又可能执行不同的代码分支),子进程往往要调用一种exec函数执行另外一个进程。
当进程调用exec函数时,该进程的用户空间代码和数据完全被新进程替换,从新进程的启动历程开始执行,调用exec并不创建子进程,所以调用exec前后该进程的id不发生变化。

在这里插入图片描述
我们只会改变物理内存中相关内容,将相应的信息拷贝到虚拟内存中,重新更新页表。

不会重新建立新的进程,pid也不会改变。
替换一般用于父子进程之间,子进程进行程序替换**

子进程进程程序替换后,会影响父进程的代码和数据吗??

子进程刚被创建时,与父进程共享数据和代码,子继承进行替换时,也就意味着自己才能要对数据和代码进行写入操作,这时便会将父进程的代码和数据进行写时间拷贝,此后父子进程的代码和数据也就分离了,互不影响。

2.替换函数

我们要修改的是进程中的东西,所以一定要有系统调用。

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 execvpe(const char *file, char const argv[],charconst envp[]);

记忆规则:
🌟l(list):表示参数使用列表
🌟v(vector):参数用数组
🌟p(pash):自动搜索环境变量PATH
🌟e(env):自己维护环境变量

1.execl

int execl(const char *path, const char *arg, …);

…不是省略,这个称为可变参数列表,就和printf函数一样。传参个数根据需求来。同时注意这一系列exec函数如果有可变参数列表,最后必须以NULL结尾

参数怎么传递呢???
我们接下来用Linux指令来进行操作,系统指令也是一个程序。
我们怎末用的系统指令,参数就怎么传递。

我们通过一段代码看一下

#include <stdio.h>
#include <unistd.h>
int main()
{
   printf("开始执行\n");
   int cnt = 3;
   while (cnt--)
   {
     printf("cnt:%d\n", cnt);
   }
   printf("开始替换\n");
   execl("/usr/bin/ls", "ls", "-l", NULL);
   printf("替换完成\n");
   return 0;
}

我们看一下运行结果
在这里插入图片描述
现象:
🌟1.确实进行了程序替换。
🌟2.程序一旦替换成功,exec后续代码不再执行。
我们可以观察到“替换完成“并没有被打印出来。
🌟3.exec
只要失败返回值,没有成功返回值。失败返回-1.

在这里插入图片描述
🌟4.必须以NULL结尾,不是“NULL”

创建一个进程,首先创建PCB,页表,地址空间等,再把程序加载到内存。

程序替换所做的工作,就是加载。

2.execlp

int execlp(const char *file, const char *arg, …);

我们发现只要第一个参数不同,有P自动搜索路径,我们只需要传递参数就可以。

#include <stdio.h>
#include <unistd.h>
int main()
{
   printf("开始执行\n");
   int cnt = 3;
   while (cnt--)
   {
     printf("cnt:%d\n", cnt);
   }
   printf("开始替换\n");
   execpl("ls", "ls", "-l", NULL);
   printf("替换完成\n");
   return 0;
}

在这里插入图片描述

execpl(“ls”, “ls”, “-l”, NULL);
我们看一下这参数释放重复了??
其实并不重复,虽然都是ls,但是意义不同。
第一个ls表示你想执行谁,后面的表示你想怎末执行。

3.execv

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

只有两个参数,首先我们在execv中不用加NULL了,但是我们需要在argv中加入NUL,我们传递的参数放到了一个指针数组中。

int main()
{
   printf("开始执行\n");
   int cnt = 3;
   while (cnt--)
   {
     printf("cnt:%d\n", cnt);
   }
   printf("开始替换\n");
   char* const argv[] = { (char*)"ls",(char*)"-a", (char*)"-l",NULL };
   execv("/usr/bin/ls", argv);
   printf("替换完成\n");
   return 0;
}

同样可以运行到相同的结果
不要忘记NULL的传递。

4.execle

exec*函数可以执行系统的指令,那麽可以执行我们自己写的程序吗·??

我们通过以C++例子看一下

我们这次要makefile一次性形成两个可执行程序.
makefile默认从上到下扫描,只会形成一个可执行程序。
我们应该这样写

.PHONY:all
all:myprocess test

myprocess:myprocess.c
  gcc -o $@ $^ 
test:test.cpp
   g++ -o $@ $^ -std=c++11                                                           
.PHONY:clean
clean:
  rm -rf myprocess mytest

c++,可用的后缀有很多,比如.cc .cpp .cxx.

我们现在分别看一下两个程序的代码
test.cpp

#include <iostream>
using namespace std;
int main()
{                                                                                   
  extern char**environ;
  for(int i=0;environ[i];i++)
  {
     printf("%d:%s\n",i,environ[i]);
  }
  return 0;
}

myprocess.c


int main()
{
    printf("开始执行\n");
    int cnt = 3;
    while (cnt--)
    {
        printf("cnt:%d\n", cnt);
    }
    printf("开始替换\n");
    execl("./test", "mytest", NULL);
    printf("替换完成\n");
    return 0;
}

我们运行看一下
在这里插入图片描述

确实可以替换我们自己写的程序,这是为什呢??

无论什么语言,只要在Linux可以运行的,都可以被替换。因为所有的语言,运行之后都是进程。

同时可以证明了环境变量具有全局属性。

默认可以通过地址空间继承的方式,让所以子进程拿到环境变量,进程程序替换,不会替换环境变量。

如果我们想要新增环境变量,只需要用到putenv就可以。
putenv导出环境变量。

int main()
{
    printf("开始执行\n");
    int cnt = 3;
    putenv("key=dhsbjcjbsssssssjjjjjjjjjjjjjjjjjjjjjjjjjjjjj");
    while (cnt--)
    {
        printf("cnt:%d\n", cnt);
    }
    printf("开始替换\n");
    execl("./test", "mytest", NULL);
    printf("替换完成\n");
    return 0;
}

我们运行看一下
在这里插入图片描述
确实新增了了,新增的之后影响后者,不会影响前者。

我们如果想要设置一个全新的环境变量给子进程呢??
我们就用到了execle这样带e的函数。这其实就是覆盖。

int execle(const char *path, const char *arg, …,char *const envp[]);

int main()
{
    printf("开始执行\n");
    int cnt = 3;
    while (cnt--)
    {
        printf("cnt:%d\n", cnt);
    }
    printf("开始替换\n");
    char* const envp[] = { (char*)"haha=hehe",(char*)"11=22",NULL };
    execle("./test", "mytest", NULL, envp);
    printf("替换完成\n");
    return 0;
}

在这里插入图片描述

一定不要忘记NULL,一定不要忘记NULL,一定不要忘记NULL!!!!重要的事情说三遍!!!!

3.函数原理

我们其实还有一个函数execve,这是一个系统调用函数

在这里插入图片描述
我们上面所用过的函数都是库函数。这些库函数都对execve函数进行了封装。
在这里插入图片描述
在这里插入图片描述

五.自定义简易shell

1.打印命令提示符,获取用户输入内容

我们进入会看到这样的内容

[peng@hcss-ecs-509d myshell]$

这其实是这样组成的,【用户@主机名 当前目录 】$,这些我们都可以通过环境变量拿到的。getenv函数。
在这里插入图片描述

回顾一下scanf函数


#include <stdio.h>

int main()
{
   char arr[100];
   scanf("%s", arr);
   printf("%s\n", arr);
   return 0;
}

当我们输入“ls -a -l”会打印什么内容??
在这里插入图片描述

我们发现只有一个ls打印了出来。scanf是以空格为分割的,我们只输入了一次。

我们可以用fgets解决这个问题,

char *fgets(char *s, int size, FILE *stream);

从stdion中读取size个大小的数据放到s中

#include <stdio.h>

int main()
{
   char arr[100];
   fgets(arr, 100, stdin);
   printf("%s\n", arr);
   return 0;
}

我们观察一下现象
在这里插入图片描述
我们确实将内容打印了出来,但是却多了一个空行??
我们输入的时候,敲了一下回车,当成了换行,也会进入arr中,所以打印的时候也输出了出来。
但是我们不想做,我们想去掉空行,怎末做呢??

arr[strlen(arr)-1]=0; 提前设置’/0’就可以了

我们输入的是一个字符串,我们需要将他们按照空格分割成不同的字串,最后以‘/0’结尾。

2.对命令行字符串进行切割

我们输入的是一个字符串,我们需要将他们按照空格分割成不同的字串,最后以‘/0’结尾。

字符串切割我们用到的函数是strtok,同时注意最后要以/0结尾

#include <stdio.h>
#include <string.h>
int main()
{
   char arr[100];
  
   fgets(arr, 100, stdin);
   arr[strlen(arr) - 1] = 0;
   char* argv[64];
   int i = 0;
   argv[i++] = strtok(arr, " ");
   while (argv[i++] = strtok(NULL, " "));
   for (i = 0; argv[i]; i++)
   {
      printf("[%d]=%s\n", i, argv[i]);
   }
   return 0;
}

这一步非常妙

while (argv[i++] = strtok(NULL, " "));

我们通过打印来看一下
在这里插入图片描述

3.执行这个命令

我们一般通过让子进程执行这个命令,父进程进行等待。
不能直接让父进程进行程序替换,下次就不能用了。
我们使用哪个程序替换函数比较好呢??execvp比较好
我们再来看一下这个函数

int execvp(const char *file, char *const argv[])

不用我们自己找路径,argv我们刚才已经保存了

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <string.h>
int main()
{
   char arr[100];
  
   fgets(arr, 100, stdin);
   arr[strlen(arr) - 1] = 0;
   char* argv[64];
   int i = 0;
   argv[i++] = strtok(arr, " ");
   while (argv[i++] = strtok(NULL, " "));

   pid_t id = fork();
   if (id == 0)
   {
     execvp(argv[0], argv);
     exit(1);
   }
   int status = 0;
   pid_t rid = waitpid(id, &status, 0);
   if (id == rid) {
     printf("等待成功\n");
   }
   return 0;
}

在这里插入图片描述

4.处理内建命令

我们在执行cd时,无法达到我们的预期,执行cd命令的是子进程,不是bash,我们是想让bash切换路径。
不创建子进程,bash自己执行的命令,我们称为内建命令

我们的路径发生了变化,同样环境变量也要跟着变化,

如果我们直接执行cd命令,什么都不加就会进入家目录。

我们执行cd之后,工作目录很大可能都会改变,所以我们需要修改当前的工作目录,采用

int chdir(const char *path);

在这里插入图片描述
如果我们执行cd…返回上级目录,如果我们不加处理,系统会认为我们要去…这个路径下。
我们需要获取当前的工作路径

char *getcwd(char *buf, size_t size);

在这里插入图片描述

我们更改完成后,也需要手动修改环境变量,我们就需要用到putenv改变环境变量

int putenv(char *string);

在这里插入图片描述

我们发现传的值是一个字符数组,那我们怎末实现呢??
我们需要先进行修改之后,再传进来。

int sprintf(char *str, const char *format, …);

在这里插入图片描述
使用方法与printf函数类似,只不过是将格式化的内容转化成字符串放到str中。

export导出环境变量。这也是必须得bash进行操作的,我们只需要将对应的内容拷贝到环境变量中去就可以

echo可以帮助我们完成三种操作
🌟echo 字符串 直接在屏幕上显示字符串的内容
🌟echo $? 显示最近一次的程序退出码
🌟echo $环境变量 显示某个环境变量的值
这也是需要bash自己做的

5.完整代码


#include <stdio.h>  
#include <string.h>  
#include <unistd.h>  
#include <stdlib.h>  
#include <sys/types.h>  
#include <sys/wait.h>  

#define SIZE 1024  
#define GAP " "  
int lastcode = 0;
char* argv[SIZE];
char pwd[SIZE];
char env[SIZE];

char* User()
{
    char* User = getenv("USER");
    if (User)
    {
        return User;
    }
    else
    {
        return (char*)"None";
    }
}

char* HostName()
{
    char* HostName = getenv("HOSTNAME");
    if (HostName)
    {
        return HostName;
    }
    else
    {
        return (char*)"None";
    }
}

char* Pwd()
{
    char* Pwd = getenv("PWD");
    if (Pwd)
    {
        return Pwd;
    }
    else
    {
        return (char*)"None";
    }
}

char* Home()
{
    char* Home = getenv("HOME");
    return Home;
}

int Interactive(char arr[])
{
    printf("[%s@%s %s]$ ", User(), HostName(), Pwd());
    fgets(arr, SIZE, stdin);
    arr[strlen(arr)-1] = 0; 
    return strlen(arr);
}

void Divide(char arr[])
{
    int i = 0;
    argv[i++] = strtok(arr, GAP);
    while (argv[i++] = strtok(NULL, GAP));
    
}

int System()
{
    int ret = 0;
    if (strcmp("cd", argv[0]) == 0)
    {
        ret = 1;
        //更改当前工作目录  
        char* target = argv[1];
        if (target == NULL)
        {
            target = Home();
        }
        chdir(target);
        //重新获取当前工作目录  
        char path[SIZE];
        getcwd(path, SIZE);
        //更改工作量目录  
        sprintf(pwd, "PWD=%s", path);
        //导出环境变量  
        putenv(pwd);
    }
    else if (strcmp("export", argv[0]) == 0)
    {
        ret = 1;
        if (argv[1] != NULL)
        {
            strcpy(env, argv[1]);
            putenv(env);
        }
    }
    else if (strcmp("echo", argv[0]) == 0)
    {
        ret = 1;
        if (argv[1] == NULL)
        {
            printf("\n");
        }
        else
        {
            if (argv[1][0] == '$')
            {
                if (argv[1][1] == '?')
                {
                    printf("%d\n", lastcode);
                    lastcode = 0;
                }
                else
                {
                    char* e = getenv(argv[1] + 1);
                    if (e) printf("%s\n", e);
                }
            }
            else
            {
                printf("%s\n", argv[1]);
            }
        }
    }
    return ret;
}

void Execute()
{
    //创建子进程做  
    pid_t id = fork();
    if (id == 0)
    {
        execvp(argv[0], argv);
        exit(1);
    }
    int status = 0;
    pid_t rid = waitpid(id, &status, 0);
    if (rid == id)
    {
        lastcode = WEXITSTATUS(status);
    }
}

int main()
{
    while (1)
    {
        // 1. 打印命令行,输入命令  
        char arr[SIZE] = { 0 };
        int n = Interactive(arr);
        if (n == 0) continue; // 空串  

        // 2. 截取字符串  
        Divide(arr);

        // 3. 判断是否是内建命令  
        int ret = System();
        if (ret == 1) continue;

        // 4. 执行  
        Execute();
    }

    return 0;
}

总结

以上就是今天要讲的内容,本文仅仅详细介绍了Linux进程控制的内容。希望对大家的学习有所帮助,仅供参考 如有错误请大佬指点我会尽快去改正 欢迎大家来评论~~ 😘 😘 😘

  • 33
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lim 鹏哥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值