Linux进程概念(上)

news2024/11/19 19:41:27

冯诺依曼体系结构
 

这里谈论的体系结构指的是计算机组成

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

计算机,都由一个个的硬件组件组成
 

输入单元:如键盘,话筒,摄像头,usb,鼠标,磁盘/ssd,网卡,显卡等

输出单元:如显示器,打印机,磁盘,网卡,显卡等

这里的存储器指的是内存

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

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

即所有设备都只能直接和内存打交道
 

CPU处理数据的能力非常快,然后是内存,再是各种外设(如磁盘-->永久存储介质)

在数据层面上,当代cpu一般不与外设直接进行交互,原因何在? 

答案是会导致整机效率降低

在数据层面上,cpu优先和内存直接打交道

所以有这么一个问题:

 程序在运行之前,为什么必须要先加载到内存?

因为程序=代码+数据,最终都要cpu来执行处理,cpu需要读取到这些代码和数据,而cpu只和内存有“数据(二进制)”层面的交互,生成的可执行程序.exe本质就是一个文件,在磁盘(外设)中保存,故而需要先加载到内存

操作系统
 

概念(是什么)
 

任何计算机系统都包含一个基本的程序集合,称为操作系统(OS)。笼统的理解,操作系统包括:
1 内核(进程管理,内存管理,文件管理,驱动管理)

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

操作系统就是一款软件,进行软硬件资源管理的软件
 

设计OS的目的(为什么)
 

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

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

操作系统将软硬件资源管理好(手段)给用户提供良好(稳定,高效,安全)的使用环境(目的)

如何理解 "管理"(怎么办)


 

操作系统内部一定会存在大量的数据对象和数据结构,那么如何进行管理呢?

管理的本质是管理数据,就像在学校中,真正的管理者是校长,作为学生并不会与校长有过多的接触,但是我们依然被管理的井井有条,是因为校长手里有学生的信息,如姓名,成绩,在校表现等,所以管理的本质不是管人,而是管理数据

计算机管理硬件:先描述再组织

1. 描述起来,用struct结构体
2. 组织起来,用链表或其他高效的数据结构

 

系统调用和库函数概念
 

在开发角度,操作系统对外会表现为一个整体,但是会暴露自己的部分接口,供上层开发使用,这部分由操作系统提供的接口,叫做系统调用
系统调用在使用上,功能比较基础,对用户的要求相对也比较高,所以,有心的开发者可以对部分系统调用进行适度封装,从而形成库,有了库,就很有利于更上层用户或者开发者进行二次开发
 

一般一个用户向要访问非常底层的os数据或者访问硬件,必须贯穿整个层状结构

如我们日常频繁使用的c语言中的printf,c++中的cout,都是访问了硬件的,那么必定调用系统调用, 则printf,cout必定封装了系统调用

操作系统提供系统调用的部分原因是因为不相信用户(这里的用户指狭义上的用户,即开发者)

因为群众中也有坏人,但是操作系统必须为用户提供服务,就像生活中的银行,它不相信我们但必须给我们提供服务,从而银行会有一个一个的窗口, 以提供服务,那么操作系统提供的接口(系统调用)就类似于银行的窗口

但是即使银行提供了窗口,群众中依然存在不熟悉银行办事流程的人,所以银行也会有一定的工作人员为不熟悉银行业务流程的人提供相关服务,那么对系统调用适度封装后形成的库就类似于为不熟悉银行业务的小白提供服务的工作人员。

 这样库函数vs系统调用,就是上下层关系

进程
 

基本概念
 

课本概念:程序的一个执行实例,正在执行的程序等
内核观点:担当分配系统资源(CPU时间,内存)的实体
 

更深刻的理解是:进程=可执行程序+内核数据结构(--->方便os对进程进行管理)(如PCB)

可执行程序加载到内存之后就是进程了吗?当然不完全是啦!

在可执行程序加载到内存之前,老大哥操作系统就已经加载在内存了

