IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 系统运维 -> 【Linux】进程详解(一) -> 正文阅读

[系统运维]【Linux】进程详解(一)

【Linux】进程详解(一)

前言

在上一讲 认识冯诺依曼体系&&初识LinuxOS 中已经学到了计算机的整体的体系结构和操作系统在计算机中起到的重要作用,今天我们就来学习一下,在如此复杂的计算机体系中,操作系统是如何有效的组织和管理计算机的?


操作系统如何进行有效管理?

在讲操作系统如何是管理的之前,可以先说一说什么叫做管理?

我们在学校中常说辅导员管理整个年级,那么辅导员管理的整个年级到底指的是啥?管理其实本质上是在管理一些有效的数据。 辅导员是在管理整个年级中反馈给辅导员的数据信息,而每一个班级反馈的数据信息形成了整个年级的数据信息,每一个班级中的每一个学生反馈的数据信息形成的数据信息组成了整个班级的信息。在辅导员收到这些数据信息的时候,就会进行一些措施去执行。这样就形成了整个管理的过程。

那说了这么多,我们再回来说说操作系统是如何进行管理的。同样操作系统也是通过收到来自计算机中各种数据来进行管理的。那么这些数据从哪里来的呢?这就是操作系统中最重要的概念了,即抽象出各种概念,通过结构体的形式描述出来,最后通过数据结构的形式组织起来。

据的来源就是操作系统中的众多结构体,为了描述出结构体,操作系统给结构体定义了很多的属性,这样结构体就可以返回很多操作系统所需要的有效信息了。如此多的结构体返回有效的数据信息的时候操作系统还是很头疼,所以此时数据结构就起到了至关重要的作用,即将这些结构体通过不同的数据结构组织起来,通过使用不同的数据结构组织起来的数据所实现的效果是不一样的,可以通过具体的要求来进行选择,如如果想要快速找到元素,可以使用树这种结构。如果想要进行快速地插入元素可以使用链表的形式…

有六个字总结操作系统是如何进行管理的:先描述,再组织。

初识进程和PCB

1.基本概念

操作系统中有很多抽象出的概念,而进程就是其中最深刻的概念之一。

进程的定义:一个执行中程序的实例,即一个正在执行的程序。

如果站在内核的角度来看:进程是分配系统资源的单位。

2.描述进程-进程控制块(PCB)

前面说了一个抽象的概念需要一个具体的结构体来进行描述的。进程中的信息就被放在了一个叫做进程控制块(PCB)的结构体中。

在不同的操作系统下进程控制块的名称不同(就好像不同地方的人称呼某一个东西会有不同的叫法一样),在Linux操作系统中PCB的具体名称是:task_struct

当一个程序被加载到内存中要开始执行的时候,操作系统同时会给该进程分配一个PCB,在Linux中就是task_struct这里面包含了所有关于进程的数据信息。所以CPU对task_struct进行管理就相当于对进程进行管理。

3.1 task_struct

task_struct是Linux内核的一种数据结构,它会被装载到RAM里并包含进程的信息。每个进程都把它的信息放在task_struct这个数据结构里面,而task_struct包含以下内容:

标识符:与进程相关的唯一标识符,用来区别其他进程

状态:进程会有不同的状态,如运行,停止等等

优先级:相对于其他进程的优先顺序

程序计数器:程序中即将执行的下一条指令的地址

内存指针:包括程序代码和进程相关数据的是很

上下文信息:进程执行时CPU的寄存器中的数据

IO状态信息: 包括显示的I/O请求,分配给进程的I/O设备和正在被进程使用的文件列表。

记账信息:可能包括处理器时间总和,使用的时钟总数,时间限制,记账号等

1.进程标示符: 描述本进程的唯一标示符,用来区别其他进程。

也就是进程的PID,PID是操作系统中唯一标识的进程号。

有两个获得进程PID的方式:

1.1.可以使用ps aux查看进程的信息。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SGfXCS7n-1631696272535)(D:\github\gitee\linux-study\【Linux】进程详解(收藏起来慢慢看).assets\1631435354513.png)]

1.2.可以使用系统接口得到进程PID和父进程的PPID

#include <stdio.h>    
#include <unistd.h>    

int main() {    
    printf("pid=%d, ppid=%d\n", getpid(), getppid()); // 进程号和父进程号
    return 0;    
}

