Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >【Linux】进程控制:理解什么是进程创建,进程终止,进程等待 | 进程替换

【Linux】进程控制:理解什么是进程创建,进程终止,进程等待 | 进程替换

作者头像
aosei
发布于 2024-01-23 07:32:40
发布于 2024-01-23 07:32:40
34800
代码可运行
举报
文章被收录于专栏:csdn-nagiYcsdn-nagiY
运行总次数:0
代码可运行

一.进程创建

fork函数创建进程,新进程为子进程,原进程为父进程;

fork函数包含在头文件 <unistd.h>

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

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

关于fork函数的返回值:

  • 返回0给子进程
  • 返回子进程的PID给父进程
  • 创建失败,返回值 < 0

子进程和父进程共享fork函数之后的代码

实例演示:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
  int main()
  {
      printf("before: pid: %d\n",getpid());
      pid_t id=fork();
      printf("after:\n");
      if(id==0)
      {
          //子进程
          printf("我是子进程  pid: %d  ppid: %d\n",getpid(),getppid());
      }
      else if(id>0)
      {
          //父进程
          printf("我是父进程  pid:%d   ppid: %d\n",getpid(),getppid());                                                       
      }
      else
      {
          printf("出错\n");
      }
  
      return 0;
  }

二.进程终止

进程退出时的三种情况:

  • 代码运行完毕,结果正确
  • 代码运行完毕,结果不正确
  • 代码异常终止:异常退出本质是收到了对应的信号

进程退出的常用方法

  • return
  • exit
  • _exit

那么谁会关心一个进程的运行情况呢?

答案是父进程。子进程在退出时,会成为僵尸进程,需要父进程的回收。

那么父进程期望获得子进程退出时得哪些信息呢?

  • 子进程是否是异常退出
  • 没有异常;如果有,可以通过查看错误码来查看错误信息

可以通过查看退出码,来知晓进程的退出情况 

可以用以下命令查看最后一次进程退出的退出码

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
echo  $?

 我们可以打印一下,每个错误码对应着什么错误信息

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
int main()
{
     for(int i=0;i<100;i++)
     {
         printf("%d  : %s\n",i,strerror(i));
     }
  
     return 0;                                                                                                                }                                                                                                                    

可以发现,错误码为0时,代表代码正常执行完毕,所以我们平时主函数里的return 都是return 0

当然我们也可以自己设计一套错误码体系。

exit和_exit

exit 和 _exit 都可以退出进程,但是exit在退出进程前会做其它工作:

  •  执行用户通过 atexit或on_exit定义的清理函数。
  • 关闭所有打开的流,所有的缓存数据均被写入
  •  调用_exit

而 _exit 是直接退出进程,所以缓冲区绝对不在内核。

所以一般推荐使用 exit 函数来退出进程。

 return 退出

return是一种更常见的退出进程方法。执行return n等同于执行exit(n),因为调用main的运行时函数会将main的返回值当做 exit的参数。

三.进程等待

进程等待是什么?

通过系统调用wait/waitpid,来进行对子进程进行状态检测与回收的功能!

为什么要进程等待?

我们知道子进程在退出时会变成僵尸进程:

  • 僵尸进程无法被杀死,需要通过进程等待来杀掉它,进而解决内存泄漏问题---必须解决的
  • 我们要通过进程等待,获得子进程的退出情况---知道我布置给子进程的任务,它完成的怎么样了---可能关心,也可能不关心---可选的

怎么等待?

父进程通过调用 wait/waitpid 进行僵尸进程的回收问题!

wait函数

查看 man 手册 ,wait 函数所在的头文件是 <sys/types.h> 和 <sys/wait.h>

返回值:         成功返回被等待进程pid,失败返回-1。 参数:         输出型参数,获取子进程退出状态,不关心则可以设置成为NULL.

wait等待的是任意一个子进程

实例:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <sys/types.h>
#include <sys/wait.h> 

