开发者社区> 二肥是只大懒蓝猫> 正文

Linux进程控制

简介: 重点讲解了Linux进程控制板块:进程的创建、终止、等待,替换等待。
+关注继续查看

一.进程创建

fork()函数:

在进程概念这篇文章中,我们浅浅地了解了一下fork函数,它的功能是让父进程去创建一个子进程,并且有两个返回值,对应着父进程的返回值和子进程的返回值。那么,为什么会这样?接下来我们好好地讨论一下fork函数。

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

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

image.gif

先来看三个问题:

1.如何理解fork函数有两个返回值的问题?

2.如何理解fork函数返回后,子进程中返回0,父进程返回子进程id?

3.如何理解同一个id值,为什么会保存两个不同的值,让if? else? if同时执行?

现象看下面代码:

#include <stdio.h>
#include <unistd.h>
int global_value = 100;
int main()
{
    pid_t id = fork();
    if(id < 0)
    {
        printf("fork error\n");
        return 1;
    }
    else if(id == 0)
    {
        int cnt = 0;
        while(1)
        {
            printf("我是子进程, pid: %d, ppid: %d | global_value: %d, &global_value: %p\n", getpid(), getppid(), global_value, &global_value);
            sleep(1);
            cnt++;
            if(cnt == 10)
            {
                global_value = 300;
                printf("子进程已经更改了全局的变量啦..........\n");
            }
        }
    }
    else
    {
        while(1)
        {
            printf("我是父进程, pid: %d, ppid: %d | global_value: %d, &global_value: %p\n", getpid(), getppid(), global_value, &global_value);
            sleep(2);
        }
    }
    sleep(1);
}

image.gif

结果:

