Linux系统:进程概念

news2024/9/20 21:27:38

文章目录

  • 1 操作系统
    • 1.1 概念
    • 1.2设计操作系统的目的
  • 2进程
    • 2.1 进程是什么
    • 2.2 如何管理进程
    • 2.2查看进程
    • 2.3 程序中获取自己的pid
    • 2.4 创建一个进程
    • 2.5 进程状态
    • 2.6 进程优先级
      • 2.6.1为什么要有优先级
      • 2.6.2 什么是优先级
      • 2.6.3 linux下优先级的做法
    • 2.8 其他概念
  • 3进程地址空间
    • 3.1 初识地址空间
    • 3.2进程地址空间的意义

1 操作系统

1.1 概念

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

1.2设计操作系统的目的

对下:与硬件交互,管理所有的软硬件资源
对上:为用户程序(应用程序)提供一个良好的执行环境
理解:操作系统就是一款纯正“搞管理”的软件,对软硬件资源进行管理
如何理解管理:即先描述被管理对象(struct),然后组织被管理对象(数据结构)

2进程

2.1 进程是什么

进程=对应的代码和数据(程序加载进内存)+进程对应的PCB结构体

2.2 如何管理进程

运行中的系统会存在大量的进程,操作系统会如何管理这些进程呢?
仍然是先描述,再组织

描述是指:进程在形成之初,操作系统就会为其创建进程控制块PCB,PCB用于描述进程,里面存储着进程的所有属性,可以理解为进程属性的集合。
linux下的PCB叫做叫做 task_struct

组织是指:通过双链表的形式将大量的进程链接起来。这也再一次对应了操作系统进行管理的本质

2.2查看进程

在这里插入图片描述
例如:要获取PID为1的进程信息,你需要/proc/1这个文件夹

2.3 程序中获取自己的pid

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

2.4 创建一个进程

fork()函数,创建一个子进程
返回值 创建失败返回-1 ,创建成功给父进程返回子进程的pid,给子进程返回0
fork之后,父子进程代码共享,数据各自开辟空间

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

int main()
{

  int ret=fork();
  if(ret<0)
  {
    perror("fork");
    return 1;
  }
  else if(ret==0)//子进程
  {
    while(1)
    {

     printf("I am  child .pid: %d ,  ppid:%d,ret: %d\n ",getpid(),getppid(),ret );
     sleep(1);    

  }
  }
  else//父进程
  {
    while(1)
    {  
    printf("I am father.pid: %d,ppid:%d,ret: %d\n",getpid(),getppid(),ret);

  
  sleep(1);

}
}

  return 0;
}

理解:
①父子进程被创建出来,谁先执行不一定,由操作系统的调度器来决定
②创建子进程成功后,为什么会有两个返回值?
在fork函数内部,当开始return的时候,核心代码已经执行完毕,所以此时已经有了父子进程,父子各自会执行自己的return语句,也就是给父进程返回的是子进程的pid,给子进程返回0

2.5 进程状态

状态解释
新建态进程刚被创建
运行态task_struct结构体在运行队列中排队
阻塞态等待非cpu资源就绪(阻塞队列)
挂起态内存不足时,OS通过适当的置换进程的代码和数据到磁盘

linux下的进程

状态解释
R对应运行态
S对应阻塞态,可中断睡眠
D睡眠状态,磁盘睡眠,深度睡眠,不可被中断,不可以被被动唤醒
T暂停状态,进行调试
X终止态
Z僵尸状态,一个进程已经退出,但是还不允许操作系统释放(PCB未释放,代码和数据可以被释放),处于一个被检测的状态

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

②僵尸进程会以终止状态保持在进程表中,并且会一直在等待父进程读取退出状态代码。

所以,只要子进程退出,父进程还在运行,但父进程没有读取子进程状态,子进程进入Z状态

代码演示

#include<stdio.h>
#include<stdlib.h>
int main()
{
  pid_t id=fork();
  if(id<0)
  {
    perror("fork");
  }
else if(id==0)
  {
    while(1)
    {
      printf("I am child pid: %d,ppid: %d\n",getpid(),getppid());
      sleep(3);
      break;
    }
    exit(0);
  }

  
  
  else{
    while(1)
    {
      printf("I am parent pid: %d,ppid: %d\n",getpid(),getppid());
      sleep(1) ;  
    }

  
}
  return 0;
}

在这里插入图片描述
僵尸进程的危害

维护退出状态本身就是要用数据去维护,所以这个状态信息就会一直保存在task_struct中,即如果Z状态一直不退出,那么PCB就一直要维护。那么当父进程创建了很多子进程,一直不回收,就会造成内存资源的浪费,即内存泄露。

再来认识下另外一种进程-孤儿进程
如果父进程退出,子进程还在,子进程就叫孤儿进程