void Run()
{
    int cnt=5;
    while(cnt)
    {
        printf("我是一个子进程 pid: %d  ppid: %d\n",getpid(),getppid());
        cnt--;
        sleep(1);
    }
}

int main()
{
     pid_t id=fork();
     if(id==0)
     {
         //子进程
         Run();
         exit(0);
     }
     else if(id>0)
     {
         //父进程                                                                                           
         int cnt=10;
         while(cnt)
         {
            printf("我是一个父进程 pid: %d  ppid: %d\n",getpid(),getppid());
            cnt--;
            sleep(1);
         }
 
         pid_t ret=wait(NULL);
     }
     else 
         printf("出错\n");
   
     return 0;
}

可以用下面的指令查看运行时进程的变化

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
while :; do ps ajx | head -1 && ps ajx | grep testwait | grep -v grep;sleep 1;echo "------------------------"; done

 waitpid函数

waitpid 函数一共有三个参数

 pid: pid=-1,等待任何一个子进程。与wait等效。         pid>0,等待其进程ID与pid相等的子进程。 status:  WIFEXITED(status): 若为正常终止子进程返回的状态,则为真。(查看进程是否是正常退出) WEXITSTATUS(status): 若WIFEXITED非零,提取子进程退出码。(查看进程的退出码) options:   WNOHANG: 若pid指定的子进程没有结束,则waitpid()函数返回0,不予以等待。若正常结束,则返回该子进程的ID。

pid很容易理解,这里重点讲讲 status。

status 是一个输出型参数,它的类型是 int ,说明有32个比特位

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

低7位用来表示终止信号 第8位表示 core dump标志 第9位到第15表示退出状态,也就是说正常退出为0,异常退出为非0 所以除了上面的使用宏函数来访问status的退出码什么的还可以用下列方式访问 status 获取终止信号:status&0x7f  获取退出码:  (status>>8)&0xff

我们为什么要传一个输出型参数呢?可不可以使用全局变量代替这个输出型参数 status?

答案是不可以!因为进程之间具有独立性。

等待的原理

其实子进程在退出的时候,会把退出码,终止信号写入到PCB的 exit_code  和  exit_signal 变量中,等待进程时,也就是从子进程的PCB中读取这两个变量的值,并写入到输出型变量 status 中,这样父进程就可以知道子进程的退出信息了。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
int exit_code;
int exit_signal;

阻塞等待

waitpid的第三个参数 options 为0时,表示当子进程一直没有退出的时候,父进程处于阻塞等待。

什么是阻塞等待?

即在子进程退出前,父进程什么也不做,一直在等着子进程退出,此时父进程处于阻塞状态。

非阻塞轮询

当waitpid的第三个参数 options 为 WNOHANG ,父进程以非阻塞轮询的方式等待子进程。

什么是非阻塞轮询?

即父进程会检查一次看子进程有没有退出,没有则返回0,此时父进程可以做一些自己的事,而不是一味的等待子进程的退出,在子进程退出前循环以上的过程,直到子进程退出,返回 >0 的一个数,返回负数则表示等待失败。

实例:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
int main()
{
	pid_t id = fork();
	if (id == 0)
	{
		//子进程
		int cnt = 5;
		while (cnt)
		{
			printf("我是一个子进程  pid: %d  ppid: %d\n", getpid(), getppid());
			cnt--;
			sleep(1);
		}
	}
	else if (id > 0)
	{
		//父进程
		int status = 0;
		while (1)  //轮询
		{
			pid_t ret = waitpid(-1, &status, WNOHANG);  //非阻塞
			if (ret > 0)
			{
				printf("子进程退出,等待成功\n");
				break;
			}
			else if (ret == 0)
			{
				printf("你先等等,子进程还没有退出....\n");
				sleep(1);
			}
			else
			{
				printf("等待失败\n");
				break;
			}
		}

		return 0;
	}
}

四.进程替换

单进程的进程替换