2.进程状态

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Mk4TDLxF-1631696272538)(D:\github\gitee\linux-study\【Linux】进程详解(收藏起来慢慢看).assets\1631435658273.png)]

后面会详细讲解每一个状态,这里先大概地介绍一下。

3.优先级

因为CPU资源有限,而进程却有很多个,所以需要优先级这个属性去决定了进程拿到资源的顺序。

4. 程序计数器: 程序中即将被执行的下一条指令的地址。

CPU有三个工作:取指令,分析指令和执行指令。CPU中的指令寄存器每一次都会保存下一条指令的地址,以此来进行指令判断。

5. 内存指针: 包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-j9B5MA2C-1631696272543)(D:\github\gitee\linux-study\【Linux】进程详解(收藏起来慢慢看).assets\1631436546514.png)]

6.上下文数据

通常操作系统内核使用一种叫做 「上下文切换」 的方式来实现控制流。

实行这种机制是因为CPU只有一套寄存器,所有只能有将一个进程的存储数据放入寄存器中计算,从而形成了上下文数据。但是同时有多个进程的时候,操作系统为了使得CPU的利用率最高,所以会让进程之间来回的切换,一般进程切换有两种情况。

6.1 第一种情况:我们称两个执行流在执行的时间上与另一个执行流有重叠的部分,就称这个两个执行流在**「并发的运行」。一个进程在和其他的进程轮流轮流运行成为「多任务」**。一个进程执行它的控制流的那一段时间叫做 「时间片」 。简单来说,每一个进程都会有最多执行的时间限制,如果执行时间超过了时间片,就会自动的退出执行。

6.2 第二种情况:当操作系统内核,发现一个优先级更高的进程的时候,该优先级更高的进程就会「抢占」当前进程的位置,然后执行优先级更高的进程。等到该进程执行完后,在执行被 「抢占」 的进程。这种决策方式叫做 「调度」

以上两种情况,都会使得进程莫名其妙的退出CPU的执行,但是下次CPU还想接着上一次执行的地方继续执行那个莫名其妙退出的进程,所以就需要在进程退出之前,在task_struct中保留下上一次执行的数据,方便下一次再被执行。

7. I/ O状态信息: 包括显示的I/O请求,分配给进程的I/ O设备和被进程使用的文件列表。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-z1cOwOZS-1631696272544)(D:\github\gitee\linux-study\【Linux】进程详解(收藏起来慢慢看).assets\1631438351577.png)]

3.组织进程

可以在内核源代码里找到task_struct。所有运行在系统里的进程都以task_struct双链表链表的形式存在内核里。如果在情况复杂的情况下,双链表中的节点也有可能存在在其他的数据结构中,例如队列等。

4.查看进程

查看进程有三种方式:

第一种方式:在/proc这个目录下保存着所有进程的信息

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CrShG99y-1631696272545)(D:\github\gitee\linux-study\【Linux】进程详解(收藏起来慢慢看).assets\1631439876116.png)]

第二种方式:可以使用命令

ps aux # 查看系统中所有的进程信息
ps axj # 可以查看进程的父进程号

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tL5MwdbR-1631696272546)(D:\github\gitee\linux-study\【Linux】进程详解(收藏起来慢慢看).assets\1631439811463.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HI5OgitI-1631696272547)(D:\github\gitee\linux-study\【Linux】进程详解(收藏起来慢慢看).assets\1631439837066.png)]

第三种方式:使用top命令

top # 动态的查看进程的信息,其中的信息默认3秒回更新

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BNYntwWq-1631696272547)(D:\github\gitee\linux-study\【Linux】进程详解(收藏起来慢慢看).assets\1631439772841.png)]

5.创建进程-fork()

创建进程有两种创建方式:

1.使用./运行某一个可执行程序,这种是最常见的方式

2.使用系统调用接口创建进程,即使用fork()

当时用fork()函数之后,就在原来的进程中创建了一个子进程,在fork()之前的代码只被父进程执行,在fork()之后的代码有父子进程一起执行。

创建的子进程和父进程几乎一模一样,子进程和父进程的共享地址空间,子进程可以或者父进程中所有的文件,只有PID是父子进程最大的不同。

利用fork创建一个进程来举个例子:

#include <stdio.h>    
#include <unistd.h>    

int main()    
{
    pid_t pid = fork();
    if (pid < 0) {
        printf("error");
    }
    if (pid == 0) {    
        printf("i am a child process\n"); // 输出 
    } else {    
        printf("i am a father process\n"); // 输出
    }
    return 0;    
}

