• [Linux入门]---进程的概念


    1.进程的概念

    在我们的电脑开机的时候,操作系统会被加载到内存中,点击多个应用进行时,那么将有多个应用的进程会被加载到内存中的操作系统上。说明一个操作系统不仅仅可以运行一个进程,而且可以运行多个进程!既然,有多个进程了,那么就需要将这些进程有条不紊地管理起来,那操作系统是如何管理进程的呢?先描述,在组织:任何一个进程加载到内存中形成真正的进程时,操作系统要创建描述进程的结构体对象;接下来便是将这些进程信息块组织起来,使用双链表的数据结构进行管理!
    在这里插入图片描述
    在很多人的理解中,一个加载到内存中的程序叫做进程,或者正在运行的程序叫做进程,其实这些都是片面的理解!假如你被复旦大学录取了,难道就可以说你是复旦大学的学生了吗?当然不是的!你被录取了表明你的档案信息已经被该大学收录了,而要想真正成为该大学的学生!到开学的时候,你要拿着录取通知书到复旦大学入学报到,这个时候你才真正成为了复旦大学的学生!同理,进程应该包括描述进程的信息和需要处理的代码和数据!

    ①描述进程-PCB

    • 进程信息被放在一个叫做进程控制块的数据结构中,可以理解为进程属性的集合
    • 进程=内核PCB数据结构对象+自己的数据和代码

    ②task_struct-PCB的一种

    • Linux中描述进程的结构体叫做task_struct
    • 课本上称之为PCB(process control block)Linux操作系统下的PCBtask_struct

    ③task_ struct内容分类

    • 标示符:描述本进程的唯一标示符,用来区别其他进程。
    • 状态:任务状态,退出代码,退出信号等。
    • 优先级:相对于其他进程的优先级。
    • 程序计数器:程序中即将被执行的下一条指令的地址。
    • 内存指针:包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针
    • 上下文数据:进程执行时处理器的寄存器中的数据[休学例子,要加图CPU,寄存器]。
    • I/O状态信息:包括显示的I/O请求,分配给进程的I/O设备和被进程使用的文件列表。
    • 记账信息:可能包括处理器时间总和,使用的时钟数总和,时间限制,记账号等。
    • 其他信息

    2.查看进程

    • PID为子进程标志,相当于学生的学号。
    • 进程的信息可以通过 /proc 系统文件夹查看
      输入ls /proc/指令,可以看到不同PID的文件
      在这里插入图片描述+ 大多数进程信息同样可以使用top和ps这些用户级工具来获取

    创建myprocess.c文件,如下:

    #include                                                                                                                                                    
    #include    
    int main()    
    {    
        while(1)    
        {    
            printf("我是一个进程....\n");    
            sleep(1);    
        }    
        return 0;    
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    创建自动化构建文件Makefile文件,如下:

    myprocess:myprocess.c                                                                                                                                                
        gcc -o $@ $^    
    .PHONY:clean    
    clean:    
        rm -rf myprocess   
    
    • 1
    • 2
    • 3
    • 4
    • 5

    Xshell代码运行的结果如下:
    在这里插入图片描述
    在另一个窗口输入如下指令:

    $ ps ajx | head &&ps ajx | grep myprocess//获取含myprocess的进程PID
    
    • 1

    指令运行的结果如下:
    在这里插入图片描述

    ./myprocess为正在运行的可执行程序,grep --color=auto myprocess为刚才我们运行的获取PID指令中包含了myprocess,说明输入的指令在系统中变成了进程运行

    可以使用如下指令过滤掉grep myprocess进程:

    $ ps ajx | head -1 && ps ajx | grep myprocess | grep -v grep
    
    • 1

    关闭进程的指令:

    $ ps -9 进程的PID//杀进程
    
    • 1

    查看进程文件:
    在这里插入图片描述

    $ ls /proc/4356/ -l //获取PID为4356的进程文件详细信息
    
    • 1

    进程文件详细信息部分截图如下:
    在这里插入图片描述

    3.通过系统调用获取进程表示符

    在前面操作系统的学习中,操作系统通过数据结构将描述的进程信息组织起来管理,我们作为普通用户很难和操作系统打交道,所以不能直接获取当前进程PID;但是可以通过操作系统上层封装的系统调用接口,获取PCB结构描述的信息,接下来让我们认识两个系统调用接口:

    getpid():获取子进程PID的信息。
    getppid():获取父进程PPID的信息。
    在这里插入图片描述

    创建proc.c文件,写入如下代码:

    #include    
    #include    
    #include    
    int main()    
    {    
      printf("我是一个子进程,pid为:%d\n",getpid());    
      printf("我是一个父进程,ppid为:%d\n",getppid());    
        return 0;                                                                                             
    }  
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    代码运行的结果为:
    在这里插入图片描述

    重新运行./proc文件,其子进程一直在变(比如某学生高考失利进入复旦大学时,他可以获得一个学号,当他第二年复读再次考入复旦大学时,他又获得一个学号,而这两个学号是不同的),而父进程一直都不变。

    $ ps ajx | head -1 && ps ajx | grep 3216 //父进程
    
    • 1

    在这里插入图片描述

    父进程ppidbash命令行进程,给我们输入指令的!

    4.通过系统调用创建进程—fork初识

    使用man指令,查找fork函数的信息
    在这里插入图片描述
    解释:

    fork函数创建一个新的进程,让当前的父进程返回当前的子进程,创建新的子进程返回0。

    eg1:

    #include    
    #include    
    #include    
         
    int main()    
    {    
        printf("我是使用fork()之前的语句\n");    
        pid_t ret=fork();    
        printf("我是使用fork()之后的语句\n");    
        return 0;    
    }  
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    代码运行的结果为:
    在这里插入图片描述

    可以发现fork()函数创建新的子进程,当前的进程执行了printf语句,创建的进程执行了一遍的printf语句,所以fork()后面的语句被执行两遍。

    eg2:

    #include    
    #include    
    #include        
    int main()    
    {    
       printf("begin: 我是一个进程,pid:%d,ppid:%d\n",getpid(),getppid());    
       pid_t ret=fork();    
       if(ret==0)    
       {    
           while(1)    
           {    
               printf("我是子进程,pid:%d,ppid:%d\n",getpid(),getppid());    
               sleep(1);                                                                                                                                                
           }    
       }    
       else if(ret>0)    
       {    
           while(1)    
           {    
               printf("我是父进程,pid:%d,ppid:%d\n",getpid(),getpid());    
               sleep(1);    
           }    
       }    
       return 0;    
    }    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    代码运行的结果为:
    在这里插入图片描述
    指令:

    while :; do ps ajx | head -1 ; ps ajx | grep proc |grep -v grep;sleep 1;done
    
    • 1

    在另一个窗口执行该指令:
    在这里插入图片描述

    使用fork函数创建新的进程,使用if语句,根据父进程返回当前的子进程(返回的值大于0),旧的子进程返回0,实现分流执行不同的代码。

    问题1:为什么fork函数返回子进程,父进程返回当前的子进程?

    一般而言fork函数之后的代码,父子进程共享;返回不同的值,让不同的执行流,执行不同的代码!

    问题2:fork函数如何做到返回两次?

    ①创建子进程PCB;②填充PCB的内容;③让父子进程共享同一份代码;④父子进程的task_struct,可以被CPU调用…最后执行return语句返回,return语句之前fork函数的主要工作已经完成了(即创建新的子进程),所以return语句为父子进程共享的语句,所以父进程返回一次,子进程返回一次。

    问题3:fork干了什么事?

    进程之间是不会相互影响,相互独立:①fork创建了子进程,子进程依据父进程为模板PCB模板创建自己的PCB,指向父进程的代码;②那指向的数据是否相同呢?子进程和父进程刚开始指向的数据相同,当操作系统检测到子进程要修改数据时会开空间,会发生写时拷贝,但不是把父进程的数据全部拷贝,子进程只会拷贝自己能使用的数据,避免造成资源浪费。
    在这里插入图片描述

    问题4:一个变量怎么会有不同的内容?

    我们已经知道了fork函数可以返回两次,并且同一个变量可以接收两次不同的值(即访问不同的内存)!那是怎么做到一块地址空间是怎么接收呢?我们后面再进行学习🎉🎉🎉

    问题5:如果父子进程被创建好,fork往后,哪个进程被先运行呢?

    哪个进程先运行,是由调度器(挑选进程)决定的,我们是不能确定的!

    问题6:执行不同命令时子进程不同,但这些子进程的父进程都为bash进程,为什么呢?

    bash内部也是通过fork函数创建子进程的,bash进程执行接收新的命令、打印出命令行提示符等任务,而bash创建的子进程执行解释新的命令,所以我们当前运行的所有的命令都是bash的进程。

  • 相关阅读:
    matlab如何实现任意长序列所有排列方式
    代码混淆与反混淆学习-第二弹
    React源码之Fiber架构
    学习在C++中使用位运算符做“int”和“4个char”之间的转换
    ssh连接服务器,实现任务挂载到后台中,避免断网导致的任务中断
    vue3使用腾讯地图选择地点
    ESP8266-Arduino编程实例-L9110直流电机风扇传感器模块
    【计算机视觉】目标跟踪| 光流算法详细介绍|附代码
    百度网盘vip免费领取一天活动链接2024最新
    开源一款基于EG1151的大功率同步整流可调升降压电源模块(支持TypeC PD快充输入)
  • 原文地址:https://blog.csdn.net/m0_74288306/article/details/132384854