linux系统编程之进程概念(操作系统---管理,进程创建,进程状态,进程优先级, 环境变量,程序地址空间,进程O(1)调度方法)

系统编程:

	进程概念->进程控制->基础IO->进程间通信->进程信号->多线程

进程概念

冯诺依曼体系结构----现代计算机硬件体系结构

在这里插入图片描述
冯诺依曼体系结构----现代计算机硬件体系结构

	计算机五大硬件单元:输入设备:键盘输出设备:显示器存储器:内存-外存---固态接口类型SATA   SATA3  PCI-E(目前最好)运算器:CPU—主频2.5GHz,主频越大代表时钟震荡周期越高,代表1s中处理的指令也就越多控制器:CPU所有设备都是围绕存储器工作的

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

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

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

硬件决定了软件的行为

操作系统—管理

在这里插入图片描述
操作系统:

一个软件安装在计算机硬件上

目的:

为了让计算机更加好用—功能:合理统筹管理计算机上边的软硬件资源

管理:

先描述使用pcb描述进程,使用双向链表将pcb串起来进行管理,再组织。

库函数与系统调用接口的关系:

封装关系:库函数封装了系统调用接口,是上下级的调用关系

进程概念----进程是什么

进行中的程序
linux是一个多任务操作系统,表示有大量的程序需要被cpu调度运行,这时候cpu使用了分时技术,分别轮询处理每一个进程,在进程程序切换调度时,需要记录运行信息,因此操作系统在调度进程在cpu上运行时,使用pcb对运行中的程序进行描述,通过调度pcb完成对进程的调度,因此进程是pcb。
pcb对运行中程序进行描述
每一个运行的程序都是pcb

在操作系统角度,操作系统通过pcb来控制一个进程的运行,这个pcb也叫进程描述符,描述了一个运行中的程序
Linux操作系统下的PCB是task_struct结构体(用双向链表进行组织的)

什么时task_struct结构体

参考链接

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

内存指针

pcb中有一个指针指向了当前要运行的程序
cpu通过pcb内存指针知道代码在什么位置,然后加载到内存上面

cpu分时机制

不会体会到卡顿的原因,调度进程时,不会一直在一个进程上面运行,轮询调度pcb 。
每个都执行一段时间,切换速度很快。
每个进程只运行很短的一段时间(时间片)

程序计数器

即将执行的指令的地址

上下文数据

cpu正在处理的数据是什么

标识符PID

每一个进程都有一个ID

进程状态

当前进程处于什么状态

优先级

前台进程(交互式进程)优先级更高
批处理(后台进程)

IO状态信息

每一个进程里面都会打开很多的文件,打开文件就要进行管理
记录描述文件,所以需要保存下来这些信息

记账信息

一个进程大致在cpu上运行了多长时间

进程查看

ps

-ef 
-aux		查看系统所有进程信息
  • /proc 保存系统中正在运行的程序信息
  • pid_t getpid() 获取调用进程的pid
    在这里插入图片描述
  • 根目录下的proc/目录存放的就是当前操作系统上面正在运行中的程序的运行信息

例如

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

在这里插入图片描述

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

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

在这里插入图片描述

进程创建

创建进程就是创建pcb
在这里插入图片描述
用fork创建进程
fork()—通过复制调用进程(父进程)创建一个新的进程(子进程)
子进程与父进程完全相同
在这里插入图片描述
在这里插入图片描述
head line 打印了一次
tail line 打印了两次
在这里插入图片描述
复制了父进程的pcb(意味着和父进程拥有一样的内存指针,程序计数器,上下文数据):
和父进程运行相同的代码,相同的运行位置,
处理一样的数据

父子进程代码共享,数据独有 。
同一个内存区域,打印的值相同

子进程创建成功都是从下一步指令开始运行

如何分辨父子进程:通过返回值
在这里插入图片描述
父子进程不一定谁先运行,要看cpu调哪个pcb
父进程:

	返回子进程的pid,pid>0

子进程:

返回0

失败:

返回-1

为什么要创建子进程?意义何在?

  1. 分摊压力,cpu资源足够的情况父子进程同时处理数据,效率高
  2. 希望子进程完成其他的任务

进程状态

普遍的系统的三种状态

就绪,运行,阻塞

在这里插入图片描述
Linux进程状态:

  1. 运行态(R)
  2. 可中断睡眠态(S)
  3. 不可中断睡眠态(D)
  4. 停止态(T)
  5. 僵死态(Z)
  6. 死亡态(X)
  7. 追踪态(t)