如果fork成功创建了一个进程,那么上面的代码就会输出i am a child process i am a father process这两句话。

这里面有很多有意思的点:

  • fork函数调用一次,返回两次。

    上面的代码是如何实现执行两个不同的分支语句的呢?其实是因为fork函数会返回两个返回值,一个是子进程会返回0,一个是父进程会返回子进程的PID。所以会同时进程两个分支语句中。

  • 并发执行

    父子进程是两个并发运行的独立程序。前面说过并发,就是两个执行流在执行的时间上有重叠的部分。也就是说父子进程谁先被调度是不能确定的。

  • 相同但是独立的地址空间

    两个进程其实地址空间是一样的,但是它们都有自己私有的地址空间,所以父子进程的运行都是独立的,一个进程中的内存不会影响另一个进程中的内存。

  • 共享文件

    子进程继承了父进程所有打开的文件,所以父进程调用fork的时候,stdout文件呢是打开的,所以子进程中执行的内容也可以输出到屏幕上

下面这个例子有一些的复杂,你可以检验一下自己有没有真正理解fork()

#include <stdio.h>    
#include <unistd.h>    
#include <sys/types.h>

int main()    
{    
    fork();    
    fork();    
    printf("hello\n");    
    exit(0);
    return 0;    
}

上面应该是输出了4个hello,下面这张图就可以解释

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gbwD3uS4-1631696272548)(D:\github\gitee\linux-study\【Linux】进程详解(收藏起来慢慢看).assets\1631441949286.png)]

6.进程状态详解

6.1 查看进程的状态

可以使用ps aux或者ps axj命令查看进程的状态。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-h35JJZKb-1631696272548)(D:\github\gitee\linux-study\【Linux】进程详解(收藏起来慢慢看).assets\1631449849699.png)]

6.2 不同的进程状态

进程有很多的不同的状态,在kernel源代码中是这样定义的

static const char * const task_state_array[] = {
"R (running)", /* 0 */
"S (sleeping)", /* 1 */
"D (disk sleep)", /* 2 */
"T (stopped)", /* 4 */
"t (tracing stop)", /* 8 */
"X (dead)", /* 16 */
"Z (zombie)", /* 32 */
};
  • R运行状态

处于R状态的进程有的在cpu中执行,但是很多都是在运行队列中等待运行,也就是该进程允许被调度。

模拟实现

可以运行任意一个可运行的程序,即可出现R状态。

  • S 睡眠状态

这种状态是一种浅度睡眠,此时的进程是在被阻塞的状态中,等待着条件的满足过后进程才可以运行。在这种状态下可以被信号激活,也可以被信号杀死。

模拟实现:

可以使用sleep() 系统调用接口使得一个进程睡眠

#include <stdio.h>
int main()
{
    while (1) 
    {
     	printf("hello world\n");
        sleep(100); // 睡眠100秒
    }
    return 0;
}
  • D磁盘休眠状态

这种状态是一种深度休眠的状态,在这种状态下即使是操作系统发送信号也不可以杀死进程,只能等待进程自动唤醒才可以。

模拟实现:

这种情况没法模拟,一般都是一个进程正在对IO这样的外设写入或者读取的时候,为了防止操作系统不小心杀掉这个进程,所以特地创建出一个状态保护这种进程。

  • T停止状态

可以通过发送 SIGSTOP 信号给进程来停止(T)进程。这个被暂停的进程可以通过发送 SIGCONT 信号让进程继续运行。

模拟实现:

可以使用信号

kill -SIGSTOP PID // 停止进程
kill -SIGSONT PID // 继续进程

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RAkfh1FC-1631696272549)(D:\github\gitee\linux-study\【Linux】进程详解(收藏起来慢慢看).assets\1631450717981.png)]

  • X死亡状态

进程停止执行,进程不能投入运行。通常这种状态发生在接受到SIGSTOP、SIGTSTP、SIGTTIN、SIGOUT等信号的时候。

模拟实现:

可以使用kill -9 PID即可杀死一个进程

  • z僵死状态

后面会详细讲解

6.3 孤儿进程

如果父进程比子进程先退出,那么此时子进程就叫做孤儿进程。而操作系统不会让这个子进程孤苦伶仃的运行在操作系统中,所以此时孤儿进程会被init进程(也就是1号进程,即所有进程的祖先)领养,从此以后孤儿进程的状态和最后的PCB空间释放都是由init进程负责了。