在理解什么是进程替换之前,我们先来看看进程替换怎么使用,下面是操作系统提供的进程替换的一些函数

  • 这些函数如果调用成功则加载新的程序从启动代码开始执行,不再返回。
  • 如果调用出错则返回-1
  • 所以exec函数只有出错的返回值而没有成功的返回值。

参数所表达的意思:

  • l(list) : 表示参数采用列表
  • v(vector) : 参数用数组
  • p(path) : 有p自动搜索环境变量PATH
  • e(env) : 表示自己维护环境变量

以上的这些函数中,只有execve是系统调用,其它函数在底层都会调用这个函数。

对于像execl 和 execlp 有可变参数的函数,其实它们的使用方法很简单,从第二个参数开始,参数的写法就很我们在命令行中的一样,且最后一个参数是NULL。

可以想想,当我们要执行一个程序时,第一件事是什么?

第一件事就是要先找到这个程序,找到程序后做什么?

第二件事就是你得知道要怎么执行这个程序。

这样就能更好的理解这些函数为什么要这么用了

例如命令行中输入 ls -l -a (以单进程的进程替换来演示)

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
int main()
{
     execl("/usr/bin/ls","ls","-l","-a",NULL);                                    
  
     return 0;
}

下面是其它进程替换函数的一些用法

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
int main()
{
     extern char**environ;
     execl("/usr/bin/ls","ls","-l","-a",NULL);
  
     char*const myargv[]={"ls","-l","-a",NULL};
     execv("/usr/bin/ls",myargv);   //v表示数组
     execvp("ls",myargv);         //有p的可以省去路径                                
     execvpe("ls",myargv,environ);   //有e的可以自己控制环境变量,且采用的策略是覆盖而不是追加
    
     return 0;
}

 进程替换的原理

先来看这样一段代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
int main()
{
    printf("before:\n");
    execl("/usr/bin/ls","ls","-l","-a",NULL);
    printf("after:\n");

    return 0;
}

打印结果会是什么?

 发现只打印了before ,after呢?也就是 execl 前面的代码会被执行,后面的代码不会被执行,这是为什么?

进程替换的原理

进程在替换时,只会替换掉物理内存中原来程序的代码和数据,其它的并不会动,且调用exec并不创建新进程,所以调用exec前后该进程的id并未改变。

所以execl函数执行完后,原来的代码和数据就被替换了,物理内存中是全新的代码和数据,也就不是原来的代码,所以execl后的代码不会被执行,除非execl函数调用失败。

那么环境变量也是数据,它会被替换吗?

不会!!因为创建子进程的时候,环境变量已经被子进程继承下去了,所以进程替换不会替换环境变量。

多进程的进程替换

 前面的例子是单进程的执行系统命令的进程替换,接下来我们实现一个多进程的执行自己命令的进程替换。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
int main()
{


    pid_t id=fork();
    if(id==0)
    {
        execl("./other1exe","./other1exe",NULL);
    }
    else if(id>0)
    {
        wait(NULL);
        execl("./other2exe","./other2exe",NULL);
    }


    return 0;
}
多个目标的makefile文件的写法 

对于有多个目标的makefile文件,可以这样写,就可以一次生成所需要的全部文件。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
.PHONY:all 
all: other1exe other2exe test 
other1exe:other1exe.cpp 
	g++ -o $@ $^ -std=c++11

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

 定义一个伪目标 all ,all 的依赖文件就是你所需要生成的文件。

打印结果:


五.制作一个自己的shell 

 有了以上的这些知识后,我们就可以自己制作一个简易的myshell了,使它也能像shell那么使用。

