Linux系统进程的创建,终止及进程的前后台切换

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_41500251/article/details/84792398

人生不易,生活无趣,一起找点乐子吧。欢迎评论,和文章无关的也可以。

 

 

进程是操作系统中程序运行的基本单位,在操作系统中,进程的相关描述由 3 个部分组成:进程控制块(Process Control Block,PCB)、有关程序段和该程序段对其进行操作的数据结构集。每个进程用数据结构 task_struct 描述,最初的进程在系统初始化时生成,而以后的进程均有已有的进程通过进程创建函数 fork()来创建。当一个进程在创建时,系统为该进程建立一个task_struct 结构,当进程运行结束时,系统就会撤销该进程的task_struct 结构。

 

 

下面通过代码 fork_test.c 给出使用 fork()函数的基本过程。
程序文件如下:

/**** fork_test.c ****/
#include <unistd.h>
#include <stdio.h>
int main ()
{
    pid_t fpid; //fpid 表示 fork 函数返回的值
    int count=0;
    fpid=fork();
    if (fpid < 0)
        printf("error in fork!");
    else if (fpid = = 0)
    {
        printf("i am the child process, my process id is %d\n",getpid()\n);
        count++;
    }
    else
    {
        printf("i am the parent process, my process id is %d\n",getpid()\n);
        count++;
    }
    printf("The counter result is: %d\n",count);
    return 0;
}

程序分析:

unistd.h头文件为进程方法调用的前提。pid_t即为数据类型之一,类比int去理解就好,保存进程状态码的一种特殊类型。前面说到fork创建进程,fork的返回值有三种:

1、对于父进程,fork返回创建的子进程的进程码,也就是子进程的fpid

2、对于子进程,fork返回0

3、返回-1,表示创建新进程失败。这里的原因也有两种:

①当前的进程数已经达到上限,此情况errno的值为EAGAIN

②内存不足,创建失败,此情况errno的值为ENOMEM。

 

返回到程序中,当程序开始执行,此时只有一个进程,主进程。我们记为P,当执行到fork()时,主进程P创建子进程,我们记为P1。此时执行这段代码的进程就成了两个,两个进程一起执行fork之后的代码,且主进程结束的前提是子进程结束,也因此,子进程先执行,主进程紧随其后。

                        

也就是说当主程序执行到fork时,先挂起,执行完子程序再回来。前面提到,对于子程序fork()返回值是0,那么输出条件为0的语句,致此。子程序的代码执行完毕,则返回主进程。刚刚主进程挂在fork,继续执行。对于主程序,fork返回的是子程序的程序号。则主程序执行fpid大于0的语句。(ps:printf语句中的getpid()的功能是得到当前进程号)

也就是说程序执行完,显示子程序输出,然后主程序输出。

编译执行:

That's all.如果不够吃别急,等下还有个稍微复杂的例子。

 

进程的终止:

终止一个进程的命令很酷:kill。简单粗暴,不解释。

让我们在终端打开个文本(或者说是创建),命令:vi os.txt

当文本处于编辑状态时,就说明vi进程正在进行,当你编辑完毕保存退出后,vi进程才会被kill。(以上的进程结束为操作系统自动执行过程)我们来试试手动kill it。

终端执行上面命令,会进入文本的内容页面。不要退出~,我们重启一个新的终端,在终端里输入命令:ps -ef。

这个命令可以常看当前进程的状态信息,我们找到vi进程,查看他的进程号,也就是pid。

然后在终端中kill pid,来结束进程。并返回到我们创建os.txt的那个终端中看看情况:

手刃的感觉好吗?(真香~~~)

 

 

进程前后台的切换:

我们依旧拿文本来举例好了,终端执行命令:vi test.txt

进入文本内容界面,此时按ctrl + z,将进程后台暂停挂起。界面回到命令行,输入命令jobs,可以看到当前后台的进程。

我们看到vi进程后台标号1,命令fg 1。即可将vi进程提到前台,bg 1将1号进程后台运行,这里对于文本进程没用。

 

 