在这里插入图片描述
加号代表前台进程

cpu使用率非常高,什么原因?

死循环。

杀死进程

kill  进程ID

普通杀死进程杀不死停止态进程
在这里插入图片描述
要用强杀

kill -9 进程ID

在这里插入图片描述
僵尸进程:
处于僵死态的进程----进程退出后,资源没有完全释放(没有完全退出)
强杀都杀不死
如何产生?
在这里插入图片描述
子进程先于父进程退出,将自己退出原因保存在pcb中,操作系统检测到子进程退出,因为父进程有可能关注退出原因,所以不敢随意释放所有资源,通知父进程子进程的退出,但是这时父进程可能正在打麻将,没有关注到这个通知,导致子进程退出了
但是资源一直没有 释放,处于僵尸进程,处于僵死状态,成为僵尸进程。

危害:资源泄露,一个用户能够创建的进程是有限的,导致新进程创建失败
处理:干掉父进程
如何避免:

进程等待

孤儿进程

父进程先于子进程退出,子进程成为孤儿进程,运行在操作系统后台,父进程成为1号进程(被领养)

孤儿进程的使命就是不断奋斗最后成为守护进程
守护进程/精灵进程

特殊的孤儿进程     一个特殊的孤儿进程(脱离终端,脱离登会话的孤儿进程)

进程优先级

通过一个评级来决定一个进程的cpu资源优先分配权
为了让计算机运行的更加合理
(因为进程的性质各有不同—批处理/交互式)
查看:

ps  -l

修改:优先级无法直接修改,但是可以通过修改NI的值,来调整PRI的值
PRI=PRI+NI
renice程序运行后修改 (nice的范围(-20~19))

	Renic  -n ni_val  -p  pid

nice程序运行时指定

	nice  -n  ni_val  ./main

优先级调整更多的是针对cpu密集型程序(对cpu资源要求比较高)
磁盘密集型程序因为本事呢对cpu资源要求不是很高,因此大多数情况下,没必要调整

在这里插入图片描述
我们很容易注意到其中的几个重要信息,有下:

UID : 代表执行者的身份 
PID : 代表这个进程的代号PPID :代表这个进程是由哪个进程发展衍生而来的,亦即父进程的代号PRI :代表这个进程可被执行的优先级,其值越小越早被执行 NI :代表这个进程的nice值 

PRI and NI

PRI也还是比较好理解的,即进程的优先级,或者通俗点说就是程序被CPU执行的先后顺序,此值越小 进程的优先级别越高那NI呢?就是我们所要说的nice值了,其表示进程可被执行的优先级的修正数值 PRI值越小越快被执行,那么加入nice值后,将会使得PRI变为:PRI(new)=PRI(old)+nice这样,当nice值为负值的时候,那么该程序将会优先级值将变小,即其优先级会变高,则其越快被执行 所以,调整进程优先级,在Linux下,就是调整进程nice值 nice其取值范围是-20至19,一共40个级别需要强调一点的是,进程的nice值不是进程的优先级,他们不是一个概念,但是进程nice值会影响到进 程的优先级变化。可以理解nice值是进程优先级的修正修正数据

用top命令更改已存在进程的nice

top 进入top后按“r”–>输入进程PID–>输入nice的值

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

环境变量

环境变量(environment variables)一般是指在操作系统中用来指定操作系统运行环境的一些参数 如:我们在编写C/C++代码的时候,在链接的时候,从来不知道我们的所链接的动态静态库在哪里,但 是照样可以链接成功,生成可执行程序,原因就是有相关环境变量帮助编译器进行查找。 环境变量通常具有某些特殊用途,还有在系统当中通常具有全局特性 

就是内存解释
和环境变量相关的命令

  1. echo: 显示某个环境变量值
  2. export: 设置一个新的环境变量
  3. env: 显示所有环境变量
  4. unset: 清除环境变量
  5. set: 显示本地定义的shell变量和环境变量

环境变量的组织方式
在这里插入图片描述
每个程序都会收到一张环境表,环境表是一个字符指针数组,每个指针指向一个以’\0’结尾的环境字符串
常见环境变量:HOME SHLL PATH

通过第三方变量environ获取
**int argc 参数个数
char argv[] 字符串指针数组放的是参数
char env[] 这个字符串指针数组所保存的就是环境变量

#include <stdio.h>int main(int argc, char *argv[], char *env[]){    int i = 0;   for(; env[i]; i++){        printf("%s\n", env[i]);    }    return 0; }

通过系统调用获取或设置环境变量