这里要特别注意环境变量的维护!

        因为在linux中,环境变量的内容是在一个区域放着的,而环境变量表 env 中存的是环境变量的地址,这些地址指向所对应的环境变量;         而我们putenv一个环境变量时,其实是在环境变量表中找一个未使用的下标,把要导入的环境变量的地址放进去,这个地址就指向导入的环境变量的内容。         所以当我们要put环境变量时,只是将它的地址填入了环境变量表中,而环境变量的内容是由我们自己输入的,在我们自己创建的命令行参数表中,而这个命令行参数表是会变的,但环境变量表依然指向不变,但是其实所指向的内容已经变了,所以就会导致导入环境变量不成功。         为解决上述问题,我们需要自己创建空间,用来专门维护环境变量。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#define SIZE 1024
#define ARGV_SIZE 32
#define DELIM " \t"
#define EXIT_CODE -1
#define NONE -1
#define IN_RDIR 0
#define OUT_RDIR 1
#define APPEND_RDIR 2

int quit=0;
int lastcode=0;
char pwd[SIZE];
char commandline[SIZE];
char *argv[ARGV_SIZE];
char *rdirfilename=NULL;
int rdir=NONE;


//维护环境变量表
extern char**environ;
char myenv[SIZE];

const char*getusername()   //获取用户名
{
    return getenv("USER");
}


const char* gethostname()   //获取主机名
{
    return getenv("HOSTNAME");
}

void getpwd()   //获取当前路径 
{
    getcwd(pwd,sizeof(pwd));  getcwd是系统调用接口
}

void interate(char *cline,int size)  //交互
{
    getpwd();
    printf("[%s@%s %s]# ",getusername(),gethostname(),pwd);
    char*s=fgets(cline,size,stdin);
    assert(s);   //检查是否输入成功
    (void)s;  //一些编译器会对未使用的变量报警告,这里防止这个情况发生
    //abcd\n\0
    cline[strlen(cline)-1]='\0';  //将最后读入的回车变成 '\0' ,使其符合C形式的字符串
    
}


int separationline(char *cline,char*_argv[])   //分割字符串
{
    int i=0;
    _argv[i++]=strtok(cline,DELIM);   //注意strtok函数的用法
    while(_argv[i++]=strtok(NULL,DELIM));
    

    return i-1;  //返回命令行参数表的大小
}

void normalcom(int _argc,char *_argv[])   //普通命令
{
    pid_t id=fork();
    if(id==0)
    {
        execvp(_argv[0],_argv);   //通过进程替换来执行普通命令
        exit(EXIT_CODE);
    }
    else if(id>0)
    {
        int status =0;
        pid_t rid=waitpid(id,&status,0);  //等待回收子进程
        if(rid==id)
        {
            lastcode=WEXITSTATUS(status);  //设置错误码

        }
    }
    else 
    {
        perror("fork");
        return ;
    }
}

int buildcom(int _argc,char*_argv[])  //内建命令
{
    //cd  export  echo
    if(_argc==2&&strcmp(_argv[0],"cd")==0)
    {
        chdir(_argv[1]);
        getpwd();
     sprintf(getenv("PWD"),"%s",pwd);
     return 1;

    }
    else if(_argc==2&&strcmp(_argv[0],"export")==0)
    {
        strcpy(myenv,_argv[1]);
        putenv(myenv);
        return 1;
    }
    else if(_argc==2&&strcmp(_argv[0],"echo")==0)
    {
        if(strcmp(_argv[1],"$?")==0)   //打印最后一次程序退出的错误码
        {
            printf("%d\n",lastcode);
            lastcode=0;
        }
        else if(*_argv[1]=='$')  //打印环境变量
        {
            char*val=getenv(_argv[1]+1);
            if(val) printf("%s\n",val);

        }
        else   //普通打印
        {
            printf("%s\n",_argv[1]);
        }
        return 1;
    }

    if(strcmp(_argv[0],"ls")==0)   //特殊处理ls命令,为文件带上颜色,例如目录是蓝色的
    {
        _argv[_argc++]="--color";
        _argv[_argc]=NULL;
    }

    return 0;
}