孤儿进程会被1号进程领养

来段代码

#include<stdio.h>
#include<stdlib.h>
int main()
{
  pid_t id=fork();
  if(id<0)
  {
    perror("fork");
  }
  else if(id==0)
  {
    while(1)
    {
printf("I am child\n");
sleep(1);
    }
  }
  else{
    while(1)
    {
      printf("I am father\n");
      sleep(3);
      break;
    }
    exit(0);
  }
  return 0;
}

在这里插入图片描述

2.6 进程优先级

2.6.1为什么要有优先级

因为cpu是有限的,进程太多,需要通过某种方式来竞争资源

2.6.2 什么是优先级

用一些数据来确认谁先获得某种资源,谁后获得

2.6.3 linux下优先级的做法

优先级=老的优先级(PRI)+nice值(NI)
在这里插入图片描述

PRI:进程的优先级,也就是程序被CPU执行的先后顺序,此值越小进程的优先级别越高。
NI:表示进程可被执行的优先级的修正数值,当nice值为负值的时候,那么该程序的优先级值将变小,即其优先级会变高,则其越快被执行,nice 值为正值时则相反。
所以,调整进程优先级,在Linux下,就是调整进程nice值(因为PRI值每次都是80),nice其取值范围是-20至19,一共40个级别。

2.8 其他概念

① 竞争性: 系统进程数目众多,而CPU资源只有少量,甚至1个,所以进程之间是具有竞争属性的。为了高效完成任务,更合理竞争相关资源,便具有了优先级。
②独立性: 多进程运行,需要独享各种资源,多进程运行期间互不干扰
③并行: 多个进程在多个CPU下分别同时进行运行,这称之为并行
④并发: 多个进程在一个CPU下采用进程切换的方式,在一段时间之内,让多个进程都得以推进,称之为并发。

3进程地址空间

3.1 初识地址空间

在这里插入图片描述
这是我们写代码的时候的空间布局图

先在linux下验证下

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
int g_unval;
int g_val=100;
int main(int argc,char* argv[],char*env[])
{
  const char* str="hello";
  printf("code addr:%p\n",main);
  printf("init global addr:%p\n",&g_val);
  printf("uninit global addr:%p\n",&g_unval);
  static int test = 10;
  char *heap_mem = (char*)malloc(10);
   char *heap_mem1 = (char*)malloc(10);
   char *heap_mem2 = (char*)malloc(10);
   char *heap_mem3 = (char*)malloc(10);
    printf("heap addr: %p\n", heap_mem);
    printf("heap addr: %p\n", heap_mem1);
    printf("heap addr: %p\n", heap_mem2); 
     printf("heap addr: %p\n", heap_mem3);
     printf("test staic addr: %p\n", &test); 
      printf("stack addr: %p\n", &heap_mem);
       printf("stack addr: %p\n", &heap_mem1);
       printf("stack addr: %p\n", &heap_mem2);
       printf("stack addr: %p\n", &heap_mem3);
       printf("read only string addr: %p\n", str);
       int i,j;
       for( i = 0 ;i < argc; i++)
       {
                 printf("argv[%d]: %p\n", i, argv[i]);
                     
       }
        for( j = 0; env[j]; j++)
        {
                  printf("env[%d]: %p\n",j, env[j]);
                      
        }
        return 0;

}

在这里插入图片描述
仔细观察堆区和栈区,可以发现堆区和栈区是相对而生,
堆是向高地址方向生长,栈是向低地址方向生长

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
int g_val=100;
int main()
{
     pid_t id=fork();
     if(id==0)
     {
       g_val=200;
       printf("child[%d]:%d,%p\n",getpid(),g_val,&g_val);

     }
     else 
     {
       printf("parent[%d]:%d,%p\n",getpid(),g_val,&g_val);
     }
     return 0;
}

在这里插入图片描述

查看运行结果,发现了一个很奇怪的问题,父子进程,g_val的输出地址是一样的,但是
变量的值却不一样。
所以得出如下结论
①变量的值不一样,所以父子进程输出的变量绝对不是同一个变量
②打印出的地址值是一样的,说明这个地址并不是实际的物理地址。

在linux下,这种地址叫做虚拟地址(进程虚拟地址空间)
地址空间是一种内核数据结构,每一个进程都拥有一个进程地址空间,它里面通过起始和结束下标来确定各个区域的划分。而操作系统通过页表和MMU(内存管理单元)将虚拟地址和物理地址关联起来。
页表是操作系统提供的一张映射表,其左侧存放虚拟地址,右侧存放实际地址。

在这里插入图片描述

