[linux]--关于进程概念(上)

目录

冯诺依曼体系结构

操作系统

概念

设计os的目的

定位

如何理解管理

总结

系统调用和库函数概念

进程

描述进程-pcb

组织进程

查看进程

通过系统调用获取进程标示符

通过系统调用创建进程-fork初识

进程状态

阻塞和挂起

Z(zombie)-僵尸进程


冯诺依曼体系结构

我们常见的计算机,如笔记本。我们不常见的计算机,如服务器,大部分都遵守冯诺依曼体系。

截至目前,我们所认识的计算机,都是有一个个的硬件组件组成

输入单元:包括键盘, 鼠标,扫描仪, 写板等

中央处理器(CPU):含有运算器和控制器等

输出单元:显示器,打印机等 

关于冯诺依曼,必须强调几点:

这里的存储器指的是内存

不考虑缓存情况,这里的CPU能且只能对内存进行读写,不能访问外设(输入或输出设备)

外设(输入或输出设备)要输入或者输出数据,也只能写入内存或者从内存中读取。

一句话,所有设备都只能直接和内存打交道。 

对冯诺依曼的理解,不能停留在概念上,要深入到对软件数据流理解上,假如,从你登录上qq开始和某位朋友聊 天开始,数据的流动过程。从你打开窗口,开始给他发消息,到他收到消息之后的数据流动过程。如果是在qq上发 送文件呢? 

我们通过键盘输入一条信息,然后会传给存储器,存储器再传给运算器和控制器,运算器和控制器统称为cpu,cpu处理完我们的信息,就会发送到qq,通过网络上传给我们的朋友。

操作系统

概念

任何计算机系统都包含一个基本的程序集合,称为操作系统(OS)。笼统的理解,操作系统包括:

内核(进程管理,内存管理,文件管理,驱动管理)

其他程序(例如函数库,shell程序等等)

 这就是进程管理,系统会给每个进程合理安排资源。

设计os的目的

与硬件交互,管理所有的软硬件资源

为用户程序(应用程序)提供一个良好的执行环境

定位

在整个计算机软硬件架构中,操作系统的定位是:一款纯正的“搞管理”的软件

也就是说,在软件方面分配好内存给它们运行,在硬件上合理分配软件去使用内存,还有用户的读写操作。

如何理解管理

这个时候就可以 以我们自己为例子了,大家学生时代基本都是在学校的吧,那么,学校就是一个计算机,校长就是操作系统,我们就是一堆堆的“软件".毕竟铁打的学校流水的学生嘛。

那么学校是每个学期都要安排考试,如果有人考试没过,下学期就要安排补考(大家都逢考必过),校长怎么知道有什么人需要补考呢,这个时候就需要通知我们的辅导员,调取我们的成绩信息,让没过的同学在安排好的教室和时间去补考。这个时候辅导员就是一个小的操作系统,它管理着我们,校长管理着辅导员。我们每个学期都要通过考试证明我们是合格的,当我们各项指标都符合要求时,就可以毕业,而校长和辅导员,就是管理我们的os,他们是管理方,审核我们的毕业要求,而我们就是被管理方,去申请这个系统的资源。

总结

计算机管理硬件 1. 描述起来,用struct结构体 2. 组织起来,用链表或其他高效的数据结构

也就是先描述再组织

系统调用和库函数概念

在开发角度,操作系统对外会表现为一个整体,但是会暴露自己的部分接口,供上层开发使用,这部分 由操作系统提供的接口,叫做系统调用。

系统调用在使用上,功能比较基础,对用户的要求相对也比较高,所以,有心的开发者可以对部分系统 调用进行适度封装,从而形成库,有了库,就很有利于更上层用户或者开发者进行二次开发。

进程

基本概念

课本概念:程序的一个执行实例,正在执行的程序等

内核观点:担当分配系统资源(CPU时间,内存)的实体。

进程不只是包含我们的软件,还有一些后台进程,也就是只要我们电脑开机了,就有进程运行

描述进程-pcb

进程信息被放在一个叫做进程控制块的数据结构中,可以理解为进程属性的集合。

课本上称之为PCB(process control block),Linux操作系统下的PCB是task struct

task_struct-PCB的一种

task_struct 在Linux中描述进程的结构体叫做task_struct。

task_struct是Linux内核的一种数据结构,它会被装载到RAM(内存)里并且包含着进程的信息。

task_ struct内容分类 

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

状态: 任务状态,退出代码,退出信号等。

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

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

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

上下文数据: 进程执行时处理器的寄存器中的数据[休学例子,要加图CPU,寄存器]。

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

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