进程创建的第二个例子:

/***** fork_test1.c *****/
#include <unistd.h>
#include <stdio.h>
int main(void)
{
    int i=0;
    printf("I son/pa ppid pid fpid/n"); /*ppid 指当前进程的父进程 pid,pid 指
    当前进程的 pid,fpid 指 fork 返回给当前进程的值*/
    for(i=0;i<2;i++){
        pid_t fpid=fork();
        if(fpid= =0)
            printf("%d child %4d %4d %4d/n",i,getppid(),getpid(),fpid);
        else
            printf("%d parent %4d %4d %4d/n",i,getppid(),getpid(),fpid);
    }
    return 0;
}

(getpid,得到当前进程号。getppid,得到当前进程的父进程的进程号。)

有了第一个例子,能猜到这个例子的输出吗?尝试下,写下来,然后看下面结果:

 

 

 

 

 

 

 

 

 

 

 

 

 

是不是没有想到除了标签行外,竟然输出了6条内容。再回头想想,如果没想明白看看下面的分析吧。

 

 

 

 

 

为了直观,我们把程序改一下,把循环结构改成顺序结构,这样来对程序进行分析。

/***** fork_test1.c *****/
#include <unistd.h>
#include <stdio.h>
int main(void)
{
    int i=0;
    printf("I son/pa ppid pid fpid/n"); /*ppid 指当前进程的父进程 pid,pid 指
    当前进程的 pid,fpid 指 fork 返回给当前进程的值*/


    ************************
    for(i=0;i<2;i++){
        pid_t fpid=fork();
        if(fpid= =0)
            printf("%d child %4d %4d %4d/n",i,getppid(),getpid(),fpid);
        else
            printf("%d parent %4d %4d %4d/n",i,getppid(),getpid(),fpid);
    }
    ************************

    int i = 0;
    pid_t fpid=fork();
    if(fpid= =0)
        printf("%d child %4d %4d %4d/n",i,getppid(),getpid(),fpid);
    else
        printf("%d parent %4d %4d %4d/n",i,getppid(),getpid(),fpid);

    int i = 1;
    pid_t fpid=fork();
    if(fpid= =0)
        printf("%d child %4d %4d %4d/n",i,getppid(),getpid(),fpid);
    else
        printf("%d parent %4d %4d %4d/n",i,getppid(),getpid(),fpid);


    return 0;
}

没有异议把,把***** *****间的内容展开,就是下面的语句。我们可以看到,代码段的大致结构就成了这样子

i = 0

......

i = 0
fork()#记为fork1
print()#记为print1

......

i = 1
fork()#记为fork2
print()#记为print2

return

我们只写出重要部分,就像上面那样,因为for展开后有两块内容,两块的代码是相同的,所以我们分别为两块的fork和print标上号。

程序分析:

首先主进程开始执行记为P,执行到fork1,主进程P创建子进程,记为P1。主进程挂起,子进程P1继续执行

fork1的下条语句为print1,所以子进程执行print1。继续执行,到fork2。进程P1创建子进程,记为P2。P1挂起,P2继续执行。

fork2的下条语句print2,所以进程P2执行print2,之后return。致此 P2进程结束。则返回P1,(P1在fork2处挂起,现在继续执行)

下一条语句print2,P1执行print2,return。P1结束,返回主进程P(P在fork1挂起,现在继续执行)

先是print1,之后fork2,P创建子进程,记为P3。主进程挂起,P3执行。

P3执行print2,return,P3结束。返回P,

P执行print2,return。致此主进程结束。

 

大致图是这样的:

绿色纯手绘,丑的一批......

 

懂了吗?现在在看下结果,想着过程,看看能不能输出了:

 

 

 

 

 

 

没想到循环两次,背后的进程搞了这么多事吧。

 

写完了,感谢观看。

猜你喜欢

转载自blog.csdn.net/qq_41500251/article/details/84792398