模拟实现:

模拟实现让父进程比子进程提前退出。

#include <stdio.h>    
#include <stdlib.h>    
#include <unistd.h>    
    
int main()    
{    
    pid_t pid = fork();    
    
    if (pid == 0) {    // 子进程一直执行
        while (1) {    
            printf("I am a child, pid=%d, ppid=%d\n", getpid(), getppid());    
            sleep(1);    
        }    
    } else {    
        int count = 5;    // 父进程执行5次
        while (count --) {    
            printf("I am a father, pid=%d, ppid=%d\n", getpid(), getppid());    
            sleep(1);
        }    
        exit(1);    
    }    

    return 0;    
}

使用shell脚本监控进程信息

# 每隔一秒显示进程的信息
while :; do ps axj | head -1 && ps axj | grep a.out | grep -v grep; sleep 1; done

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vOGkEvtB-1631696272549)(D:\github\gitee\linux-study\【Linux】进程详解(收藏起来慢慢看).assets\1631452171146.png)]

父进程退出后,自己子进程被1号init进程收养。

6.4 僵尸进程

1.为什么会出现僵尸进程?

前面说过进程的作用是为了给操作系统提供信息的,所以在进程调用结束之后,应该将该进程完成的任务情况汇报(eixt code)给操作系统,但是进程在执行完之后已经结束了,所以此时进程的状态就是僵尸状态。

2.僵尸进程的概念

僵尸进程:即进程已经结束了,但是父进程没有使用wait()系统调用,此时父进程不能读取到子进程退出返回的信息,此时就该进程就进入僵死状态。

3.僵尸进程的危害

进程已经结束了,但是进程控制块PCB却还是没有被释放,这时就会浪费这一块资源空间。所以会导致操作系统的内存泄漏。

4.如何消灭僵尸进程?

僵死状态需要父进程发出wait()系统调用终止进程,如果父进程不终止进程,那么此时要消灭僵尸进程只能通过找到僵尸进程的父进程,然后kill掉这个父进程,然后僵尸进程就会成为孤儿进程,此时由init进程领养这个进程然后杀死这个僵尸进程。

模拟实现:

模拟实现让子进程比父进程提前退出。

#include <stdio.h>    
#include <stdlib.h>    
#include <unistd.h>    
    
int main()    
{    
    pid_t pid = fork();    

    if (pid == 0) {    
        int count = 5;    // 子进程运行5次
        while (count --) {    
            printf("I am a child, pid=%d, ppid=%d\n", getpid(), getppid());    
            sleep(1);    
        }    
        exit(0);    
    } else {              // 父进程一直运行
        while (1) {    
            printf("I am a father, pid=%d, ppid=%d\n", getpid(), getppid());    
            sleep(1);    
        }    
    }
    return 0;    
}

可以使用shell脚本监控

# 每隔一秒就查看进程的信息
while :; do ps aux | head -1 && ps aux | grep a.out | grep -v grep; sleep 1; done

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-i51e4oq8-1631696272550)(D:\github\gitee\linux-study\【Linux】进程详解(收藏起来慢慢看).assets\1631451718004.png)]

子进程执行了5次之后,编程僵尸状态

6.5 进程状态的转化

粗糙版本:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-D8dFv32e-1631696272550)(D:\github\gitee\linux-study\【Linux】进程详解(收藏起来慢慢看).assets\1631447042781.png)]

详细版本:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pO44sS0x-1631696272550)(D:\github\gitee\linux-study\【Linux】进程详解(收藏起来慢慢看).assets\1631452861003.png)]

  系统运维 最新文章
配置小型公司网络WLAN基本业务(AC通过三层
如何在交付运维过程中建立风险底线意识,提
快速传输大文件,怎么通过网络传大文件给对
从游戏服务端角度分析移动同步(状态同步)
MySQL使用MyCat实现分库分表
如何用DWDM射频光纤技术实现200公里外的站点
国内顺畅下载k8s.gcr.io的镜像
自动化测试appium
ctfshow ssrf
Linux操作系统学习之实用指令(Centos7/8均
上一篇文章      下一篇文章      查看所有文章
加:2021-09-18 10:39:18  更:2021-09-18 10:40:02 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/15 15:56:15-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码