int main()
{
    while(!quit)
    {
        //实现交互
        interate(commandline,sizeof(commandline));


        //分割命令
        int argc=separationline(commandline,argv);
        if(argc==0)
            continue;
        //执行命令
        
        //内建命令
        int n=buildcom(argc,argv);
        
        //普通命令
       if(!n) normalcom(argc,argv);
    }
    return 0;
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-01-23,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
【Linux】Linux进程控制>进程创建&&进程终止&&进程等待&&进程程序替换
在linux中fork函数时非常重要的函数,它从已存在进程中创建一个新进程。新进程为子进程,而原进程为父进程
用户10925563
2024/06/04
2060
【Linux】Linux进程控制>进程创建&&进程终止&&进程等待&&进程程序替换
【Linux】Linux进程控制 --- 进程创建、终止、等待、替换、shell派生子进程的理解…
1. 在调用fork函数之后,当执行的程序代码转移到内核中的fork代码后,内核需要分配新的内存块和内核数据结构给子进程,内核数据结构包括PCB、mm_struct和页表,然后构建起映射关系,同时将父进程内核数据结构中的部分内容拷贝到子进程,并且内核还会将子进程添加到系统进程列表当中,最后内核空间中的fork代码执行完毕,操作系统中也就已经创建出来了子进程,最后返回用户空间,父子进程执行程序fork之后的剩余代码。
举杯邀明月
2023/04/12
14.9K0
【Linux】Linux进程控制 --- 进程创建、终止、等待、替换、shell派生子进程的理解…
【Linux】进程控制
在 Linux 中 fork 函数是非常重要的函数,它从已存在进程中创建一个新进程。新进程为子进程,而原进程为父进程。我们在前面的学习中也遇到过,所以在此简单介绍一下。
YoungMLet
2024/03/01
1580
【Linux】进程控制
【Linux】万字解读<进程控制>:创建&中止&等待&替换
YY的秘密代码小屋
2024/09/11
1130
【Linux】万字解读<进程控制>:创建&中止&等待&替换
Linux进程控制
在进程概念这篇文章中,我们浅浅地了解了一下fork函数,它的功能是让父进程去创建一个子进程,并且有两个返回值,对应着父进程的返回值和子进程的返回值。那么,为什么会这样?接下来我们好好地讨论一下fork函数。
二肥是只大懒蓝猫
2023/03/30
2.4K0
Linux进程控制
Linux之进程控制
此外还可以通过调用fork函数创建子进程,子进程和父进程共享fork之后的代码,可以采用对fork返回值进行判断的办法来让父子进程分别执行后续代码的一部分。
始终学不会
2023/04/17
8310
Linux之进程控制
【linux】进程等待与进程替换
任何子进程,在退出的情况下,一般必须要被父进程进行等待。进程在退出的时候,如果父进程不管不顾,退出进程,状态Z(僵尸状态),内存泄漏
用户11029103
2024/11/16
880
【linux】进程等待与进程替换
Linux系统-进程控制
Linux进程控制 零、前言 一、进程创建 1、fork函数 2、fork返回值 写时拷贝 3、fork用法 4、fork失败 二、进程终止 1、退出码 2、退出方法 1) 调用_exit函数 2)调用exit函数 3)main函数return 4)异常退出 3、理解终止 三、进程等待 1、等待方法 2、获取status 3、理解等待 四、进程替换 1、替换原理 2、替换方法 五、实现简易shell 零、前言 前篇我们讲解学习了关于进程的概念知识,本章主要讲解关于进程的控制,深入学习进程 一、进程创建
用户9645905
2022/11/30
1.5K0
Linux系统-进程控制
【Linux】进程控制(创建、终止、等待)
在前文中我们了解了fork函数的使用,以及写时拷贝机制的原理等,并且也学习了什么是僵尸进程,但是并没有具体讲到应如何处理僵尸进程,本次章节将对fork函数以及如何终止进程,还有僵尸进程的处理做更为详细的探讨。
诺诺的包包
2023/04/02
3.5K0
【Linux】进程控制(创建、终止、等待)
【Linux】进程控制
我们在前面的文章中多次使用过fork函数,我们在这里再来简单概括一下进程的创建 fork可以在已有的进程中创建出一个新进程,老进程为父进程,新进程为子进程
s-little-monster
2025/02/14
260
【Linux】进程控制
进程控制
在linux中fork函数是非常重要的函数,它从已存在进程中创建一个新进程。新进程为子进程,而原进程为父进程。
海盗船长
2020/08/27
7300
【Linux修炼】11.进程的创建、终止、等待、程序替换
在linux中fork函数是非常重要的函数,它从已存在进程中创建一个新进程。新进程为子进程,而原进程为父进程。
每天都要进步呀
2023/03/28
6.2K0
【Linux修炼】11.进程的创建、终止、等待、程序替换
Linux进程控制
函数也被称为子程序,与进程退出时返回退出码类似,函数执行完毕也会返回一个值,这个值通常用于表示函数的执行结果或状态。
绝活蛋炒饭
2024/12/16
1030
Linux进程控制
Linux探秘坊-------9.进程控制
当进程调⽤⼀种exec函数时,该进程的 ⽤⼾空间代码和数据完全被新程序替换 ,从新程序的启动例程开始执⾏。调⽤exec并不创建新进程,所以调⽤exec前后该进程的 id并未改变。
hope kc
2025/03/22
570
Linux探秘坊-------9.进程控制
Linux进程控制【进程程序替换】
子进程 在被创建后,共享的是 父进程 的代码,如果想实现自己的逻辑就需要再额外编写代码,为了能让 子进程 执行其他任务,可以把当前 子进程 的程序替换为目标程序,此时需要用到 Linux 进程程序替换相关知识
北 海
2023/07/01
2590
Linux进程控制【进程程序替换】
Linux之进程控制
退出码是用来标识一个进程任务执行结果的情况。因为成功只有一种情况,而失败的情况很多,因此,一般情况下0表示执行成功,非0表示执行失败。非0的数字不同,所表示的错误不同。系统对于退出码一般都有着相应的文字藐视,当然我们也可以自定义,也可以直接使用系统给定的映射关系。(例如,strerror这个函数)
摘星
2023/10/15
1990
Linux之进程控制
【Linux】探索进程控制奥秘,解锁高效实战技巧
因为字符串具有常量属性,字符常量不可被修改。这里的问题是字符串为什么会有常量属性呢?
用户11316056
2024/10/16
510
【Linux】探索进程控制奥秘,解锁高效实战技巧
Linux进程控制【创建、终止、等待】
进程 创建后,需要对其进行合理管理,光靠 OS 是无法满足我们的需求的,此时可以运用 进程 控制相关知识,对 进程 进行手动管理,如创建 进程、终止 进制、等待 进程 等,其中等待 进程 可以有效解决僵尸 进程 问题
北 海
2023/07/01
3310
Linux进程控制【创建、终止、等待】
Linux进程控制
返回值:自进程中返回0,父进程返回子进程id,出错返回-1。 进程拥有独立性,fork之后就变成了两个程序,父子进程共享后边的代码。 那么为什么给父进程返回的就是子进程的pid,而给子进程返回的就是0呢? 就好比孩子只能有一个亲生的父亲,而一个父亲可以拥有很多亲生孩子,每个孩子都是独立不同的。 fork函数是在什么时候创建的子进程呢?
有礼貌的灰绅士
2023/03/28
2.9K0
Linux进程控制
Linux进程控制
进程是操作系统中的一个重要概念,它是一个程序的一次执行过程,程序是进程的一种静态描述,系统中运行的每一个程序都是在它的进程中运行的。
xxpcb
2020/08/04
2K0
相关推荐
【Linux】Linux进程控制>进程创建&&进程终止&&进程等待&&进程程序替换
更多 >
LV.1
这个人很懒,什么都没有留下~
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验