#include <stdio.h> #include <stdlib.h>int main() 
{    printf("%s\n", getenv("PATH"));    
return 0; }

环境变量通常是具有全局属性的
环境变量通常具有全局属性,可以被子进程继承下去

#include <stdio.h> #include <stdlib.h>

int main() 
{  char * env = getenv("MYENV"); if(env){       printf("%s\n", env);  }    return 0;}

直接查看,发现没有结果,说明该环境变量根本不存在
导出环境变量 export MYENV=“hello world”
再次运行程序,发现结果有了!说明:环境变量是可以被子进程继承下去的!想想为什么?

子进程崩溃了,对shell本身没有影响

程序地址空间

为什么要用虚拟地址空间+页表:保持进程独立性+充分利用内存+内存访问控制
段页式内存管理:段号+段内地址+页内偏移
段式内存管理:段号+段内地址
页式内存管理:页号+页内偏移
在这里插入图片描述
#include <stdio.h> #include <unistd.h> #include <stdlib.h>

int g_val = 0;

int main(){    pid_t id = fork();    if(id < 0){        perror("fork");      return 0;   }else if(id == 0){ //childprintf("child[%d]: %d : %p\n", getpid(), g_val, &g_val);   }else{ //parent       printf("parent[%d]: %d : %p\n", getpid(), g_val, &g_val);   }  sleep(1);  return 0; }

在这里插入图片描述
我们发现,输出出来的变量值和地址是一模一样的,很好理解呀,因为子进程按照父进程为模版,父子并没有对变 量进行进行任何修改。可是将代码稍加改动:

#include <stdio.h>#include <unistd.h>#include <stdlib.h>int g_val = 0;int main() 
{    
pid_t id = fork(); if(id < 0){        perror("fork");      return 0;    }    else if(id == 0){ //child,子进程肯定先跑完,也就是子进程先修改,完成之后,父进程再读取       g_val=100;       printf("child[%d]: %d : %p\n", getpid(), g_val, &g_val);   }else{ //parent       sleep(3);        printf("parent[%d]: %d : %p\n", getpid(), g_val, &g_val);   }    sleep(1);   return 0; }

在这里插入图片描述
我们发现,父子进程,输出地址是一致的,但是变量内容不一样!能得出如下结论

变量内容不一样,所以父子进程输出的变量绝对不是同一个变量
但地址值是一样的,说明,该地址绝对不是物理地址!在Linux地址下,这种地址叫做 虚拟地址 我们在用C/C++语言所看到的地址,全部都是虚拟地址!物理地址,用户一概看不到,由OS统一管理 

OS必须负责将 虚拟地址 转化成 物理地址 。
地址:内存区域的编号
-----进程的虚拟地址空间—内存描述符----mm_struct

操作系统通过mm_struct这个结构体给进程描述了一个虚拟的地址
如何描述:
mm_struct{
ulong size;
ulong code_start;
ulong code_end;
ulong data_start;
ulong data_end;
}
在这里插入图片描述
为什么要使用虚拟地址空间虚拟地址空间+页表
通过页表进行映射,页表可以进行标记,当前地址是可读还是可写
提高内存利用率
对内存访问进行控制
保证进程独立性

虚拟内存的方式
写时拷贝技术:提高子进程创建效率

父进程创建了子进程,但是并没有直接给子进程开辟内存,拷贝数据,
而是跟父进程映射到同一位置,
但是如果内存中数据发生的改变,那么对于改变的这块内存,
需要重新给子进程开辟内存,并且更新页表信息。

进程O(1)调度方法

一个CPU拥有一个runqueue

普通优先级:100~139(我们都是普通的优先级,想想nice值的取值范围,可与之对应!实时优先级:0~99(不关心) 

活动队列
时间片还没有结束的所有进程都按照优先级放在该队列 nr_active: 总共有多少个运行状态的进程

queue[140]: 一个元素就是一个进程队列,相同优先级的进程按照FIFO规则进行排队调度,所以,数组下 标就是优先级!
从该结构中,选择一个最合适的进程,过程是怎么的呢?

  1. 从0下表开始遍历queue[140]
  2. 找到第一个非空队列,该队列必定为优先级最高的队列
  3. 拿到选中队列的第一个进程,开始运行,调度完成!
  4. 遍历queue[140]时间复杂度是常数!但还是太低效了!
    bitmap[5]:一共140个优先级,一共140个进程队列,为了提高查找非空队列的效率,就可以用5*32个 比特位表示队列是否为空,这样,便可以大大提高查找效率
    过期队列
过期队列和活动队列结构一模一样过期队列上放置的进程,都是时间片耗尽的进程 当活动队列上的进程都被处理完毕之后,对过期队列的进程进行时间片重新计算

active指针和expired指针

  1. active指针永远指向活动队列

  2. expired指针永远指向过期队列

  3. 可是活动队列上的进程会越来越少,过期队列上的进程会越来越多,因为进程时间片到期时一直都存在 的。

  4. 没关系,在合适的时候,只要能够交换active指针和expired指针的内容,就相当于有具有了一批新的活 动进程!

在系统当中查找一个最合适调度的进程的时间复杂度是一个常数,不随着进程增多而导致时间成本增 加,我们称之为进程调度O(1)算法

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

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

相关文章

Linux系统编程之进程控制(进程创建,fork函数,进程中止,进程等待,程序替换)

进程创建 fork()------复制&#xff0c;返回值&#xff0c;写时复制 vfork()创建子进程—子进程与父进程共用同一块虚拟地址空间&#xff0c; 为了防止调用栈混乱&#xff0c;因此阻塞父进程直到子进程调用exit&#xff08;&#xff09;退出或者进行程序替换 vfork创建的子…

Linux系统编程下做一个简易的shell

自主实现一个shell--------minshell shell&#xff1a;命令行解释器-------解释执行用户的输入&#xff08;完成相对应的功能&#xff09; 步骤 1. 获取标准输入中的字符串 2. 对字符串进行解析[ls -l -a][ls ] [-l ] [-a] 3. 创建子进程 4. 子进程中进行程序替换 5. 父进程…

C++起始(内联函数,宏的优缺点,const关键字,auto关键字(C++11)基于范围的for循环(C++11). 指针空值nullptr(C++11))

内联函数 概念 以inline修饰的函数叫做内联函数&#xff0c;编译时C编译器会在调用内联函数的地方展开&#xff0c;没有函数压栈的开销&#xff0c; 内联函数提升程序运行的效率 函数前增加inline关键字将其改成内联函数&#xff0c;在编译期间编译器会用函数体替换函数的调用…

数据结构课程设计---c语言实现通讯录(动态扩容+文件存储)

1 题目一 &#xff1a; 通讯录 1.1问题描述 编写一个通讯录管理系统&#xff0c;以把所学数据结构知识应用到实际软件开发中去。每条信息至包含 &#xff1a;姓名&#xff08;NAME &#xff09;街道&#xff08;STREET&#xff09;城市&#xff08;CITY&#xff09;邮编&#…

数据结构课程设计------c实现散列表(二次探测再哈希)电话簿(文件存储)

题目二 &#xff1a;散列表的设计与实现 2.1问题描述 设计散列表实现电话号码查找系统&#xff0c;使得平均查找长度不超过2基本要求 &#xff08;1&#xff09;设每个记录有下列数据项&#xff1a;电话号码、用户名、地址&#xff1b; &#xff08;2&#xff09;从键盘输入各…

科技论文----论搜索引擎现状及发展趋势

搜索引擎现状及发展趋势 【摘要】 随着最近10年中国互联网的快速发展菜互联网已经彻底改变了人们的生活方式&#xff0c;而在互联网的发展过程中。搜索引擎发挥了巨大的推动作用。本文对搜索引擎的发展历史采用的技术&#xff0c;发展现状出现的问题以及未来发展方向进行了综述…

数据结构课程设计------扫雷游戏(升级版,可展开)

本程序由团队中的一个人所写&#xff0c;本人看懂并写下此文章 题目&#xff1a;扫雷 3.1问题描述 扫雷游戏 [基本要求] &#xff08;1&#xff09;完成棋盘的初始化并在标准显示器中显示 &#xff08;2&#xff09;通过输入行列值确定用户输入 &#xff08;3&#xff09;游…

C语言的编译链接过程的介绍

发布时间: 2012-11-08 10:17 作者: 未知 来源: 51Testing软件测试网采编 字体: 小 中 大 | 上一篇 下一篇 | 打印 | 我要投稿 | 推荐标签&#xff1a; DotNet 软件开发 | 感言十年 C语言的编译链接过程要把我们编写的一个c程序&#xff08;源代码&#x…

技术与技巧札记

Linux常用命令及技巧&#xff1a; &#xff08;1&#xff09;cat /proc/version 查看当前内核的版本 (2) 挂载nfs文件夹&#xff1a;需要先确认在&#xff0f;etc&#xff0f;exports文件&#xff0c;可以用于开发板挂载的文件夹 mount -o nolock 10.0.22.30:/root/sharednfs …

从0开始python学习-35.allure报告企业定制

目录 1. 搭建allure环境 2. 生成报告 3. logo定制 4. 企业级报告内容或层级定制 5. allure局域网查看 1. 搭建allure环境 1.1 JDK&#xff0c;使用PyCharm 找到pycharm安装目录找到java.exe记下jbr目录的完整路径&#xff0c;eg: C:\Program Files\JetBrains\PyCharm Com…

系统架构札记

什么是高内聚、低耦合&#xff1f; 起因&#xff1a;模块独立性指每个模块只完成系统要求的独立子功能&#xff0c;并且与其他模块的联系最少且接口简单&#xff0c;两个定性的度量标准――耦合性和内聚性。 耦合性也称块间联系。指软件系统结构中各模块间相互联系紧密程度的一…

c++中的继承--1(引出,继承方式,继承的对象模型)

继承的引出 概念&#xff1a; 继承(inheritance)机制是面向对象程序设计使代码可以复用的最重要的手段&#xff0c;它允许程序员在保持原有类特 性的基础上进行扩展&#xff0c;增加功能&#xff0c;这样产生新的类&#xff0c;称派生类。继承呈现了面向对象程序设计的层次结构…

c++中的多态---1(多态概念,静态联编和动态联编,多态原理解析,重载,重写,重定义的对比)

多态的基本概念 多态是面向对象设计语言数据抽象和继承之外的第三个基本特征多态性(polymorphism)提供接口与具体实现之间的另一层隔膜&#xff0c;从而将“what”和“how”分离开来&#xff0c;多态性改善了代码的可读和组织性&#xff0c;同时也使创建的程序具有可扩展性&am…

Ubuntu下各种服务搭建及操作技巧

Ubuntu下搭建TFTP 1、安装软件包 sudo apt-get install tftpd tftp xinetd 2、建立配置文件 在/etc/xinetd.d/下建立一个配置文件tftp sudo vi /etc/xinetd.d/tftp 内容如下 service tftp { socket_type dgram protocol udp wait yes user root …

使用Automake和Autoconf生成Makefile

automake 所产生的 Makefile 除了可以做到程序的自动编译和链接 外&#xff0c;还可以用来生成各种文档&#xff08;如manual page、info文件&#xff09;&#xff0c;可以将源代码文件包装起来以供发布。所以程序源代码所存放的目录 结构最好符合GNU的标准惯例。下面以hello.…

c++中多态---3(虚析构和纯虚析构,向上类型转化和向下类型转化)

虚析构和纯虚析构 虚析构virtual ~类名(){}类内声明&#xff0c;类内实现解决问题&#xff1a;通过父类指针指向子类对象释放时候不干净的问题 纯虚析构 写法 virtual ~类名(){}0; 类内声明 类外实现 如果出现了纯虚析构函数&#xff0c;这个类也算是抽象类&#xff0c;不可…

嵌入式开发硬件知识札记

三态逻辑 1. 概念 三态指其输出既可以是一般二值逻辑电路&#xff0c;即正常的高电平&#xff08;逻辑1&#xff09;或低电平&#xff08;逻辑0&#xff09;&#xff0c;又可以保持特有的高阻抗状态。高阻态相当于隔断状态&#xff08;电阻很大&#xff0c;相当于开路&#xff…

《凡人修仙传》中打斗场景(c++多态实现)

我们 要实现打斗场景&#xff0c;第一&#xff0c;我们需要有打斗的双方&#xff0c;一个是英雄&#xff0c;一个是怪物&#xff0c;他们都有自己的属性&#xff0c;比如攻击&#xff0c;防御&#xff0c;血量。其次我们的英雄还会有武器。武器上有一些加成属性&#xff0c;可以…

c++中的文件读写的操作

写文件 ofstreamopen指定打开方式isopen判断是否打开成功ifs<<“数据”ofs.close&#xff08;&#xff09; 读文件 ifstream ifs 指定打开方式ios::in isopen判断是否打开成功 读取有三种方式 #include<iostream>using namespace std;//文件读写头文件#incl…

c++的STL--1概念通述

STL的概念 什么是STL? STL(standard template libaray-标准模板库)&#xff1a;是C标准库的重要组成部分&#xff0c;不仅是一个可复用的组件库&#xff0c;而且 是一个包罗数据结构与算法的软件框架。 STL从广义上分为&#xff1a;容器(container)&#xff0c;算法(algorit…