其他信息 

组织进程

可以在内核源代码里找到它。所有运行在系统里的进程都以task_struct链表的形式存在内核里。

linux的内核源码可以在网上下载学习

查看进程

进程的信息可以通过 /proc 系统文件夹查看

如:要获取PID为1的进程信息,你需要查看 /proc/1 这个文件夹。

大多数进程信息同样可以使用top和ps这些用户级工具来获取 

比如我们现在写一个循环运行的文件

 #include <stdio.h>
 #include <sys/types.h>
 #include <unistd.h>
int main()
 {
    while(1){
        sleep(1);
    }
    return 0;
 }

通过系统调用获取进程标示符

进程id(PID)     父进程id(PPID)

 #include <stdio.h>
 #include <sys/types.h>
 #include <unistd.h>
 
int main()
 {
    printf("pid: %d\n", getpid());
    printf("ppid: %d\n", getppid());
    return 0;
 }

通过系统调用创建进程-fork初识

运行man fork认识fork

fork有两个返回值

父子进程代码共享,数据各自开辟空间,私有一份(采用写时拷贝)

比如说我们现在写一个这样的程序

fork 之后通常要用if进行分流

#include <stdio.h>
 #include <sys/types.h>
 #include <unistd.h>
 
int main()
 {
    int ret = fork();
    if(ret < 0){
        perror("fork");
        return 1;
    }
    else if(ret == 0){ //child
        printf("I am child : %d!, ret: %d\n", getpid(), ret);
    }else{ //father
        printf("I am father : %d!, ret: %d\n", getpid(), ret);
    }
    sleep(1);

return 0;

}

 也就是我们说的fork有两个返回值,当ret为0时,进入分支语句打印一次,当ret不为0,也就是父进程,打印一次。

进程状态

在学习进程状态前,我们先说一下两个关于进程运行的情况

阻塞和挂起


阻塞: 进程因为等待某种条件(资源)就绪,而导致的一种不推进的状态(如我们常说的卡住了一般:页面无法响应、因网络中断下载任务无法继续执行等)。或者说,阻塞就是当前进程不被CPU调度。事实上,进程要通过等待的方式,等某个具体的资源被别人用完或者有了某个资源之后,再使用该资源。

我们知道,操作系统对软硬件做管理,其方式可以被总结为:先描述,再组织 。其中进程被描述为结构体 task_struct ,硬件被管理时同样也是被描述为一个结构体如 struct dev ,每个软硬件对应的结构体中都包括了关于自身的信息。值得注意的是,在每个硬件对应的结构体中还包含了指向进程控制块 PCB(task_struct) 的指针,可以认为该指针指向了一个进程队列的队头,通过该指针可以对某个进程队列进行管理。事实上,一个进程处在运行状态时,可以表示该进程处在CPU进程调度的运行队列中,而当某个进程因等待某种资源而无法继续推进时(通常是等待某种硬件资源,如磁盘、网卡、键盘等),CPU就会将该进程调出当前的运行队列,并调入其所等待资源对应的等待队列中(此时该进程就处在一种 阻塞 状态。换句话说,当某个进程处于阻塞状态时,就表示该进程对应的结构体 task_struct 正在某种被操作系统管理的资源下排队),当该资源准备就绪后,再将该进程调回CPU的运行队列中继续排队运行。

挂起: 当因为等待某种资源就绪,进程对应PCB由运行队列转至资源下的等待队列时,考虑到内存空间紧张,CPU会将因为等待而暂时无法运行的进程对应的代码和数据先由内存转移到磁盘中,此时进程即为挂起状态,等到该进程可以被运行时再将对应的代码和数据由磁盘转移回内存中。

 

看看Linux内核源代码怎么说

为了弄明白正在运行的进程是什么意思,我们需要知道进程的不同状态。一个进程可以有几个状态(在 Linux内核里,进程有时候也叫做任务)。 下面的状态在kernel源代码里定义:(可以在网上找到kernel源码查看)

/*
 * The task state array is a strange "bitmap" of
 * reasons to sleep. Thus "running" is zero, and
 * you can test for combinations of others with
 * simple bit tests.
 */
 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运行状态(running): 并不意味着进程一定在运行中,它表明进程要么是在运行中要么在运行队列
里。
S睡眠状态(sleeping): 意味着进程在等待事件完成(这里的睡眠有时候也叫做可中断睡眠
(interruptible sleep))。
D磁盘休眠状态(Disk sleep)有时候也叫不可中断睡眠状态(uninterruptible sleep),在这个状态的
进程通常会等待IO的结束。
T停止状态(stopped): 可以通过发送 SIGSTOP 信号给进程来停止(T)进程。这个被暂停的进程可
以通过发送 SIGCONT 信号让进程继续运行。
X死亡状态(dead):这个状态只是一个返回状态,你不会在任务列表里看到这个状态。

