当前位置: 首页 > news >正文

操作系统:进程1

一.进程

1.什么是进程

一个进程创建,他会生成几块:

  • 代码段:进程执行的程序代码
  • 数据段:全局变量,静态变量,在进程生命周期中是动态可变的
  • 堆:动态分配的内存区域,malloc、calloc、realloc等函数进行开辟
  • 栈:用于存储局部变量、函数参数和返回地址
  • PCB进程控制块:包含进程ID(PID),进程状态,程序计数器(吓一条指令的位置),进程优先级等等

进程是资源分配和调度的基本单位

进程是资源分配的最小单位

线程是最小的调度单位

2.进程中的三态模型

  • 阻塞态:这个进程需要相应的资源,而现在需要等待相应的资源,那么这个进程就进入到阻塞态,CPU会将这个进程给拿下CPU,去执行就绪在就绪队列中的第一个进程。
  • 就绪态:一个进程需要运行,并且他所有的需求都满足了,就等待CPU去执行,那么这个进程会在这个就绪队列中进行排队,等待CPU的调度执行
  • 运行态:这个进程在CPU上执行

挂起

  • 挂起:在挂起这个进程时,会将这些运行在内存上的内容给写入到磁盘中或交换区中,然后释放内存,在记录上下文(记录上下文的作用就是,为了让你回到在你挂起这个进程前那个状态,和玩游戏存档的意思差不多)
  • 唤醒:将刚刚写入到磁盘的内容,又写入到内存中,到达就绪态,等待操作系统的调度

进程详细的五态

对于3态模型,只多了创建态和终止态。

3.进程的控制 

如何实现的原子性?

在原语时,执行关中断指令,将中断关闭掉,这样CPU就不会中断这个原语的执行,在执行完成原语后,执行开中断指令,将中断打开。这样就实现了原语的原子性,在执行原语的过程中就不会被中断。

4.调度的基本概念

        为什么需要了解如何进行调度?

        当有一堆任务需要处理,但由于资源有限,这些事情没有同时处理。这就需要确定某种规则来决定处理这些任务的顺序,这就是”调度“研究”的问题

        能否进行调度切换进程的情况:

        进行切换是有代价的,如果过度频繁进行调度、切换,会使系统的效率降低。

 调度算法:

1.时间片轮转算法

        给每个进入到就绪队列中的进程分配一个时间片,这个时间片的大小需要写程序的我们来决定,不能太短,也不能太长。然后每个程序在CPU上执行的时间就是时间片的大小,如果执行完成可以先退出然后让出CPU,如果没有在时间片规定的时间完成,那么也要让出CPU让就绪队列中下一个进程进行执行,然后让出CPU后又到就绪队列中进行排队,等待下一次的CPU调度执行。

2.优先级调度算法

 优先级调度算法分为:

  • 静态:进程在创建时就确定了优先级,这个进程在生命周期中优先级不会改变
  • 动态:在创建时确定了一个优先级,在生命周期的过程中,操作系统会通过规定进行对应的优先级改变
3.多级反馈队列调度算法

  • 特点:进程可以在不同优先级队列之间移动。新进程进入高优先级队列,如果未能在规定时间完成则降级。
  • 优点:兼顾响应时间和吞吐量,适应性强。
  • 缺点:配置参数(如队列数量、时间片等)复杂。
4.短作业优先
  • 特点:优先调度运行时间最短的进程。
  • 优点:可以最小化平均等待时间。
  • 缺点:难以准确预测每个进程的运行时间;可能导致长作业得不到及时处理(即“饥饿”)。
5.最短剩余时间优先
  • 特点:是SJF的抢占式版本。每次调度时选择剩余执行时间最短的进程。
  • 优点:可以进一步减少平均等待时间。
  • 缺点:和SJF一样,难以预测执行时间,并且会导致频繁的上下文切换。

  对比短作业优先就是,假如P1进程需要执行时间是10s,P2执行时间是20S,现在执行P1已近5s了,来了一个新进程P3执行是减是3S,那么CPU会将P1给拿下放入到就绪队列中,来执行P3。短作业优先就是P1执行完成了才执行P3