不可否认的是,操作系统内可能会同时存在非常多的“进程”,操作系统要管理它们,就要遵循”先描述再组织“,操作系统使用c语言写的,要管理这些”进程“,就要先描述这些”进程“,怎么描述?

struct结构体!来描述该可执行程序的各种重要属性

 描述进程-PCB

进程信息被放在一个叫做进程控制块的数据结构中,可以理解为进程属性的集合
课本上称之为PCB(process control block),Linux操作系统下的PCB是: task_struct
task_struct是PCB的一种,在Linux中描述进程的结构体叫做task_struct
task_struct是Linux内核的一种数据结构,它会被装载到RAM(内存)里并且包含着进程的信息


task_ struct内容分类
 

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

到目前为止,我们已经初步有了进程的框架,那么task_struct的核心字段有哪些?

进程(任务)对应的标识符:pid(process id)

父进程id:ppid   命令行中父进程一般是命令行解释器

我们运行的所有指令,软件,自己写的程序,最终都是进程

下面抛出一个问题:

在linux中,登录之后,命令行启动的进程,父进程一直不变,那么

这个父进程是谁? 是bash(命令行解释器)我们命令行启动的进程都是bash的子进程

获取该进程的pid:getpid()

获得该进程的父进程id :getppid() 

一个进程的PCB(task_struct)里包含了该进程的pid和其父进程的id(ppid)

 查看进程

除却上面图片中用ps查看进程信息的方法以外,还可以这样查看:

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

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

 /proc:动态的目录结构,存放所有存在的进程,目录的名称以该进程的pid命名

我们可以看到pid为10714的进程里有exe:一个进程能找到自己的可执行程序,如上图中我生成的可执行程序是mycode

pid为10714的进程里有cwd(当前工作目录):默认情况下,进程启动所处的路径,就是当前路径

当然啦,只是默认情况下,要想更改当前工作目录可以使用chdir()

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

进程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初识
 

linux中创建进程的方式:

1 命令行中直接启动进程--(手动启动)

2 通过代码来创建进程

3 fork之后通常要用if来分流,让父子进程执行不同的代码,做不一样的事情

系统调用fork(),就是通过代码创建进程

启动一个进程本质就是系统多一个进程,OS要管理的进程就多了一个,进程=可执行程序+task_strcut对象(内核对象)

创建一个进程就是系统中要申请内存,保存当前进程的可执行程序+task_strcut对象,并将task_strcut对象添加到进程列表中

 fork有两个返回值,成功的话给子进程返回0,给父进程返回子进程的pid

失败则返回-1

 

可以看到pid为16869的进程 是pid为16870的父进程,二者是父子关系 

结合目前来看:只有父进程执行fork之前的代码,fork之后父子进程都要执行后续的代码

fork作为一个函数竟然有两个返回值?

fork代码的一般写法:

我们为什么要创建子进程?因为我们想要子进程协助父进程完成一些工作,比如边下载边播放

创建子进程就是为了让子进程和父进程做不一样的事情,执行不一样的代码

怎么保证能够做到上面的要求呢?可以通过判断fork的返回值,区分谁是父进程谁是子进程,然后让它们执行不同的代码片段

下面来处理几个问题:

1 为什么fork的两个返回值,给父进程返回子进程的pid,给子进程返回0?

因为父:子=1:n  父进程是唯一的,但是子进程可以有多个,父进程要管理众多子进程就必须有区分子进程的唯一标识符,而子进程只需要确定创建成功即可

2 fork之后,父子进程谁先运行?

答案是不确定

因为创建完成子进程只是一个开始,在这之后,系统的其他进程,包括父进程,子进程,接下来要被调度执行的,当父子进程的PCB都被创建,并在运行队列中排队的时候,哪一个进程的PCB先被选择调度,哪个进程就先运行,这个有操作系统自主决定,由各自PCB中的调度信息(时间片,优先级等)+调度器算法共同决定

进程的独立性首先是表现在有各自的PCB,进程之间不会相互影响,代码本身是只读的,不会影响,数据父子进程可能会修改,子进程的PCB以父进程为模板

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