进程状态查看命令   ps aux / ps axj (二选一)

ps -aux 是以BSD方式显示

a 显示所有用户的进程(show processes for all users)

u 显示用户(display the process's user/owner)

x 显示无控制终端的进程(also show processes not attached to a terminal)

ps-axj

a: 显示所有用户进程

x:显示没有控制终端的进程

j:显示与作业有关的信息(显示的列):会话期ID(SID),进程组ID(PGID),控制终端(TT),终端进程组ID(TRGID)

我们可以写一段代码来观察进程的状态

     #include <stdio.h>
    #include <unistd.h>
   
   int main()
    {
       while(1)
   {   
       printf("我是一个进程\n");
      sleep(1);
  }   
     return 0;
   }

我们可以看到,程序一直在打印这段文字,那么这个时候程序是不是一直在运行呢?

通过命令 ps axj | head -1 && ps axj | grep test | grep -v grep

查看对应的进程状态: 

可以发现:虽然该进程看上去似乎一直在运行,但所显示的进程状态却表示其处于 S+(睡眠状态) 。这是由于在这段死循环中代码中,我们需要显示器资源来显示输出内容(这是一个进程!),显然显示器资源不会一直只供该进程使用,即该进程的运行需要等待显示器资源的就绪,也就是上述所说的睡眠状态。

下面我们又将代码中的输出语句注释,使该死循环中为空,重新编译运行,再次查看相应进程状态,此时可以看到,该进程处于 R+(运行状态) ,这是因为此时该进程不需要等待某个资源就绪,因此其一直处于运行状态。

我们可以看到,我们的状态都有一个+号

这是因为我们的程序都在前台运行,也就是我们的窗口一直在被占用,要解除这种状态我们就ctrl+C,就可以退出程序了。

这个时候就没有这个进程了。

也可以通过kill -9指令去终止(杀死)这个进程

kill- l 查看kill指令

我们可以把程序再跑起来,再用指令去终止

T就是停止状态

这个时候没有+号,就是在后台运行,ctrl+C就终止不了了

我们就通过kill -9杀死进程。

还有一个小t状态,当进程正在被跟踪时,就处于 t 这个特殊状态,其本质上也是一种停止状态。例如调试程序时,触发断点而停止运行,此时对应进程就处在 t 状态。

这里补充一下,如果我们在gdb调试可执行文件时,报这样的错误

No symbol table is loaded.  Use the "file" command.

问题应该是我们编译时没有加上ggdb3,我们再加上编译一遍,基本就可以了

gcc -ggdb3 -o test test.c

 这就是我们运行程序,调试程序,还有打了断点之后,程序所处的不同状态

X(dead)死亡状态


该状态只是一个返回状态(瞬时状态),我们不会在任务列表里看到这个状态。事实上,我们创建进程,无非是想通过进程完成一些任务,而对于任务完成结果,我们可能关心,也可能不关心,这就涉及到一个概念 – 退出码 。所谓 退出码 ,其实就是我们编写的代码中最常见的

main()主函数中的{return 0}(也就是return的那个数字)

我们可以通过 echo $? 命令来查看进程退出码。

  #include <stdio.h>
    #include <unistd.h>
   
   int main()
    {
   int number =3;

  if(number ==3)

    return 1;

sleep(1);
     return 0;
   }

Z(zombie)-僵尸进程

僵死状态(Zombies)是一个比较特殊的状态。

当进程退出并且父进程(使用wait()系统调用,后面讲) 没有读取到子进程退出的返回代码时就会产生僵死(尸)进程 僵死进程会以终止状态保持在进程表中,并且会一直在等待父进程读取退出状态代码。 所以,只要子进程退出,父进程还在运行,但父进程没有读取子进程状态,子进程进入Z状态

我们直接写一个程序看看原理

 #include <stdio.h>
 #include <stdlib.h>
 
int main()
 {
    pid_t id = fork();
    if(id < 0){
        perror("fork");
        return 1;
}
 else if(id > 0){ //parent
 printf("parent[%d] is sleeping...\n", getpid());
 sleep(30);
 }else{
 printf("child[%d] is begin Z...\n", getpid());
 sleep(5);
 exit(EXIT_SUCCESS);
 }
 return 0;
 }

 也就是说,如果我们的父进程进入休眠,没有读取子进程的返回码,子进程一直无法把返回码给到父进程,那么子进程就会进到僵尸状态

僵尸状态有什么危害呢?

进程的退出状态必须被维持下去,因为他要告诉关心它的进程(父进程),你交给我的任务,我办的怎 么样了。可父进程如果一直不读取,那子进程就一直处于Z状态?是的! 维护退出状态本身就是要用数据维护,也属于进程基本信息,

所以保存在task_struct(PCB)中,换句话说

Z状态一直不退出,PCB一直都要维护?是的! 那一个父进程创建了很多子进程,就是不回收,

是不是就会造成内存资源的浪费?是的!因为数据结构 对象本身就要占用内存

想想C中定义一个结构体变量(对象),是要在内存的某个位置进行开辟空 间!

内存泄漏?是的!


有一些知识点我放在下一篇再写啦,一时间写不过来啦哈哈哈哈

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/472264.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

spring整合Sentinel

安装sentinel&#xff1a; 执行命令; java -jar sentinel-dashboard-1.8.6.jar 注:sentinel的默认端口为8080&#xff0c;容易出现tomcat的冲突。 当端口冲突&#xff0c;可以使用该指令修改sentinel的端口 默认账号和密码都为sentinel Springcloud整合sentinel&#xff1a;…

IDA反汇编工具详解之菜单栏和基本操作

文章目录 IDA 菜单栏FileEditJumpSearchViewDebuggerOptionsWindows 反汇编操作名称和命名IDA 中的注释基本代码转换修改exe文件并保存 IDA 菜单栏 File File菜单负责项目工程的管理&#xff0c;操作包括:打开项目、关闭项目、保存项目 Edit Edit菜单负责编辑和管理该项目中的…

matlab 混沌系统李雅普洛夫指数谱相图分岔图和庞加莱界面

1、内容简介 略 65-可以交流、咨询、答疑 2、内容说明 matlab 混沌系统李雅普洛夫指数谱相图分岔图和庞加莱界面 混沌系统李雅普洛夫指数谱相图分岔图和庞加莱界面 李雅普洛夫指数谱、相图、分岔图、庞加莱界面 3、仿真分析 略 4、参考论文 略

队列的算法

数组队列 数组的子集 主要方法addLast( )和removeFirst( ) public interface IQueueDesc<E>{void enqueue(E e);E dequeue();E getFront();int getSize();boolean isEmpty(); }public class QueueMyList<E> implements IQueueDesc<E{MyArray<E> a…

深度学习500问——Chapter03:深度学习基础(4)

文章目录 3.7 预训练与微调&#xff08;fine tuning&#xff09; 3.7.1 为什么无监督预训练可以帮助深度学习 3.7.2 什么是模型微调 fine tuning 3.7.3 微调时候网络参数是否更新 3.7.4 fine-tuning模型的三种状态 3.8 权重偏差和初始化 3.8.1 全都初始化为0 3.8.2 全都初始化为…

无需PS技能!2024年在线UI设计工具推荐,让你快速上手!

随着UI设计行业的蓬勃发展&#xff0c;越来越多的设计师进入UI设计&#xff0c;选择一个方便的UI设计工具尤为重要&#xff01;除了传统的UI设计工具外&#xff0c;在线UI设计工具也受到越来越多设计师的青睐。这种不受时间、地点和计算机配置限制的工作模式真的很令人兴奋。在…

回归预测 | Matlab实现SO-BP蛇算法优化BP神经网络多变量回归预测

回归预测 | Matlab实现SO-BP蛇算法优化BP神经网络多变量回归预测 目录 回归预测 | Matlab实现SO-BP蛇算法优化BP神经网络多变量回归预测预测效果基本描述程序设计参考资料 预测效果 基本描述 1.Matlab实现SO-BP蛇算法优化BP神经网络多变量回归预测&#xff08;完整源码和数据) …

Figure 公司推出首款集成 OpenAI 大模型的自主人形机器人,开启与人类全面对话的新纪元

2024年3月13日&#xff0c;Figure&#xff0c;一家在人工智能机器人领域引领创新的公司&#xff0c;宣布推出了一款革命性的自主人形机器人。这款全新的 demo 机器人不仅标志着商业上可行的自主人形机器人技术的突破&#xff0c;更是通过整合 OpenAI 的先进大模型技术&#xff…

操作符详解(C语言)—算数操作符,移位操作符,位操作符

操作符的分类 算术操作符 移位操作符 位操作符 赋值操作符 单目操作符 关系操作符 逻辑操作符 条件操作符 逗号表达式 下标引用、函数调用和结构成员 算术操作符 - * / %除了 % 操作符之外&#xff0c;其他的几个操作符可以作用于整数和浮点数。对于 / 操作符如果两个…

Unity Mesh简化为Cube mesh

Mesh简化为Cube mesh &#x1f373;食用&#x1f959;子物体独立生成CubeMesh&#x1f96a;合并成一个CubeMesh&#x1f32d;Demo &#x1f373;食用 下载并导入插件&#x1f448;即可在代码中调用。 &#x1f959;子物体独立生成CubeMesh gameObject.ToCubeMesh_Invidual()…

关于 HTTP 协议,你了解多少?

你好&#xff0c;我是 shengjk1&#xff0c;多年大厂经验&#xff0c;努力构建 通俗易懂的、好玩的编程语言教程。 欢迎关注&#xff01;你会有如下收益&#xff1a; 了解大厂经验拥有和大厂相匹配的技术等 希望看什么&#xff0c;评论或者私信告诉我&#xff01; 文章目录 一…

数据分析的具体流程

1.导入 表格导入数据时要注意数据的格式问题非表格导入 可以先将文档放入word中 将换行符&#xff08;^p&#xff09;替换为|||&#xff0c;选择特殊格式中的段落标记 进行全部替换 以每一列最后的数据/平&#xff0c;作为换行的标志 将所整理的信息导入excel,对数据进行分列 选…

数据库系统概论-第5章 数据库完整性

5.1 实体完整性 5.2 参照完整性 5.3 用户定义完整性 5.4 完整性约束命名子句 5.5 域中的完整性限制 5.6 断言 5.7 触发器 5.8 小结

OpenGL+QT实现矢量和影像的叠加绘制

一、QT下OpenGL框架的初始化 OpenGL的介绍我在这里就没有必要介绍了&#xff0c;那OpenGL和QT的结合在这里就有必要先介绍一下&#xff0c;也就是怎么使用QT下的OpenGL框架。要想使用QT下的OpenGL框架&#xff0c;就必须要子类化QGLWidget&#xff0c;然后实现。 void initia…

冶炼金属 (第十四届蓝桥杯省赛C++ B组)详解(二分+推公式)

题目描述&#xff1a; 小蓝有一个神奇的炉子用于将普通金属 O 冶炼成为一种特殊金属 X。 这个炉子有一个称作转换率的属性 V&#xff0c;V 是一个正整数&#xff0c;这意味着消耗 V 个普通金属 O 恰好可以冶炼出一个特殊金属 X&#xff0c;当普通金属 O 的数目不足 V 时&#…

Spring MVC文件下载配置

版权声明 本文原创作者&#xff1a;谷哥的小弟作者博客地址&#xff1a;http://blog.csdn.net/lfdfhl 文件下载 在Spring MVC中通常利用commons-io实现文件下载&#xff0c;示例代码如下&#xff1a; Controller RequestMapping("......") public class DownloadC…

AI大模型-Grok搭建

Grok搭建 硬件要求项目下载Checkpoint下载运行代码 马斯克又搞事情了&#xff0c;正式开源AI大模型Grok-1&#xff0c;免费还可商用&#xff0c;国内AI技术即将迎来重大突破。笔者简单整合了一下&#xff0c;如何搭建Grok-1的思路&#xff0c;供后期自己搭建以及读者学习使用。…

房屋租赁系统|基于JSP技术+ Mysql+Java+ B/S结构的房屋租赁系统设计与实现(可运行源码+数据库+设计文档)

推荐阅读100套最新项目 最新ssmjava项目文档视频演示可运行源码分享 最新jspjava项目文档视频演示可运行源码分享 最新Spring Boot项目文档视频演示可运行源码分享 2024年56套包含java&#xff0c;ssm&#xff0c;springboot的平台设计与实现项目系统开发资源&#xff08;可…

理解数据库习题

1.选择 &#xff08;1&#xff09;现实世界中客观存在并能相互区别的事物称为&#xff08; &#xff09;。 A.实体 B.实体集 C字段 D 记录 &#xff08;2&#xff09;下列实体类型的联系中&#xff0c;属于一对一联系的是&#xff08; &#xff09;A.教研室对教师的所属联系 …

centos 环境部署

一、安装redis 1. 升级 GCC 最直接的解决方式是升级你的 GCC 编译器到支持 C11 标准的版本。CentOS 7 默认的 GCC 版本较旧&#xff0c;可能不支持 _Atomic。你可以通过以下步骤升级 GCC&#xff1a; 启用 CentOS 的 Software Collections (SCL) 仓库&#xff0c;该仓库提供了…
最新文章