【Linux学习】日积月累——进程(1)

news2024/11/17 13:51:51

一、背景

1.1 冯诺依曼体系结构

  我们常见的计算机、服务器等设备大部分遵循冯诺依曼体系。

image-20230707230514578

图1 冯诺依曼体系结构

当前,我们所认识的计算机,主要由各类硬件组成:

  • 输入单元:包含键盘、鼠标、扫描仪、写板等;
  • 中央处理器:含有运算器和控制器;
  • 输出单元:显示器、打印机等。

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

  • 图1的存储器指的是内存;
  • 不考虑缓存情况,这里的CPU能且只能对内存进行读写,不能访问外设(输入或输出设备);
  • 外设(输入或输出设备)要输入或者输出数据,也只能写入内存或从内存中读取;
  • 注意:所有设备只能直接和内存打交道。

1.2 操作系统(Operator System)

1.2.1 概念

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

  • 内核:进程管理、内存管理、文件管理、驱动管理等
  • 其他程序:函数库、shell程序等

1.2.2 设计OS的目的

  • 与硬件交互,管理所有的软硬件资源;
  • 为用户程序(应用程序)提供一个良好的执行环境

1.2.3 定位

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

如何理解管理:

  • 描述被管理的对象
  • 组织被管理的对象

image-20230707232358682

图2 计算机算硬件管理层次图

  由上图可知:操作系统是一款进行软硬件资源管理的软件。且管理的本质:先描述,再组织。操作系统四大管理:内存管理、进程管理、文件管理、驱动管理。

1.2.4 操作系统总结

计算机硬件管理

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

在数据层面:一般CPU不和外设直接沟通,而是直接只和内存打交道,外设亦只会与内存打交道。

系统调用和库函数概念

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

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

承上启下

  操作系统管理进程管理:先把进程描述起来,再把进程组织起来。

二、进程的概念与定位

2.1 什么是进程

  相信各位读者对进程并不陌生,在我们日常使用的Windows操作系统下,进程图形化显示详见图3中。但进程具体是什么又说不出,进程可以理解为进程是程序的一个执行实例,正在执行的程序等。 内核观点:担当分配系统资源(CPU时间、内存)的实体。

image-20230306155738484

图3 Windows11下进程管理

在图3中,任何启动并运行程序的行为,都由操作系统帮助我们将程序转换为进程,完成特定的任务

2.2 进程的PCB

  图4中,在磁盘上的可执行程序,本质上就是一个普通二进制文件(文件包含文件内容和文件属性)。而可执行程序想要运行就必须将可执行程序的代码和数据加载到内存。进程信息被放在一个叫进程控制块的数据结构中,可以理解为进程属性的集合,一般称之为PCB(Process Control Block)。

image-20230414160542605

图4 进程管理的建模过程

pcb/task_struct提取了所有进程的属性;进程是由内核关于进程的相关数据结构(PCB/task_struct)与当前进程代码和数据组成。

task_struct-PCB的一种

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

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

task_struct内容分类

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

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

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

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

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

上下文数据:进程执行时处理器的寄存器中的数据

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

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

其他信息

PCB属性与文件(内容+属性)的属性关系不大,因为该属性仅仅包含文件的RWX权限,而PCB属性属于重起炉灶的数据结构。

2.3 组织进程

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

查看进程

  进程的信息可以通过/proc系统文件夹查看,详见图5:
在这里插入图片描述

图5 系统文件中进程信息查看

  Linux系统中,根目录下有一个proc目录,保存进程相关属性的目录。proc目录下保存的是一个内存级的文件系统,只有当Linux系统启动时它才会存在,磁盘上并不存在。图2中蓝色数字表示特定进程的PID。

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

例如,在执行下列代码的时候,生成可执行程序后,通过./将其转化为进程,详见图6:

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

int main(){
  while(1){
    printf("hello process\n");
    sleep(1);
  }
  return 0;
}

image-20230414165408228

图6 ./将可执行程序转化为进程

此时,可以通过ps命令查看进程的相关信息,详见图7:

image-20230414165613188

图7 ps查看进程信息

注意:去除grep进程,可以通过grep -v grep

通过图8及图9可以看出,将进程20110中断后(或该进程终止后),系统目录proc下的20110文件亦会删除:

image-20230414170609281

图8 中断的进程PID无法保存