所以,fork之后父子进程会执行一样的代码

3 为什么fork会有两个返回值?

我们通常认为若一个函数已经执行到return了,那么它的核心工作也就完成了

fork也是一个函数,会创建子进程,并将子进程放入到调度队列中运行

且fork之后代码共享,即fork函数中核心工作完成后,就已经有子进程了, return也是代码,return也会被共享! 父进程被调度就要执行return,子进程被调度也要执行return

4 如何理解一个变量会有不同的值?

 

id是fork的返回值,怎么可能同一个变量,同一个地址,会有不同的内容?

这里只能得出一个结论:这个地址绝对不是物理地址

另外,进程之间是互相独立的,互不影响,无论是什么关系

若是父进程被kill掉,子进程依然可以运行没有任何问题,反之亦然 

进程状态
 

为了弄明白正在运行的进程是什么意思,我们需要知道进程的不同状态
下面的状态在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)),也叫做浅度睡眠,浅度睡眠会对外部信号做出反应,可以被kill掉
D磁盘休眠状态(Disk sleep):有时候也叫不可中断睡眠状态(uninterruptible sleep),在这个状态的进程通常会等待IO的结束,也叫做深度睡眠,不可以被kill掉,os也没资格
T停止状态(stopped): 可以通过发送 SIGSTOP 信号给进程来停止(T)进程。这个被暂停的进程可以通过发送 SIGCONT 信号让进程继续运行。进程为什么要暂停?在进程访问软件资源的时候,可能暂时不能让进程进行访问,就将进程设置为T

t(tracing stop):debug调试程序的时候,追踪程序,遇到断点,进程就暂停了
X死亡状态(dead):这个状态只是一个返回状态,你不会在任务列表里看到这个状态

Z(zombie)-僵尸进程:僵死状态(Zombies)是一个比较特殊的状态。当进程退出并且父进程
没有读取到子进程退出的返回代码时就会产生僵死(尸)进程

僵死进程会以终止状态保持在进程表中,并且会一直在等待父进程读取退出状态代码
所以,只要子进程退出,父进程还在运行,但父进程没有读取子进程状态,子进程进入Z状态
 

简单来说,进程状态就是PCB中的一个字段,是PCB中的一个变量,int status

现在有#define NEW 1    #define RUNNING 2  #define BLOCK 3……

PCB->status = NEW    

if(pcb->status==NEW),pcb放入什么运行队列之类的

else if(pcb->status==BLOCK),pcb放入什么阻塞队列之类的

……

进程状态变化的本质:

1 更改PCB中status这个整型变量

2 将PCB列入不同的队列中

所谓状态变化,本质就是修改status整型变量

1 运行状态

只要是在运行队列中的进程,状态都是运行状态,表明进程已经准备好了,可以随时被调度

每一个cpu都会在系统层面维护一个运行队列

2 阻塞状态

我们的代码一定或多或少会访问系统中的某些资源,比如:磁盘,键盘,网卡等各种硬件设备

如scanf()  cin>> 本质就是我们需要从键盘上去读取数据,若是我们在键盘中输入了数据,那代码顺利执行下去,但若是我们一直不输入,那么键盘上的数据就没有就绪--->我们进程要访问的资源没有就绪(操作系统是要知道设备的状态的,且一定是最先知道它所管理的设备的状态变化的)--->进程代码无法继续向后进行,这就是进程阻塞了

操作系统要管理它手下的各种设备,就要先描述再组织:

当一个进程阻塞了,我们会看到什么现象?

1 进程卡住了

2 PCB没有在运行队列中,并且状态不是running,CPU不调度该进程了

该进程会进入键盘设备的阻塞队列中,同时将status修改成阻塞状态

3 挂起状态

如果一个进程当前被阻塞了,那么这个进程在它所等待的资源没有就绪的时候,该进程是无法被调度的,当如果此时os内的内存资源严重不足了怎么办?

