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

news2025/2/23 12:39:06

冯诺依曼体系结构

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

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

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

中央处理器(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.coloradmin.cn/o/1529355.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SaaS模式全套云HIS源码包含EMR、LIS

目录 系统简介 一、云HIS分系统功能 【医疗业务子系统】 【电子病历系统】 【经济管理子系统】 【药物管理子系统】 【统计报表子系统】 【系统管理子系统】 二、运维运营分系统 三、互联互通分系统 系统简介 SaaS模式Java版云HIS系统源码&#xff0c;在公立二甲医院…

使用Vuex构建网络打靶成绩管理系统及其测试页面平台思路

使用Vuex构建网络打靶成绩管理系统及其测试页面平台 一、引言 在现代Web开发中&#xff0c;前端框架和状态管理库已经成为构建复杂应用的关键工具。Vue.js作为一个轻量级且易于上手的前端框架&#xff0c;结合Vuex这个专门为Vue.js设计的状态管理库&#xff0c;可以让我们更加…

string类型的使用以及编码方式

Redis 中所有的键的类型都是字符串类型&#xff0c;⼀个字符串的最⼤值不能超过 512 MB。 由于 Redis 内部存储字符串完全是按照⼆进制流的形式保存的&#xff0c;所以 Redis 是不处理字符集编码问题的&#xff0c;客⼾端传⼊的命令中使⽤的是什么字符集编码&#xff0c;就存储…

【EDSR】《Enhanced Deep Residual Networks for Single Image Super-Resolution》

CVPR workshops-2017 code&#xff1a; https://github.com/limbee/NTIRE2017/tree/masterhttps://github.com/sanghyun-son/EDSR-PyTorch 文章目录 1 Background and Motivation2 Related Work3 Advantages / Contributions4 Method4.1 Residual blocks4.2 Single-scale mod…

Hive 数据迁移与备份

迁移类型 同时迁移表及其数据&#xff08;使用import和export&#xff09; 迁移步骤 将表和数据从 Hive 导出到 HDFS将表和数据从 HDFS 导出到本地服务器将表和数据从本地服务器复制到目标服务器将表和数据从目标服务器上传到目标 HDFS将表和数据从目标 HDFS 上传到目标 Hiv…

王老吉药业开拓数字经济“新蓝海”,成立数字经济研究所,科技赋能新品压片糖

3月12日&#xff0c;广州王老吉药业股份有限公司&#xff08;以下简称“王老吉药业”&#xff09;召开第十一届312感恩活动新闻发布会&#xff0c;宣告王老吉数字经济研究所成立&#xff0c;并发布王老吉压片糖新品。一系列重要重要举措&#xff0c;无一不标志着王老吉药业正以…

基于python的变配电室运行状态评估与预警系统flask-django-nodejs-php

近年来,随着我国工业化、城镇化步伐的不断加快&#xff0c;城市配电网络取得令人瞩目的发展成果。变配电室是供配电系统的核心&#xff0c;在供配电系统中占有特殊的重要地位[1]。变配电室电气设备运行状态和环境信息缺乏必要的监测评估预警手段&#xff0c;如有一日遭遇突发情…

Sentinel(熔断规则)

慢调用比例 慢调用比例( SLOM_REQUEST_RATTo ):选择以慢调用比例作为阈值&#xff0c;需要设置允许的慢调用RT(即最大的响应时间)&#xff0c;请求的响应时间大于该值则统计为慢调用。当单位统计时长(statIntervalMs&#xff09;内请求数目大于设置的最小请求数目&#xff0c;…

【web前端】CSS语法

CSS语法 1. CSS语法格式 通常情况下语法格式如下: 选择器{属性名:属性值;属性名:属性值;属性名:属性值;... }2. CSS添加方式 2.1 行内样式 直接将样式写在本行的标签内。 <h1><p style"font-size: 48px; color:red;";>行内样式测试</p></…

es 集群安全认证

参考文档&#xff1a;Configure security for the Elastic Stack | Elasticsearch Guide [7.17] | Elastic ES敏感信息泄露的原因 Elasticsearch在默认安装后&#xff0c;不提供任何形式的安全防护不合理的配置导致公网可以访问ES集群。比如在elasticsearch.yml文件中,server…

【Godot4.2】2D导航03 - NavigationRegion2D及其使用方法

概述 Godot4.0改进了2D部分的导航&#xff0c;NavigationRegion2D基本可以看做是3.X的Navigation2D的进化版本。 它的基本用法就是先绘制navigation_polygon&#xff0c;也就是导航网格&#xff0c;或者直白点就是“可通行区域”。然后将带有碰撞形状和NavigationAgent2D子节点…

蓝桥杯前端Web赛道-输入搜索联想

蓝桥杯前端Web赛道-输入搜索联想 题目链接&#xff1a;1.输入搜索联想 - 蓝桥云课 (lanqiao.cn) 题目要求&#xff1a; 题目中还包含effect.gif 更详细的说明了需求 那么观察这道题需要做两件事情 把表头的每一个字母进行大写进行模糊查询 这里我们会用到几个js函数&#…

如何用VR全景讲述产品、企业的故事

请记住&#xff0c;你的全景不只是全景&#xff01;使用VR全景来讲述企业或产品的故事是一种创新且互动性强的方法&#xff0c;可以吸引观众并提供沉浸式体验。 运用720云漫游编辑工具的导览功能和数字人功能&#xff0c;您可以创造一个既视觉吸引又能情感连结的故事。 1. 规划…

OpenCV学习笔记(十)——利用腐蚀和膨胀进行梯度计算以及礼帽和黑帽

梯度计算 在OpenCV中&#xff0c;梯度计算是图像处理中的一个基本操作&#xff0c;用于分析图像中像素值的变化速率的方向&#xff0c;其中梯度的方向是函数变化最快的方向&#xff0c;因此在图像中&#xff0c;沿着梯度方向可以找到灰度值变化最大的区域&#xff0c;这通常是…

golang面试题总结

零、go与其他语言 0、什么是面向对象 在了解 Go 语言是不是面向对象&#xff08;简称&#xff1a;OOP&#xff09; 之前&#xff0c;我们必须先知道 OOP 是啥&#xff0c;得先给他 “下定义” 根据 Wikipedia 的定义&#xff0c;我们梳理出 OOP 的几个基本认知&#xff1a; …

Springboot+Redis:实现缓存 减少对数据库的压力

&#x1f389;&#x1f389;欢迎光临&#xff0c;终于等到你啦&#x1f389;&#x1f389; &#x1f3c5;我是苏泽&#xff0c;一位对技术充满热情的探索者和分享者。&#x1f680;&#x1f680; &#x1f31f;持续更新的专栏Redis实战与进阶 本专栏讲解Redis从原理到实践 …

期权波动率是什么?怎么计算?

期权波动率 历史波动率&#xff1a;基于历史行情计算出来的历史波动率 我们现在站在现实时点B回顾过去&#xff0c;从A到B这段时间的历史行情我们是知道的&#xff0c;但是基于过去一段时间&#xff0c;标的价格的历史数据计算出来的波动率&#xff0c;就是历史波动率&#x…

c++类和对象(中)类的6个默认成员函数及const成员函数

&#x1fa90;&#x1fa90;&#x1fa90;欢迎来到程序员餐厅&#x1f4ab;&#x1f4ab;&#x1f4ab; 今日主菜&#xff1a;类和对象 主厨&#xff1a;邪王真眼 所属专栏&#xff1a;c专栏 主厨的主页&#xff1a;Chef‘s blog 前言&#xff1a; 咱们之前也是…

字符分类函数

字符分类函数 文章目录 前言1. 字符分类函数1.2.使用例子 2.2.字符操作函数 前言 在编程的过程中&#xff0c;我们经常要处理字符和字符串&#xff0c;为了⽅便操作字符和字符串&#xff0c;C语⾔标准库中提供了⼀系列库函数&#xff0c;接下来我们就学习⼀下这些函数。 1. 字…

搭建项目后台系统基础架构

任务描述 1、了解搭建民航后端框架 2、使用IDEA创建基于SpringBoot、MyBatis、MySQL、Redis的Java项目 3、以原项目为参照搭建项目所涉及到的各个业务和底层服务 4、以原项目为例&#xff0c;具体介绍各个目录情况并参照创建相关文件夹 1、创建项目后端 BigData-KongGuan …