进程关闭时,操作系统会在/proc路径下将PID命名的文件夹20110的内容全部删除。

image-20230414171712852

图9 proc目录下进程属性的相关信息

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

  • 进程ID(PID)
  • 父进程ID(PPID)
#include<stdio.h>
#include<unistd.h>

int main(){    
  while(1){
  	printf("hello proccess, 我已经是一个进程了,我的pid是:%d,我的父进程是:%d\n",getpid(),getppid());
  }
  return 0;
}

  图10中,频繁启动上述代码生成的可执行程序myprocess时,PPID值不变,PID数值在myprocess重启时会更改:

image-20230414203221924

图10 myproccess进程的执行结果

image-20230414203247987

图11 进程myproccess的信息

且通过图11可知,进程myproccess的父进程是bash。bash是命令行解释器,本质上它也是一个进程,因为它有独立的PID;命令行启动的所有的程序,最终都会变成进程,而该进程对应的父进程都是bash。

  进程的删除除了通过ctrl+c命令还可以通过kill -9 "PID",根据进程PID来删除进程,结果图9所示:

image-20230414204145192

图12 kill删除进程

2.5 通过系统调用创建进程

2.5.1 如何创建子进程?

  要想创建子进程,必须使用系统调用函数fork()。例如,执行下述代码,生成对应的可执行程序myprocess后,经过./进程执行后,图13中产生了2个返回值。

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

int main(){    
  printf("AAAAAAAAAAAAAAAAAAAAAAA:pid->%d,ppid->%d\n",getpid(),getppid());
  fork();
  printf("BBBBBBBBBBBBBBBBBBBBBBB:pid->%d,ppid->%d\n",getpid(),getppid());
  sleep(1);
  return 0;
}
//fork()后有两个执行流

image-20230414210100345

图13 进程的执行结果

2.5.2 fork的两个返回值

通过系统调用创建进程-fork(系统调用函数,操作系统提供的)

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

int main(){
  printf("AAAAAAAAAAAAAAAAAAAAAAA:pid->%d, ppid->%d\n",getpid(),getppid());
  pid_t ret = fork();
  printf("BBBBBBBBBBBBBBBBBBBBBBB:pid->%d, ppid->%d, ret:%d, &ret:%p\n",getpid(),getppid(),ret,&ret);
  sleep(1);
  return 0;
}

image-20230414212412976

图14 进程执行结果

上图中,ret给子进程返回0,给父进程返回的是子进程的PID。具体原因将在fork原理说明。此外,一般fork常与if一起使用,就能返回一个结果:

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

int main(){
  pid_t ret = fork();
  assert(ret != -1);
  if(ret == 0){
    //子进程
    while(1){
    printf("我是一个子进程了,我的pid是:%d,我的父进程是:%d\n",getpid(),getppid());
    sleep(1);
    }
  }else if(ret > 0){
    //父进程 
    while(1){
    printf("我是一个父进程了,我的pid是:%d,我的父进程是:%d\n",getpid(),getppid());
    sleep(2);
    }
  }
  return 0;
}

image-20230414215859509

图15 进程执行结果

2.5.3 fork原理

  • fork做了什么?
  • fork如何看待 —— 代码和数据
  • fork如何理解两个返回值问题

  fork原理主要从上述3个方面来讲述。在图16中,fork创建了子进程,该子进程继承了父进程大部分PCB属性(PID、PPID等除外),且子进程和父进程共享内存处的数据和代码(红框处)。

image-20230707223029955

图16 fork原理图

进程在运行的时候具有独立性的,父子进程在运行的时候也是一样

image-20230707223727586

图16 杀死子进程

  进程PCB指向的内存块中的代码是只读的;对于数据,当一个执行流尝试修改数据的时候,OS会自动给我们当前触发写时拷贝。父子进程会共享指向的内存处的代码和数据,且独立运行,如图16中,子进程被killed,父进程依然正常运行。若子进程对数据进行更改,则在内存中申请空间存储更改的数据,并返回,不会对父进程产生影响。

  一般而言,当我们函数内部准备执行return的时候,我们的主体功能已经完成。fork具有两个返回值,注意,fork本质上是OS提供的一个函数;fork之前的代码父进程执行,而fork之后的代码子进程、父进程都执行。当fork函数内部准备执行return时,我们的主体功能已经完成。父子进程都执行,因而产生两个返回值。