将内存数据置换到外设,针对所有阻塞进程,不用担心慢的问题,因为这是必然的,当前的主要矛盾是让os继续执行下去,当进程被调度,曾经被置换出去的进程的代码和数据又要被重新加载回来

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

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

相关文章

如何在IIS7里设置实现访问.txt文件是下载模式

如何在IIS7里设置实现访问.txt文件是下载模式 1、打开iis管理器--在网站中点击需要调整的站点&#xff0c;然后在如下图位置点击MIME类型 2、添加一个新的MIME类型&#xff0c;文件扩展名填写 .txt&#xff0c;MIME类型填写application/octet-stream&#xff0c;如下图&#x…

图神经网络 GNN

之前经常看到图神经网络的内容&#xff0c;但是一直都觉得很难&#xff0c;就没有继续了解&#xff0c;现在抽空学习了一下&#xff0c;简单了解GNN是个什么东西&#xff0c;还没有进行代码实践&#xff0c;随着后续的学习&#xff0c;会继续更新代码的内容&#xff0c;这里先记…

Autosar诊断实战系列20-UDS首帧数据接收及流控帧发送代码级分析

本文框架 前言1. 长帧数据的首帧接收2. 首帧数据的处理及流控帧发送2.1 首帧数据的处理2.2 流控帧数据的发送前言 在本系列笔者将结合工作中对诊断实战部分的应用经验进一步介绍常用UDS服务的进一步探讨及开发中注意事项, Dem/Dcm/CanTp/Fim模块配置开发及注意事项,诊断与Bs…

【Redis】数据过期策略和数据淘汰策略

数据过期策略和淘汰策略 过期策略 Redis所有的数据结构都可以设置过期时间&#xff0c;时间一到&#xff0c;就会自动删除。 问题&#xff1a;大家都知道&#xff0c;Redis是单线程的&#xff0c;如果同一时间太多key过期&#xff0c;Redis删除的时间也会占用线程的处理时间…

【C++】STL详解(九)—— set、map、multiset、multimap的介绍及使用

​ ​&#x1f4dd;个人主页&#xff1a;Sherry的成长之路 &#x1f3e0;学习社区&#xff1a;Sherry的成长之路&#xff08;个人社区&#xff09; &#x1f4d6;专栏链接&#xff1a;C学习 &#x1f3af;长路漫漫浩浩&#xff0c;万事皆有期待 上一篇博客&#xff1a;【C】STL…

订单型批发制造企业经营分析123个指标大全(ODOO15/16)

ODOO-ERP搭建完成之后&#xff0c;我们重点是帮客户建立经营分析能力&#xff0c;以下是针对订单型企业的经营分析指标&#xff0c;涵盖业务运营的监控、资产构成、利润、盈亏点计算、资产运营效率等各方面&#xff0c;并且持续完善​。 有些企业不重视&#xff0c;觉得自己企业…

基于Java的环境保护宣传管理系统设计与实现(源码+lw+部署文档+讲解等)

文章目录 前言具体实现截图论文参考详细视频演示为什么选择我自己的网站自己的小程序&#xff08;小蔡coding&#xff09;有保障的售后福利 代码参考源码获取 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师、全栈领域优质创作…

前端——html面试题(一)

文章目录 前言一、说说你对 html 语义化的理解。二、html的标签有哪些三、标签类型1、问题&#xff1a;请说说你对块级元素、行内元素、空元素的理解&#xff0c;它们分别都有哪些常见的标签&#xff1f; 总结 前言 说说对html语义化的理解html的标签标签类型 一、说说你对 ht…

基于YOLOv8的安全帽检测系统(3):DCNv3可形变卷积,基于DCNv2优化,助力行为检测 | CVPR2023 InternImage

目录 1.Yolov8介绍 2.安全帽数据集介绍 3.InternImage介绍 4.训练结果分析 1.Yolov8介绍 Ultralytics YOLOv8是Ultralytics公司开发的YOLO目标检测和图像分割模型的最新版本。YOLOv8是一种尖端的、最先进的&#xff08;SOTA&#xff09;模型&#xff0c;它建立在先前YOLO成功…

