操作系统:进程1

devtools/2024/12/22 20:06:33/

一.进程

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;
}

 

 


http://www.ppmy.cn/devtools/85393.html

相关文章

Unity Canvas动画:UI元素的动态展示

在Unity中&#xff0c;Canvas是用于管理和展示用户界面&#xff08;UI&#xff09;元素的系统。Canvas动画是UI设计中的重要组成部分&#xff0c;它能够提升用户体验&#xff0c;使界面更加生动和响应用户操作。本文将探讨Unity Canvas动画的基本概念、实现方法以及一些实用的技…

HDShredder 7 企业版案例分享: 依照国际权威标准,安全清除企业数据

HDShredder 7 企业版用户案例 天津鸿萌科贸发展有限公司是德国 Miray 公司 HDShredder 数据清除软件的授权代理商。近日&#xff0c;上海某网络科技有限公司采购 HDShredder 7 企业版x4&#xff0c;为公司数据存储资产的安全清除工作流程配备高效的执行工具。HDShredder 7 企业…

LC 128.最长连续序列

128.最长连续序列 给定一个未排序的整数数组 nums &#xff0c;找出数字连续的最长序列&#xff08;不要求序列元素在原数组中连续&#xff09;的长度。 请你设计并实现时间复杂度为 O(n) 的算法解决此问题。 示例 1&#xff1a; 输入&#xff1a; nums [100,4,200,1,3,2]…

防火墙——网络环境支持

目录 网络环境支持 防火墙的组网 web连接上防火墙 web管理口 让防火墙接到网络环境中 ​编辑 管理员用户管理 缺省管理员 接口 配置一个普通接口 创建安全区域 路由模式 透明模式 混合模式 防火墙的安全策略 防火墙转发流程 与传统包过滤的区别 创建安全策略 …

【BUG】已解决:Downgrade the protobuf package to 3.20.x or lower.

Downgrade the protobuf package to 3.20.x or lower. 目录 Downgrade the protobuf package to 3.20.x or lower. 【常见模块错误】 【解决方案】 欢迎来到英杰社区https://bbs.csdn.net/topics/617804998 欢迎来到我的主页&#xff0c;我是博主英杰&#xff0c;211科班出身…

会员购项目面试题解析:高效数据抓取与异常处理

会员购项目 亮点 日志记录信息协程异步抓取数据&#xff0c;大大提高抓取速度捕获异常&#xff0c;并添加重试机制 源码 import logging import timeimport requests import asyncio import aiohttp from aiohttp import ContentTypeError import csv# 配置日志 logging.ba…

47、PHP实现机器人的运动范围

题目&#xff1a; PHP 实现机器人的运动范围 描述&#xff1a; 地上有一个m行和n列的方格。一个机器人从坐标0,0的格子开始移动&#xff0c;每一次只能向左&#xff0c;右&#xff0c;上&#xff0c;下四个方向移动一格&#xff0c;但是不能进入行坐标和列坐标的数位之和大于k…

汽车免拆诊断案例 | 2018 款别克阅朗车蓄电池偶尔亏电

故障现象 一辆2018款别克阅朗车&#xff0c;搭载LI6发动机和GF6变速器&#xff0c;累计行驶里程约为9.6万km。车主反映&#xff0c;该车停放一晚后&#xff0c;蓄电池偶尔亏电。 故障诊断 接车后用虹科Pico汽车示波器和高精度电流钳&#xff08;30 A&#xff09;测量该车的寄…