2.5.4 fork小结:

  • fork之后,执行流会变成2个执行流;
  • fork之后,谁先运行由调度器决定;
  • fork之后,fork之后的代码共享,通常通过if和else if来进行执行流分流
  • 进程在运行的时候,是具有独立性的!父子进程在运行的时候也是一样具有独立性。
  • 代码:代码是只读的
  • 数据:当有一个执行流尝试修改数据时侯,OS会自动给我们当前进程触发写时拷贝

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

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

相关文章

Unity游戏开发之游戏动画(Unity动画系统)

文章目录 Unity动画系统动画片段 Animation Clip在Unity中制作动画动画复用 替身系统 AvatarAnimator 组件 Animator Component动画状态机 Animator Controller动画状态动画状态的类型动画状态的属性 动画状态机----动画转换转换属性的配置面板 、、、、 Unity动画系统 Unity动…

selenium 02

✏️作者&#xff1a;银河罐头 &#x1f4cb;系列专栏&#xff1a;JavaEE &#x1f332;“种一棵树最好的时间是十年前&#xff0c;其次是现在” 目录 等待浏览器的操作键盘事件鼠标事件定位一组元素多层框架定位下拉框处理弹窗处理上传文件操作关闭浏览器切换窗口截图 等待 s…

SOLIDWORKS小问题解答 硕迪科技

众所周知&#xff0c;SOLIDWORKS的使用者非常多&#xff0c;在使用过程中呢也会有一些小问题&#xff0c;前几天就有朋友来咨询了两个问题&#xff0c;可能其他使用者也有这样的问题&#xff0c;所以今天硕迪科技统一为大家解答 问题一&#xff1a;SOLIDWORKS装配图中的颜色怎么…

csv文件如何在单元格内保留换行 \r\n

csv文件如何在单元格内保留换行 \r\n 一、问题描述 我们都知道 csv 文件内是通过 , 来切分单元格的。 但如果某个单元格内的内容有换行&#xff1a; 34, KyleBing, 你好 # 这里换行了 我的名字是 kyebing 35, Tina, hello 36, Lucy, nice to meet you当用 Excel 打开的时候…

如何使Revit导出带有标准材质的FBX模型?

如何使Revit导出带有标准材质的FBX模型&#xff1f; 初次使用Revit导出fbx格式模型&#xff0c;在导出模型后发现模型材质丢失了&#xff0c;上网查询之后也没到具体原因是什么&#xff0c;不过倒是找到了解决方式&#xff1a;在Revit中安装naviswork插件&#xff0c;然后从re…

Vue之router和vuex

Vue之router和vuex 在学习之前&#xff0c;我们可以先安装一个插件&#xff0c;是一个浏览器插件&#xff0c;这个插件的作用是可以让我们更好的观察到数据的变化和状态的变更 他是一个github的链接&#xff0c;如果你没办法科学上网的话&#xff0c;也可以在 插件下载 上面这…

CodeQL在代码审计中的应用

一、CodeQL简介 CodeQL是一种基于静态分析的程序分析工具&#xff0c;由GitHub开发和维护。截止到此刻&#xff0c;CodeQL在Github上已经有超过6100个Star。它可以用于发现代码中的漏洞、代码质量问题和安全问题。CodeQL使用了一种特殊的编程语言QL&#xff08;查询语言&#…

【Unity项目】Unity实现 双屏显示效果

需求&#xff1a;两个屏显示项目&#xff0c;一个屏显示列表&#xff0c;一个屏显示列表按钮点击后的效果。 最近在修改一个项目&#xff0c;是要求分屏显示项目&#xff0c;一个一体机&#xff0c;一个大屏&#xff0c;一体机当作目录界面&#xff0c;大屏当作模型显示界面&am…

NC140 排序