K]Y(XU%MGV66HNXZT7CTBU5.png

?先来解决第二个问题:2.如何理解fork函数返回后,子进程中返回0,父进程返回子进程id?

对于这个问题,我们可以结合现实:一个父亲可以有多个孩子,而每个孩子只能也必定有一个父亲。而对于孩子而言,父亲对于孩子来说是具有唯一性的,而孩子对于父亲来说,父亲需要给孩子取名,才能准确地从几个孩子中找到某个孩子,比如son1,son2,son3.

所以,父子进程也一样,子进程返回0,是因为父亲只有一位。而父进程返回的是子进程的id,即是孩子的名字。

然后来看第一个问题:1.如何理解fork函数有两个返回值的问题?

fork()函数,是操作系统提供的函数,在用户空间调用fork函数的时候,实际上就是在调用内核空间中的fork函数。在fork函数的函数主体中,就有创建子进程的相关指令,最后是返回 子进程的pid。那么在返回的时候,是分流了。因为在到达return指令之前,子进程就已经被创建好了,并且有可能已经在OS的运行队列当中,准备被调度,因此,此时对于fork函数的这个return指令,不仅仅是被父进程使用,还会给子进程拿去使用。所以,fork函数就有两个返回值,一个是返回子进程的,一个是返回父进程的。

第三个问题:3.如何理解同一个id值,为什么会保存两个不同的值,让if? else? if同时执行?

返回的本质就是写入。所以,对于pid_t id = fork();为什么会保存两个不同的值,就先看谁先返回,那就谁先写入id。比如父进程先返回,先写入id,此时id的值是子进程的pid,此时的子进程中的id,它的地址和内容,跟父进程的是一样的,就是指向了同一个地址。但是当子进程返回的时候,此时为了保证进程的独立性,OS就会进行写时拷贝,额外给子进程一个id的空间,此时的现象是:父子进程的id的地址是一样的,但是!内容不一样,这里就用到了上一篇的文章:进程概念? ?所了解到的进程地址空间的知识。这就是id为什么会保存两个不同的值。

然后,父子进程会共用上面那段代码,就是if else if的代码,当id的值对应着不同的判断条件,代码就指向那种指令。也就看到了if? else if会同时执行的现象了。

NAR1]Y04EZ8(Z4C[@3X@II9.png

写时拷贝

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

(10[D0D$03~3}9)$AIG`]]D.png

?fork的常规用法

1、一个父进程希望复制自己,使父子进程同时执行不同的代码段。例如,父进程等待客户端请求,生成子进程来处理请求。

2、一个进程要执行一个不同的程序。例如子进程从fork返回后,调用exec函数。

fork也有调用失败的时候,其原因很大可能是系统中有太多的进程或者实际用户的进程数超过了限制。

二、进程终止:

在谈进程退出情况之前,我们先来聊聊退出码的问题。

相信我们在写代码的时候,特别是使用C/C++写代码时,我们都会写main函数,然后最后写一个return 0。那么问题来了,return 0的含义是什么?0又是什么意思?为什么是0,而不是1,不是2等等。

其实return 0中的0,就是退出码的意思,而return 0,标定进程退出的结果是否正确。

来看下代码和对应的测试结果:

#include<stdio.h>
int add(int begin, int end)
{
    int i = 0;
    int sum = 0;
    for (i = begin; i < end; i++)
    {
        sum += i;
    }
    return sum;
}
int main()
{
    //进程退出码的作用就是:让我们得知我们写的代码所完成的任务是否完成
    int num = add(1, 100);
    if (num == 5050)
    {
        return 0;
    }
    else
    {
        return 1;
    }
    //进程对出的时候,对应的退出码
 //标定进程执行的结果是否正确
    //return 0;
}

image.gif

运行后,我们通过echo $?来查看退出码的结果:

$?是环境变量的一种,$?的作用是永远记录最近的一个进程在命令行中执行完毕时对应的退出码(main---->return ?;)

`JM~J)2R]GEA(W`2EOQ%LPD.png

?可以看到,当执行了上面的那个程序之和,退出码的结果是1,但当我们再次执行echo $?的指令后,发现变为0了,是因为$?会对最近的一个程序进行判断。echo $?本身也是一个程序指令,所以后面的退出码为0.

接下来我们来看看不同数字的退出码代表着什么意思:

FNHN{KHEWN8H`_RV(AFQA{V.png

?从图中可以得知,0代表着成功的意思,而非0的数字,代表着各种失败的提升。可以举的例子有:当我们在命令行写入:ls asdasdas,打开这样的一个文件,但是我们没有这样的文件,那么可以看到结果如下:

VQ668{JRIPOFJKNA@D0N31P.png

?好了,在了解了退出码之和,我们可以谈谈进程退出的情况了。

进程退出情况

1.代码运行完毕,结果正确? ----return 0;

2.代码运行完毕,结果不正确 -------return !0? ?退出码在这个时候起效

3.代码异常终止-----这情况下退出码无意义

进程如何结束?

有两种办法:

1. 从main返回

2. 调用exit

第一种很好理解,我们的程序都是从main函数开始,最后由main函数的return 0来返回,终止程序。对于第二种,我们需要认识exit()函数。exit函数其实在平时写代码的时候,就用过几次。选择来了解一下它。

exit()函数的作用是终止进程,不管在哪调用它:不管是在main函数里面调用exit,还是在main函数调用的函数的内部使用它,只要执行了exit函数,整个进程都会终止。

还有一个功能与exit函数类型的,叫做_exit()。它们的区别就在于,exit函数是库函数,而_exit属于系统调用,并且,exit()函数会刷新缓冲区,_exit并不会刷新缓冲区。看下图:

UNKD2[J2~KPI)5ILRT62X]X.png

?温馨提示:库函数和系统调用的不同之处在于,库函数的调用,本质上就是建立在了系统调用之上,是操作系统提供给用户写代码时使用的函数。库函数——系统调用——OS三者的层次关系大概如下图:

IV%8ISD3N[RHB65WL26@Q9E.png

当然啦,如果存在父子进程同时使用一段代码的时候,而且exit函数是在当fork函数返回值为0,也就是子进程执行的代码段的时候,终止的子进程。即谁调用谁终止。

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

?进程终止就到这。接下来我们来谈谈进程等待。

三、进程等待

进程等待可以解决僵尸进程问题。

所以,进程等待是很有比较性的:

1.子进程退出,父进程如果不管不顾,就可能造成‘僵尸进程’的问题,进而造成内存泄漏。

2.另外,进程一旦变成僵尸状态,那就刀枪不入,“杀人不眨眼”的kill -9 也无能为力,因为谁也没有办法杀死一个已经死去的进程。

3.最后,父进程派给子进程的任务完成的如何,我们需要知道。如,子进程运行完成,结果对还是不对,或者是否正常退出。

4.父进程通过进程等待的方式,回收子进程资源,获取子进程退出信息

进程等待方法

1.wait方法。

wait()是一个函数。通过man我们可以搜出其基本信息:

4NA__KMP@I~5Q(H@S512DAC.jpg

pid_t wait(int*status);

返回值:

成功返回被等待进程pid,失败返回-1。

参数:

输出型参数,获取子进程退出状态,不关心则可以设置成为NULL

?

?它的功能是让进程等待,从而时父进程回收子进程资源。

看下面的代码:

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
int main()
{
    pid_t id = fork();
    if (id == 0)
    {
        //子进程
        int cnt = 10;
        while (cnt)
        {
            printf("我是子进程: %d, 父进程: %d,cnt: %d\n", getpid(), getppid(), cnt--);
            sleep(1);
        }
        exit(0);//进程终止
    }
    //父进程
    sleep(15);
    pid_t ret = wait(NULL);
    if (id > 0)
    {
        printf("wait success: %d\n", ret);
    }
    return 0;
}

image.gif

上面程序的功能:我们期望,子进程返回0,即进入while循环后,10秒的时间内,子进程在运行着,然后子进程终止,此时,父进程中的sleep的时间也过了10秒,还有5秒,在这5秒的时间内,子进程就是一个僵尸进程(Z)。我们期望,通过父进程中的wait,可以回收子进程的资源,从而解决僵尸进程。看下面结果:

M(GU[EZA@W7VIJRLMPDP6XW.jpg

TD3@W4SV48QVT)Z~{K}XV@7.jpg

?可以看到,有在一段时间内,子进程的状态为Z,即僵尸状态,然后变成了STAT。

2.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。

?

①如果子进程已经退出,调用wait/waitpid时,wait/waitpid会立即返回,并且释放资源,获得子进程退出信息。

②如果在任意时刻调用wait/waitpid,子进程存在且正常运行,则进程可能阻塞。

③如果不存在该子进程,则立即出错返回

获取子进程status:

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

~{O~FL9J75[)XF~7(XSU_0G.png

?当子进程变成僵尸状态的时候,子进程的PCB内部就保存有子进程的退出码和退出信号,父进程通过status,将子进程的这些资源拿到手。

阻塞与非阻塞

阻塞:当父进程通过系统调用wait/waitpid去获取子进程的资源时,但子进程还没有退出,等待的这个状态,就叫做阻塞。

非阻塞:子进程还没退出,父进程就不等了,直接返回

下面的代码可以测试一下非阻塞

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
int main()
{
    pid_t id = fork();
    (id == 0)
    {
        //子进程
        int cnt = 10;
        while (cnt)
        {
            printf("我是子进程: %d, 父进程: %d,cnt: %d\n", getpid(), getppid(), cnt--);
            sleep(1);
        }
        exit(0);//进程终止
    }
    //父进程
       //sleep(15);                                                                                                                                
       // pid_t ret = wait(NULL);
    int status = 0;
    while (1)
    {
        pid_t ret = waitpid(id, &status, WNOHANG);
        if (ret == 0)
        {
            //waitpid调用成功,子进程没有退出
               //子进程没有退出,waitpid没有等待失败,仅仅只是检查状态                                                                              
            printf("wait done, but child is running......\n");
        }
        else if (ret > 0)
        {
            //waitpid调用成功,子进程退出了
            printf("wait success: %d, sig number: %d,child eixt code: %d\n", ret, (status & 0x7F), (status >> 8) & 0XFF);
            break;
        }
        else {
            //waitpid调用失败
            printf("waitpid call failed\n");
            break;
        }
    }
    return 0;
}

image.gif

我们可以看到,父进程在进行轮询检测,直到子进程退出。

4[5I1X6X1{DQ)P(U`T183(0.png

?非阻塞的好处是不会占用父进程的资源,父进程在轮询的期间可以去做别的事。

四、进程替换

首先需要知道的是创建子进程的目的:

a. 让子进程执行父进程代码的一部分:执行父进程在磁盘上对应的一部分代码。

b、让进程执行一个全新的程序:让子进程加载磁盘上指定的程序,执行新程序的代码和数据——>这动作就叫做进程的替换

接下来将以四步来对进程的替换进行学习:①先见见猪跑,看看什么是进程替换;②理解原理(是什么,为什么,怎么办);③对应的方法;④应用的场景

4.1?先见见猪跑,看看什么是进程替换

需要用到替换函数(execl)

其实有六种以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[]);

函数解释:

这些函数如果调用成功则加载新的程序从启动代码开始执行,不再返回。

如果调用出错则返回-1

所以exec函数只有出错的返回值而没有成功的返回值。因为成功的返回值没有必要,都已经替换了进程了,即使返回了,这个值也用不了。

命名理解:

l(list) : 表示参数采用列表。意思是将参数一个一个地传入exec*

v(vector) : 参数用数组。意思是可以将我们需要传入的参数放在数组里面,然后统一传入。

p(path) : 有p自动搜索环境变量PATH

e(env) : 表示自己维护环境变量

C`B5L(KLFRIO242S)VXPU81.png

?温馨提示:int execl(const char *path, const char *arg, ...);第一个参数的意思是找到这个程序的路径,第二个参数的意思是如何执行这个程序,第三个参数? ...? 是c语言中的可变参数列表,像scanf,printf等都有...)。

替换函数的功能就是将指定的程序 (注意是程序,不是进程) 加载到内存当中,让指定的程序执行。请看下面代码:

#include<stdio.h>
#include<unistd.h>
int main()
{
    printf("process is running..\n");
    execl("/usr/bin/ls", "ls", "--color=auto", "-a", "-l", NULL);
    printf("process running done...\n");
    return 0;
}

image.gif

通过替换函数execl,我们可以执行别人的代码程序,比如ls,-a,-l。

~OA]$XGO0{LRJ`3HUIT{`E3.png

?可以看到,在代码里面的第二个printf没有将我们需要打印的内容打印出来,因此我们需要了解清除进程替换的原理。

4.2 进程程序替换原理?

进程程序替换本质上就是将指定的程序的代码和数据,从磁盘上加载到物理内存的指定的位置上,并且把原来位置上的的数据和代码给覆盖掉,因此,在进程程序替换的时候,并没有创建新的进程。

7$8)%CTH$[}$765VLX`~X)H.png

?所以我们回到上面的那个问题,为什么第二个printf没有执行?

答案就是:因为第二个printf是在execl之后的,在执行了execl后,第二个printf被覆盖掉了,所以也就没办法执行了。

我们再举一个例子,那就是再父子进程中,子进程进行程序替换:看下面代码:

#include<stdio.h>
#include<unistd.h>
#include<assert.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/wait.h>
int main()
{
    printf("process is running..\n");
    pid_t id = fork();
    assert(id != -1);
    if (id == 0)
    {
        sleep(1);
        execl("/usr/bin/ls", "ls", "-a", "-l", "--color=auto", NULL);
        exit(1);
    }
    int status = 0;
    pid_t ret = waitpid(id, &status, 0);
    if (ret > 0)
    {
        printf("wait success: exit code:%d,sig: %d\n", (status >> 8) & 0xFF, status & 0x7F);
    }
    return 0;
}

image.gif

结果如下:

TGIUTMK}YHDMPI2V}49Z6C4.png

因为进程具有独立性,所以当子进程进行程序替换的时候,OS就会在物理内存中进行写时拷贝,页表的映射关系重新安排。由此,子进程的程序替换也不会影响到了父进程。

替换自己写的程序:

①C程序替换C程序:

那么接下来,我们试着去写一段程序, 然后用另外一段代码程序来执行,也就是说,上面程序替换是替换系统命令的, 现在是替换自己写的代码程序。

创建一个my_exec.c的C程序。

#include<stdio.h>
int main()
{
    printf("这是一段C程序\n");
    printf("这是一段C程序\n");
    printf("这是一段C程序\n");
    return 0;
}

image.gif

然后在my_test.c的C程序总,使用execl函数即可:

execl("./my_exec", "my_exec",NULL);

image.gif

UW{A528T_S3L2%}DRZO5{LL.png

?②C程序替换C++程序:

没错,在替换函数中,我们可以在C程序的代码中去替换CPP的程序,因为是系统调用,系统就是老大,系统想替换谁就是谁,而且程序替换,就是叫程序替换,不叫语言替换,C++、Java。shell和python都是没问题的。这里不演示了,演示的例子无非就是将后缀改为cpp,并且使用C++的语法,操作过程几乎差不多。

4.3 对应的方法

前面我们已经将了第一个方法:int execl(const char *path, const char *arg, ...);

那么接下来我们谈谈第二个方法:int execlp(const char *file, const char *arg, ...);

execlp的使用方法,就是不需要带路径:

execlp("ls", "ls", "--color=auto", "-a", "-l", NULL);

image.gif

这里面的两个"ls",并不是重复,因为第一个"ls"的意思是要执行的对象,第二个"ls"的意思是如何执行。

第三个方法:int execv(const char *path, char *const argv[]);

带v,需要传的是数组的形式,即将需要传入的参数放入一个数组中,然后传入数组即可。

char* const avg[] = {"ls", "-a", "-l", "--color=auto", NULL};
execlv("/usr/bin/ls", avg);

image.gif

第四个方法:int execvp(const char *file, char *const argv[]);

execvp可以看作是execv和execp的结合使用

execlvp("ls", avg);

image.gif

第五个方法:int execle(const char *path, const char *arg, ...,char *const envp[]);

第四个参数,我们传的是环境变量。

在my_exec.c的程序中,加入环境变量:

#include<stdio.h>
#include<stdlib.h>
int main()
{
    //系统就有
    printf("PATH:%s\n", getenv("PATH"));
    printf("PWD:%s\n", getenv("PWD"));
    //自定义环境
    printf("MYENV:%s\n", getenv("MYENV"));
    printf("这是一段C程序\n");
    printf("这是一段C程序\n");
    printf("这是一段C程序\n");
    return 0;
}

image.gif

①自定义变量:

然后拿到my_test.c中:

char* const envp[] = {(char*)"MYENV=1122334455",NULL};
 execle("./my_exec","my_exec",NULL,envp);

image.gif

结果发现,没有将系统自带的环境变量的内容输出。

]RR`29E~~SQMKZDPC4L`)GJ.png

?②系统自带的环境变量:

extern char **environ;                                                                                                  
 execle("./my_exec","my_exec",NULL,environ);

image.gif

结果发现,没有了自定义的环境变量

PYP%V9]05$)2Q[20YLZ@JFF.png

?那么,我想把自定义的和系统自带的环境变量都输出:使用putenv函数,将自定义环境变量导入系统的环境变量表中。

putenv((char*)"MYENV=44332211");//将指定环境变量导入到系统中,即environ指向的环境变量表                                                   
 execle("./my_exec","my_exec",NULL,environ);

image.gif

可以看到,即有系统自带的,也有自定义的。

Z6D${S4$V[GW4HH0PSZKFPY.png

?其实对于execle,我们可以与 int main(int argc,char *argv[],char *env[]){}结合起来谈谈。代码和数据加载到内存的操作,其实就是操作系统调用了exec*函数完成的,所以在Linux的系统中,exec*是加载器。exec*函数的功能就是将程序加载到内存嘛,这是谈的第一点。第二点就是,对于main函数而言,是先进行程序的加载,才会开始调用main函数,那么main函数的参数,就是由execle传参传过去的!

第六个方法:真正的系统调用的接口:

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

这个方法是真正的系统调用的接口,上面五个,都是基于系统调用的接口封装起来的,是为了有更多的选择性。

总结一下exec*家族的成员:

MBET24T0JOG09J_8M49H3}4.png

4.4 应用场景

?综合前面的知识,实现一个简单的shell。

#include<stdio.h>
#include<unistd.h>
#include<assert.h>
#include<string.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<stdlib.h>
#define NUM 1024
#define OPT_NUM 64
char lineCommand[NUM];
char* myargv[OPT_NUM];
int mian()
{
    while (1)
    {
        //输出提示符
        printf("用户名@主机名 当前路径# ");
        fflush(stdout);
        //获取用户输入,输入的时候,回输入\n
        char* s = fgets(lineCommand, sizeof(lineCommand) - 1, stdin);
        assert(s != NULL);
        (void)s;
        //清除最后一个\n
        lineCommand[strlen(lineCommand) - 1] = 0;
        //字符串切割
        myargv[0] = strtok(lineCommand, " ");
        int i = 1;
        while (myargv[i++] = strtok(NULL, " "));
        //条件编译
#ifdef DEBUG
        for (int i = 0; myargv[i]; i++)
        {
            printf("myargv[%d]: %s\n", i, myargv[i]);
        }
#endif
        //指向命令
        pid_t id = fork();
        assert(if != -1);
        if (id == 0)
        {
            execvp(myargv[0], myargv);
            exit(1);
        }
        waidpid(id, NULL, 0);
    }
}

image.gif

?最后,我们指向myshell的程序后,输入一些命令行指令,那么就可以通过execvp去将对应的程序加载到内存,就可以执行这些程序了!

47`BWVP{JL%{D_8)8Y43O73.png

?本文结束~喜欢的话可以点波关注。

版权声明:本文内容由便宜云服务器实名注册用户自发贡献,版权归原作者所有,便宜云服务器开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《便宜云服务器开发者社区用户服务协议》和《便宜云服务器开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
Linux进程控制
进程控制 1. fork后内核做什么? 分配新的内存块和内核数据结构给子进程 将父进程部分数据结构拷贝子进程 将子进程添加到系统进程列表中 fork返回开始调度器调度 2. fork调用失败的原因 系统中有太多的进程 实际用户的进程数超过了上限 3. 进程退出场景 代码运行完毕,结果正确 代码运行完毕,结果不正确 代码异常终止 4. 查看进程退出码 echo $? 只会保留最近一次执行的进程的退出码
13 0
Linux线程控制
线程控制的相关操作:线程创建、线程终止、线程等待和线程分离。
30 0
Linux进程控制【进程程序替换】
子进程 在被创建后,共享的是 父进程 的代码,如果想实现自己的逻辑就需要再额外编写代码,为了能让 子进程 执行其他任务,可以把当前 子进程 的程序替换为目标程序,此时需要用到 Linux 进程程序替换相关知识 子进程 替换为其他程序后,无法再执行原有程序,但 进程 始终为同一个
39 0
Linux进程控制【创建、终止、等待】
进程创建后,需要对其进行合理管理,光靠OS是无法满足我们的需求的,此时可以运用进程控制相关知识,对进程进行手动管理,如创建进程、终止进制、等待进程等,其中等待进程可以有效解决僵尸进程问题
60 0
Linux进程管理
进程管理是操作系统的最重要的功能之一。有效率的进程管理能保证一个程序平稳而高效地运行。 Linux的进程管理与UNIX的进程管理相似。它包括进程调度、中断处理、信号、进程优先级、上下文切换、进程状态、进度内存等。
63 0
12.1 Linux进程管理
无论是 Linux 系统管理员还是普通用户,监视系统进程的运行情况并适时终止一些失控的进程,是每天的例行事务。和 Linux 系统相比,进程管理在 Windows 中更加直观,它主要是使用”任务管理器”来进行进程管理的。
76 0
Linux进程控制(1)
Linux进程控制(1)
64 0
Linux进程控制(2)
Linux进程控制(2)
80 0
Linux进程控制(3)
Linux进程控制(3)
75 0
操作系统基础知识(2)——进程的描述与控制
操作系统基础知识(2)——进程的描述与控制
164 0
+关注
二肥是只大懒蓝猫
喜欢在下雨天里安安静静地写代码
文章
问答
视频
文章排行榜
最热
最新
相关电子书
更多
ECS运维指南 之 Linux系统诊断
立即下载
低代码开发师(初级)实战教程
立即下载
阿里巴巴DevOps 最佳实践手册
立即下载
相关实验场景
更多
相关镜像
http://www.vxiaotou.com