6.先来先服务
  • 特点:按进程到达就绪队列的顺序进行调度,先到达的进程先调度。
  • 优点:简单易实现。
  • 缺点:可能导致“等待时间长”的问题(即“队头阻塞”)。

5.进程拷贝

        进程拷贝时,他们会共享物理内存页面,但是这些内存页面现在是只读的。

        其中一个进程需要堆这个内存页面进行写入时,那么就会创建该内存页的副本,然后给该进程附上可写的权限。

        这样可以节省系统资源,防止创建一个子进程就立刻复制整个地址空间,减少不必要的空间。        

6.创建进程

fork函数
#include <sys/types.h>
#include <unistd.h>pid_t fork(void);

fork函数创建子进程的关键点:

  • 共享资源:子进程继承了父进程的大部分资源,包括打开的文件描述符、环境变量、优先级、控制终端等。
  • 独立地址空间:虽然子进程最初共享父进程的地址空间,但它们有独立的地址空间,修改内存不会影响对方。
  • 执行路径fork() 返回值用于区分父子进程。子进程返回 0,父进程返回子进程的 PID。
  • 进程调度:子进程和父进程是并行执行的,具体谁先执行由操作系统调度决定。

例子:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>int main() {int n;//创建子进程pid_t child_pid = fork();if (child_pid < 0) {perror("fork");exit(1);}//当child_pid为04时说明是子进程if (child_pid == 0) {n = 6;while (n > 0) {printf("child self = %d, parent = %d\n", getpid(), getppid());n--;}} //不为0说明是父进程else {n = 3;while (n > 0) {printf("parent self = %d, parent = %d\n", getpid(), getppid());n--;}}return 0;
}

       你会发现每次输出的结果都是不一样的。

 孤儿进程 

        那么像这种情况就是父进程先执行完成了,子进程还在执行,但是没有父进程为子进程收尸(回收资源),那么这个子进程就叫做孤儿进程,那么谁会为他收尸呢,也就是我们系统最初的那个进程0号进程。也叫做init进程(或者现代系统的systemd)接管,我们也可以称它为孤儿院。

僵尸进程

        当一个进程终止后,其父进程还没有读取它的退出状态,这个进程就被称为僵尸进程。

        如何处理我们调用wait()或者waitpid()来读取子进程的状态,来等待子进程的退出状态,这样就可以为子进程进行收尸。

wait和waitpid()
pid_t wait(int *status);pid_t waitpid(pid_t pid, int *status, int options);

wait:

  • 功能wait 阻塞调用进程,直到其某个子进程终止。如果一个子进程已经终止,那么 wait 会立即返回。
  • 参数status 是一个指向整数的指针,用于存储子进程的退出状态。如果 statusNULL,则退出状态会被丢弃。
  • 返回值:返回终止的子进程的进程 ID。如果调用出错(例如,没有子进程),则返回 -1 并设置 errno

waitpid:

  • 功能waitpid 提供了比 wait 更细粒度的控制,允许父进程等待特定的子进程终止,或者以非阻塞方式等待。
  • 参数
    • pid:指定要等待的子进程 ID。如果 pid 为 -1,则等待任何子进程(等同于 wait)。
    • status:同 wait,用于存储子进程的退出状态。
    • options:提供额外的选项控制,如 WNOHANG(非阻塞方式)和 WUNTRACED(也会返回已停止的子进程)。
  • 返回值:成功时返回子进程的 PID,失败时返回 -1 并设置 errno

        如何获取退出状态,可以在Linux系统中的man手册查看。