冒泡排序 public int[] BubbleSort(int[] arr) {// write code herefor (int i 0; i < arr.length - 1; i) {for (int j 0; j < arr.length - 1 - i; j) {if (arr[j] > arr[j 1]) {int temp arr[j];arr[j] arr[j 1];arr[j 1] temp;}}}return arr;}插入排序 p…

HTTP1.0、HTTP1.1、HTTP2.0、HTTP3.0的关系和区别

文章目录 一、对比二、HTTP1.0三、HTTP1.1四、HTTP2.0四、HTTP/3.0五、总结 一、对比 二、HTTP1.0 浏览器的每次请求都需要与服务器建立一个TCP连接&#xff0c;服务器处理完成后立即断开TCP连接&#xff08;无连接&#xff09;&#xff0c;服务器不跟踪每个客户端也不记录过去…

如何使用js对图像进行压缩

JavaScript 可以使用类似于 canvas 和 web workers 来实现图像压缩。 使用 canvas&#xff0c;可以将图像绘制到 canvas 上&#xff0c;然后使用 canvas 提供的 toBlob() 或 toDataURL() 方法将其转换为不同格式的图像。在这些方法中指定图像质量参数即可实现压缩。 使用 web…

python接口自动化(二十)--token登录(详解)

简介 为了验证用户登录情况以及减轻服务器的压力&#xff0c;减少频繁的查询数据库&#xff0c;使服务器更加健壮。有些登录不是用 cookie 来验证的&#xff0c;是用 token 参数来判断是否登录。token 传参有两种一种是放在请求头里&#xff0c;本质上是跟 cookie 是一样的&…

【JS】自调用函数(自执行函数)

文章目录 自调用函数&#xff1a;只能自己在定义的同时调用自己一次&#xff0c;对外界不可见。 语法 // 函数名加与不加都可以 (function 函数名(形参){console.log(11); })(实参)使用&#xff1a; // 用法一 const str 流星; (function (params){console.log(params); // …

33.RocketMQ之Broker启动源码

highlight: arduino-light Broker启动流程:BrokerStartup#main java public static void main(String[] args) { //手动指定了 nameServer start(createBrokerController(args)); } java public static BrokerController start(BrokerController controller)…

Docker开启远程端口访问2375

开启方法&#xff1a; 1、修改/etc/default/docker下的配置 cat >> /etc/default/docker <<EOF DOCKER_OPTS"-H tcp://0.0.0.0:2375" EOF systemctl restart docker 2、修改/usr/lib/systemd/system/docker.service配置 cat >> /usr/lib/systemd/s…

第四章 网络层【计算机网络】

第四章 网络层【计算机网络】 前言推荐第四章 网络层4.1 网络层的几个重要概念4.1.1 网络层提供的两种服务4.1.2 网络层的两个层面 4.2网际协议IP4.2.1 虚拟互连网络4.2.2 IP地址4.2.3IP地址与硬件地址4.2.4地址解析协议ARP4.2.5IP数据报的格式 4.3 IP层转发分组的过程4.3.1 基…

个人域名邮箱无法给gmail发邮件

问题描述 我注册了一个域名 mydomain.com, 并在此域名下创建了 mailbox&#xff0c;从该邮箱向外发送邮件和接收邮件会失败。 主要是一些配置工作没有做好。 接收邮件 当创建邮箱 xxxmydomain.com&#xff0c;尝试向该邮箱发送邮件时&#xff0c;邮件会被拒收&#xff0c;并…

选择正确的负载均衡器:LVS还是Nginx?

选择正确的负载均衡器&#xff1a;LVS还是Nginx&#xff1f; 博主简介一、前言二、什么是LVS&#xff1f;2.1、LVS的架构 三、什么是Nginx&#xff1f;3.1、Nginx 特性3.2、Nginx 架构 四、LVS和Nginx的区别五、总结 博主简介 &#x1f4a1;一个热爱分享高性能服务器后台开发知…

windows环境下根据端口号查询进程编号并杀掉此进程

说在前面 当端口号备占用时&#xff0c;我们需要找到占用端口的进程&#xff0c;然后杀掉该进程&#xff0c;即可 开干 1、找占用端口的进程&#xff0c;比如现在是3306端口备占用了 netstat -nao|findstr 3306运行如下&#xff1a; 2、杀掉进程&#xff0c;比如进程号为…

【InnoDB 存储引擎】15.4 InnoDB Architecture

文章目录 1 InnoDB 存储引擎结构2 参考资料 1 InnoDB 存储引擎结构 下面的图显示了内存和磁盘结构组成InnoDB存储引擎架构 内存结构&#xff1a;Section 15.5, “InnoDB In-Memory Structures” Buffer Pool&#xff08;缓冲池&#xff09;Change Buffer&#xff08;修改缓冲&…