【C语言】汉诺塔 —— 详解

一、介绍 汉诺塔&#xff08;Tower of Hanoi&#xff09;&#xff0c;又称河内塔&#xff0c;是一个源于印度古老传说的益智玩具。大焚天创造世界的时候做了三根金刚石柱子&#xff0c;在一根柱子上从下往上按照大小顺序摞着64片黄金圆盘。 大焚天命令婆罗门把圆盘从下面开始按…

java连接数据库SQL注入问题的解决

演示注入 解决方法把statement类型替换成了preparedstatement类型 其底层原理就是把用户输入的字符串转义了

java使用数据库连接池

我的jar包名字 这些包都可以去搜索,有很多小伙伴会用网盘给我们.导入jar包就是复制然后粘贴就好了

【C语言】归并排序

文章目录 一、什么是归并排序二、归并排序步骤图解三、归并排序代码实现1、递归实现2、非递归实现 四、总结 一、什么是归并排序 归并排序&#xff08;MERGE-SORT&#xff09;是建立在归并操作上的一种有效的排序算法,该算法是采用分治法的一个非常典型的应用。将已有序的子序列…

抖音聊天对话模板,制作一条一条冒出来的聊天对话短视频

聊天对话模板是一种非常有趣且实用的工具&#xff0c;它主要用于制作抖音聊天记录一条一条冒出来的视频。有了聊天对话模板&#xff0c;你再也不需要费心去写剧本&#xff0c;只需输入一些简单的文案&#xff0c;就能自动生成搞笑对话视频。 聊天对话工具下载&#xff1a; htt…

2023蓝帽杯半决赛电子取证+CTF部分题解

文章目录 电子取证123456789101112131415 CTFWeb | MyLinuxBotWeb | AirticleShareCrypto | ezrsaPwn | AdminPwn | uafmisc|排排坐吃吃果果 电子取证 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 CTF Web | MyLinuxBot Web | AirticleShare import requests import times reques…

【正点原子】无法打开 源 文件 “linux/time_types.h“ (dependency of “linux/ide.h“)

问题描述 在VS code中些驱动代码时显示&#xff1a; 检测到 #include 错误。请更新 includePath。已为此翻译单元(/home/alientek/linux/atk-mp1/Linux_Drivers/2_LED/led.c)禁用波形曲线。C/C(1696) 无法打开 源 文件 "linux/time_types.h" (dependency of "…

前端开发、后端开发与全栈开发:构建现代网站的全面视角

探讨前端开发、后端开发和全栈开发的概念和在构建现代网站和应用程序中的作用。随着技术的快速发展和市场竞争的加剧&#xff0c;理解和掌握这三种开发角色对于构建成功的数字产品至关重要。 一、前端开发&#xff1a;用户体验的核心 前端开发者主要负责构建用户与服务器交互…

JUC——原子类

1.基本类型原子操作类 AtomicInteger&#xff1a;整型原子类 AtomicBoolean&#xff1a;布尔型原子类 AtomicLong&#xff1a;长整型原子类 1.1常用API public final int get() //获取当前的值 public final int getAndSet(int newValue)//获取当前的值&#xff0c;并设置…

Linux shell编程学习笔记7:只读变量

在编程过程中&#xff0c;我们经常会使用到一些常量&#xff0c;也就是值不需要改变的变量&#xff0c;在许多编程语言提供了常量的定义方式&#xff0c;比如c/c的define MAXNUM 99999 或 const int a 7&#xff0c;javasccipt的const a7&#xff0c; 等等。 跟以上这些方法…

协议栈——连接服务器

如对方的ip和port配置信息&#xff0c;这里的连接是指通信前的准备工作 上一篇介绍查看套接字的命令时&#xff0c;可以看到很多信息&#xff0c;但是刚刚创建出来的套接字是什么信息都没有的&#xff0c;协议栈也因此不知道和谁通信&#xff1b; 客户端填补信息 这一步中调…