这张图就可以很好的解释之前的问题,在创建子进程的时候,会先以父进程为模板,继承父进程的代码数据以及进程数据结构,所以父子进程的g_val的虚拟地址是一样的,但当子进程要修改数据的时候,操作系统才会在物理内存上开辟一块新的空间,通过页表映射机制,将子进程g_val的实际地址与虚拟地址联系起来。这也叫做写时拷贝。

3.2进程地址空间的意义

①有效保护了物理内存中的所有合法数据以及进程,内核的相关数据(因为地址空间和页表都是OS创建并维护的,所以想要使用地址空间和页表进行维护,也一定要在OS的监管下,如果是非法的访问或者映射,OS都会识别,并终止这个进程)
②内存管理模块和进程管理模块完成了解耦合
因为地址空间的存在,所以我们使用new或者malloc申请空间的时候,实际是在地址空间上申请的,物理内存可以一个字节都先不分配,当真正对物理地址空间访问的时候,再执行相关的算法,申请内存,内存访问是由操作系统完成的,进程几乎0感知,这种延迟分配的策略,会提高整机的效率,并且内存管理模块和进程模块做到了没有任何关系。
③内存分布有序化
因为页表的存在,它可以将地址空间上的虚拟地址和物理地址进行映射,在进程视角的所有内存分布,都是有序的。
④实现进程独立性
因为有地址空间的存在,每一个进程都认为自己拥有4GB的空间,不需要知道其他进程的存在,只通过页表映射到不同的区域,来实现进程的独立性。

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

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

相关文章

基于Docker环境下的Jenkins搭建及使用

目录 前言&#xff1a; Docker下安装Jenkins 启动Jenkins Jenkins插件下载 配置Jenkins 修改Jenkins时间 配置Python3环境 配置HTML报告/Allure报告 Allure报告配置 简单使用-实例构建 参数化构建 构建后操作 配置成功 控制台输出 Allure报告查看 HTML报告查看 前言&#xff1a…

macOS上下载安装Kibana并连接ES

下载Kibana 执行以下命令进行&#xff0c;版本号根据你所用的ES版本选择&#xff0c;比如我的是7.10.0 curl -O https://artifacts.elastic.co/downloads/kibana/kibana-7.10.0-darwin-x86_64.tar.gz解压安装Kibana tar -zxvf kibana-7.10.0-darwin-x86_64.tar.gz进行config…

Visual modflow Flex地下水数值模拟

专题一 地下水数值软件的操作流程、建模步骤和所需资料处理及相关注意事项 [1] Visual MODFLOW Flex特征 [2] Visual MODFLOW Flex软件界面及模块 [3] 地下水数值模拟的建模步骤及数据需求 专题二 模型建模操作方法 技巧、真实案例演练、特殊问题处理 [1] 直接模型建模的操…

【信号】信号处理与进程通信:快速上手

目录 0. 信号概述 1. 产生信号的方式&#xff1a; 1.1 当用户按某些终端键时&#xff0c;将产生信号。 1.2 硬件异常将产生信号。 1.3 软件异常将产生信号。 1.4 调用kill函数将发送信号。 1.5 运行kill命令将发送信号。 2. 信号的默认&#xff08;缺省&#xff09;处理…

用for循环----输出0-999的水仙数

输出0-999的水仙数 水仙算法&#xff1a;一个数它各位的立方和&#xff0c; 例如&#xff1a;1531*1*15*5*53*3*3 提示&#xff1a;for循环&#xff0c;求余数(%)取整&#xff08;/&#xff09;运算符。 答案&#xff1a; 代码如下&#xff1a; #include <stdio.h> …

物理备份xtrabackup

物理备份xtrabackup Xtrabackup 是一个开源的免费的热备工具&#xff0c;在 Xtrabackup 包中主要有 Xtrabackup 和innobackupex 两个工具。其中Xtrabackup 只能备份 InnoDB 和 XtraDB 两种引擎; innobackupex则是封装了Xtrabackup&#xff0c;同时增加了备份MyISAM引擎的功能。…

STM32开发——智能小车(循迹、避障、测速)

目录 1.循迹小车 1.1CubeMX配置 1.2函数代码 2.避障小车 3.小车测速 1.循迹小车 需求&#xff1a;用左右轮实现PWM调速、红外传感器获取道路信息改变方向。 左边红外D0——PB12 右边红外D0——PB13 1.1CubeMX配置 1.2函数代码 motor.c代码 #include "gpio.h"…

TCP三挥四握

TCP三挥四握 TCP最关键的三个步骤&#xff1a;建立连接、数据传输、释放连接&#xff0c;这里的三次握手实现的是服务端和客户端建立连接&#xff1b;四次握手实现的是服务端和客户端释放连接。 三次握手&#xff1a; 建立数据连接 TCP连接需要三次握手的原因&#xff1a; 三次…

【Free】基于主从博弈的主动配电网阻塞管理