接着上面那个例子来进行优化:
 

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <wait.h>int main() {int n;int state;//创建子进程//在子进程中child_pid 为0//父进程为fork的返回值pid_t child_pid = fork();if (child_pid < 0) {perror("fork");exit(1);}//当child_pid为04时说明是子进程if (child_pid == 0) {n = 6;while (n > 0) {printf("child self = %d, parent = %d\n", getpid(), getppid());n--;}} //不为0说明是父进程else {n = 3;while (n > 0) {printf("parent self = %d, parent = %d\n", getpid(), getppid());n--;}int ret = wait(&state);if (ret < 0) {perror("wait error");exit(1);}//看通过stata判断子进程是否正常退出if (WIFEXITED(state)) {//正常退出输出子进程PID和退出状态printf("child process %d is sucess exit %d\n", child_pid, WEXITSTATUS(state));} else {//不正常输出子进程PIDprintf("child process %d is not sucess exit\n", child_pid);}}return 0;
}

 

 

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • liteos定时器回调时间过长造成死机问题解决思路
  • 拓扑排序与有向无环图 -- Kahn算法和深度优先搜索
  • Redis - SpringDataRedis - RedisTemplate
  • QT Creator下载安装详细教程(保姆级教程)
  • NCRE3 2-1 网络总体设计基本方法
  • 如何使用 API 查看极狐GitLab 镜像仓库中的镜像?
  • Flutter Geocoding插件使用指南:简化地理编码与逆地理编码
  • Redis与MySQL数据一致性问题的策略模式及解决方案
  • 如何从网站获取表格数据
  • 第四十八天 第十章 单调栈part01 739. 每日温度 496.下一个更大元素 I 503.下一个更大元素II
  • TypeScript通过MsgPack发送数组到C++反序列化失败
  • 前端播放rtsp视频流(最后使用WebRtc)
  • MySQL环境的配置文件json
  • Redis zset 共享对象
  • OpenSNN推文:百度沈抖:深度拥抱人工智能+,加速发展新质生产力,共创智能时代新未来
  • [PHP内核探索]PHP中的哈希表
  • 2017-08-04 前端日报
  • Consul Config 使用Git做版本控制的实现
  • JavaScript标准库系列——Math对象和Date对象(二)
  • JS基础篇--通过JS生成由字母与数字组合的随机字符串
  • Mocha测试初探
  • node和express搭建代理服务器(源码)
  • Tornado学习笔记(1)
  • Vue全家桶实现一个Web App
  • 从零开始的webpack生活-0x009:FilesLoader装载文件
  • 利用jquery编写加法运算验证码
  • 如何用Ubuntu和Xen来设置Kubernetes?
  • 如何优雅的使用vue+Dcloud(Hbuild)开发混合app
  • 使用Maven插件构建SpringBoot项目,生成Docker镜像push到DockerHub上
  • 想写好前端,先练好内功
  • 协程
  • 写给高年级小学生看的《Bash 指南》
  • 新书推荐|Windows黑客编程技术详解
  • 学习HTTP相关知识笔记
  • 移动端唤起键盘时取消position:fixed定位
  • 【干货分享】dos命令大全
  • (27)4.8 习题课
  • (zt)基于Facebook和Flash平台的应用架构解析
  • (第三期)书生大模型实战营——InternVL(冷笑话大师)部署微调实践
  • (二刷)代码随想录第15天|层序遍历 226.翻转二叉树 101.对称二叉树2
  • (附源码)基于ssm的模具配件账单管理系统 毕业设计 081848
  • (十七)Flink 容错机制
  • (算法二)滑动窗口
  • (转)母版页和相对路径
  • ****** 二 ******、软设笔记【数据结构】-KMP算法、树、二叉树
  • .bat批处理出现中文乱码的情况
  • .form文件_一篇文章学会文件上传
  • .md即markdown文件的基本常用编写语法
  • .NET Conf 2023 回顾 – 庆祝社区、创新和 .NET 8 的发布
  • .Net Core 中间件与过滤器
  • .Net(C#)常用转换byte转uint32、byte转float等
  • .netcore 6.0/7.0项目迁移至.netcore 8.0 注意事项
  • .NET平台开源项目速览(15)文档数据库RavenDB-介绍与初体验
  • .sh 的运行
  • ::