目录 1 主要内容 程序亮点 2 部分代码 3 程序结果 4 下载链接 1 主要内容 《基于主从博弈的主动配电网阻塞管理》文献介绍&#xff1a;主要采用一种配电网节点边际电价统一出清的主从博弈双层调度框架。上层框架解决用户在负荷聚合商引导下的用电成本最小化问题&#xff0…

力扣题库刷题笔记18--四数之和

1、题目如下&#xff1a; 2、个人Python代码实现&#xff1a; 首先看到这题就会想到两种方式&#xff0c;一是四层循环暴力破解&#xff0c;二就是基于前面的三数之和外面加加一层嵌套。 先看一下暴力破解&#xff1a; 这里可以看到&#xff0c;当数据量足够大时&#xff0c;依…

Openresty原理概念篇(十)为什么 lua-resty-core 性能更高一些

一 为什么 lua-resty-core 性能更高一些 ① 回顾 lua-resty-core和lua-nginx-module各自都有哪些API? --> 看下面两个仓库的文档 lua-nginx-module lua-resty-core 下面&#xff1a;一起看下Lua C API和FFI 的实现有何不同之处,这样可以对它们的性能有个直观认识…

14. python从入门到精通——GUI编程

目录 常用的GUI框架 wxPython&#xff1a;比较常用 PyQt6&#xff1a;比较常用 Kivy Flexx Tkinter 安装PyQt5 要开发PyQt5程序需要安装三个模块&#xff1a; 安装命令&#xff1a; 安装 安装命令&#xff1a; window安装&#xff1a; PyCharm软件安装&#xff1a…

Android跳转具体应用权限管理,三种方式

背景&#xff1a;Android越来越安全合规&#xff0c;在应用里&#xff0c;需要给用户所有可选择和取消的明确方式。 比如&#xff1a;设置了权限&#xff0c;也要给用户关闭权限的入口。被要求在应用的设置里&#xff0c;提供权限管理入口。 解决方式有三&#xff1a; 方式一…

ansible实训-Day2(ansible基本问题及部署安装)

一、前言 该篇是对ansible实训第二天内容的归纳总结&#xff0c;主要包括ansible的一些基本问题以及ansible的部署安装。 二、理论部分 Q1&#xff1a;什么是ansible Ansible是一种自动化IT工具&#xff0c;它可以帮助管理和自动化IT基础架构。使用Ansible&#xff0c;管理员…

神仙级编程神器,吹爆!

Visual Studio 编程领域公认的“最强IDE”&#xff0c;Visual Studio是目前最流行的Windows平台应用程序的集成开发环境&#xff0c;提供了高级开发工具、调试功能、数据库功能和创新功能&#xff0c;帮助在各种平台上快速创建当前最先进的应用程序&#xff0c;开发新的程序。 …

界面控件DevExpress ASP.NET中文 - 如何自定义编辑表单运行时布局?

在DevExpress ASP.NET控件v19.2版本中就针对ASP. NET WebForms和MVC平台的ASP. NET GridView和CardView控件添加了一个主要增强功能。 DevExpress ASP.NET v23.1正式版下载(Q技术交流&#xff1a;523159565&#xff09; 当您使用预定义的或 自定义的编辑表单时&#xff0c;经…

网易兑换礼包码分析

🍋前言 由于C站版权太多,所有的爬虫相关均为记录,不做深入! 接到了一个项目大概是电商老板想弄一个自动化工具,方便自己处理买家买的兑换码,一个一个的兑换有点累人,代码写到一半才发现由于自己的原因分析错了,刚刚开始我以为他的验证码是在html上的,就像这样 <…

SpringBoot02:运行原理初探

目录 一、运行原理探究 1、pom.xml文件 1.1、父依赖 1.2、启动器Spring-boot-starter 2、主启动类 2.1、默认的主启动类&#xff1a; 2.2、分析主启动类注解&#xff1a;SpringBootApplication 1、Target(ElementType.TYPE) 2、Retention(RetentionPolicy.RUNTIME) 3…

【C语言初阶(3)】循环语句:for 循环

文章目录 1. 语法结构2. for 语句的执行流程3. for 循环中的 break 和 continue3.1 for 循环中的 break3.2 for 循环中的 continue 4. for 循环语句的循环控制变量5. for 循环的变种5.1 for( ; ; )5.2. for 循环的嵌套5.3 使用多个变量控制循环 6. for 循环笔试题 1. 语法结构 …

【读书笔记】《数据结构C语言版》

目录 第一章 线性表 第二章 栈和队列 第三章 字符串 第四章 广义表 第五章 树 第六章 图 第七章 查找 第八章 内排序 第一章 线性表 一个线性表是n个数据元素的优先序列线性表可分为顺序存储结构&#xff08;数组&#xff09;和链式存储结构&#xff08;链表&#xff…