Linux进程 -fork(初识),进程状态和进程优先级
目录
一、通过系统调用创建进程-fork
1.fork的介绍
2.fork的理解
3.fork常规用法
4.fork的三个问题
5.创建多个子进程
二、进程状态
(1)Linux内核源代码
(2)进程的状态
R运行状态(运行态)
S 睡眠状态(sleeping)和 D 磁盘休眠状态(disk sleep)
T 停止状态(stopped)
Z 僵尸状态(zombie)-- 僵尸进程
孤儿进程
三、进程优先级
(1)基本概念
(2)查看进程优先级的命令
一、通过系统调用创建进程-fork
1.fork的介绍
平时创建进程一般是通过 ./myproc 运行某个存储在磁盘上的可执行程序来创建。而我们还可以通过系统调用接口来创建进程。
pid_t是无符号整型。我们先看一段代码。
1 #include<stdio.h>2 #include <unistd.h>3 int main()4 {5 printf("I am a father: %u\n", getpid());6 fork();7 8 while(1)9 {10 printf("I am a process, pid: %u, ppid: %u\n", getpid(), getppid());11 sleep(1);12 }13 14 return 0; 15 }
pid为31318即为创建的子进程id
当我们查看进程
此时有三个进程:分别为父进程和fork创建的子进程和grep进程
2.fork的理解
从代码的角度看
父子进程共享用户代码(代码是只读的,不可写),而用户数据各自私有一份(为了不让进程互相干扰),采用写时拷贝技术。
fork 之后子进程会被创建成功,然后父子进程都会继续运行,但谁先运行是不确定的,由系统调度优先级决定。
从内核的角度看
对于操作系统来说,通过fork后,系统多了一个进程。
具体是,fork后以父进程为模板,操作系统创建新的PCB,把父进程PCB的内容属性拷贝过来,他们共享代码和数据。
3.fork常规用法
我们创建子进程的目的是为了让子进程给我们完成任务,所以 fork 之后通常要用 if 进行分流,让父子进程执行不同的代码,实现一个并行的效果。(比如父进程播放音乐,子进程下载文件)
通过 fork 的两个返回值来进行分流:
- 如果 fork 执行成功,在父进程中返回子进程的 pid,在子进程中返回 0。
- 如果 fork 执行失败,在父进程中返回 -1,不创建子进程,并适当地设置 errno。
#include <stdio.h>
#include <sys/types.h> // getpid, getppid
#include <unistd.h> // getpid, getppid, forkint main()
{ printf("I'm a father: %u\n", getpid());pid_t ret = fork();if (ret == 0){ // child processwhile (1){printf("child process, pid:%u, ppid:%u\n", getpid(), getppid());sleep(1);}}else if (ret > 0){// father processwhile (1){printf("father process, pid:%u, ppid:%u\n", getpid(), getppid());sleep(1);}}else{// failureperror("fork");return 1;}return 0;
}
这一份代码为什么会出现父进程和子进程一起循环呢?
这里给大家抛出三个问题
- fork为什么有两个返回值?
- 为什么上述代码中,fork 的返回值 ret 有两个值,既等于 0 又大于 0 呢?fork 之后,父子进程如何做到共享用户代码,如何做到用户数据各自私有的呢?
- 如果 fork 执行成功,为什么在父进程中返回子进程的 pid,在子进程中返回的是 0 呢?
4.fork的三个问题
(1)两个返回值问题
fork函数一直往下执行
- 在执行到最后ret之前,子进程已经被创建出来了,在上面我们说父进程和子进程的代码是共享的,那么这个return ret是不是一份代码呢?
- 答案肯定是的,那么是代码子进程也会执行return ret。
- 所以这就是为什么有两个返回值
(2)一个变量为什么会存在两个值呢?
这个在我们后面讲进程地址空间的时候会给大家介绍,暂时不多做解释。
(3) 为什么在父进程中返回子进程的 pid,在子进程中返回的是 0 呢?
举一个例子,一位父亲有很多孩子,那么该怎么辨别这些孩子呢?这就需要给孩子标识并记住它。而每个孩子只有唯一一个父亲,所以能很好的辨别父亲。
所以在父进程中需要返回子进程的 pid,因为得让父进程知道自己的子进程(儿子)是谁。
而子进程只需要知道自己被创建成功了就行,所以在子进程中返回 0 即可。
5.创建多个子进程
#include <stdlib.h>68 void runchild()69 {70 int cnt=10;71 while(cnt)72 {73 printf("i am a child:%d,ppid:%d",getpid(),getppid());74 sleep(1);75 cnt--;76 }77 }78 int main()79 {80 int i=0; 81 for(i=0;i<5;i++)82 {83 pid_t id=fork();84 if(id==0)85 {86 runchild();87 exit(0);88 }89 sleep(100);90 }91 }
二、进程状态
进程的状态体现一个进程的生命状态。
(1)Linux内核源代码
static const char * const task_state_array[] = {
"R (running)", /* 0 */
"S (sleeping)", /* 1 */
"D (disk sleep)", /* 2 */
"T (stopped)", /* 4 */
"t (tracing stop)", /* 8 */
"X (dead)", /* 16 */
"Z (zombie)", /* 32 */
};
- R运行状态(running): 并不意味着进程一定在运行中,它表明进程要么是在运行中要么在运行队列里。
- S睡眠状态(sleeping): 意味着进程在等待事件完成(这里的睡眠有时候也叫做可中断睡眠 (interruptible sleep)。
- D磁盘休眠状态(Disk sleep)有时候也叫不可中断睡眠状态(uninterruptible sleep),在这个状态的进程通常会等待IO的结束。
- T停止状态(stopped): 可以通过发送 SIGSTOP 信号给进程来停止(T)进程。这个被暂停的进程可以通过发送 SIGCONT 信号让进程继续运行。
- X死亡状态(dead):这个状态只是一个返回状态,你不会在任务列表里看到这个状态
(2)进程的状态
R运行状态(运行态)
当我们写一段代码,为什么状态时是S+而不是R呢?
- 因为CPU的运行速度非常快,而进程绝大多数时间都在休眠(sleep(1)),只有极少数的时间在运行 。
- 因为 printf 是往显示器上打印,涉及到 IO,所以效率比较低,该进程需要等待操作系统把数据刷新到显示器中。
那有一个问题了?如果该进程的状态是R,那它一定在CPU上运行吗?
不一定,在CPU上运行一定是R状态,但一个进程状态是R,也有可能在运行队列中。
如果想看到R状态,我们只需要写一个while(1)即可。
S 睡眠状态(sleeping)和 D 磁盘休眠状态(disk sleep)
S:休眠状态(sleeping)(也可以叫阻塞状态)
- 表示进程虽然是一种休眠状态,但随时可以接受外部的信号,处理外部的请求,被唤醒。
当键盘还未输入数据时,却在内存中,如图在等待队列中等待,可以叫做阻塞状态,可以随时接受外接的信号,被唤醒。
当在等待队列中,如果操作系统内部的内存资源严重不足时,在保证正常的情况,队列中只存在PCB,把对应的代码和数据返回到外设中,当存在响应的时候,再把代码和数据换入,再放到运行队列中。这种状态叫做挂起状态
D:磁盘休眠状态(disk sleep)(深度休眠)
比如:进程 A 想要把一些数据写入磁盘中,因为 IO 需要时间,所以进程 A 需要等待。但因为内存资源不足,在等待期间进程 A 被操作系统 kill 掉了,而此时磁盘因为空间不足,写入这些数据失败了,却不能把情况汇报给进程 A,那这些数据该如何处理呢?很可能导致这些数据被丢失,操作系统 kill 掉进程 A 导致了此次事故的发生。所以诞生了 D 状态,不可以被杀掉,即便是操作系统。只能等待 D 状态自动醒来,或者是关机重启
S状态和S+状态有什么区别呢?
S+ 状态:表示前台进程。(前台进程一旦运行,bash 就无法进行命令行解释,使用 Ctrl+C 可以终止前台进程)
S 状态:表示后台进程。(后台进程在运行时,bash 可以进行命令行解释,使用 Ctrl+C 无法终止后台进程)
T 停止状态(stopped)
我们可以通过kill命令,让进程进入T状态也就是停止状态,停止运行了。
举个例子:
-
我们给进程发 19 号信号 SIGSTOP,可以让进程进入 T 停止状态。停止运行。
-
我们给进程发 18 号信号 SIGCONT,可以让进程停止 T 停止状态。恢复运行。
Z 僵尸状态(zombie)-- 僵尸进程
我们先看一段代码
#include <stdio.h>
#include <stdlib.h> // exit
#include <sys/types.h> // getpid, getppid
#include <unistd.h> // getpid, getppid, fork, sleepint main()
{// 创建5个子进程for (int i = 0; i < 5; i++){pid_t ret = fork();if (ret == 0){// child processprintf("child%d, pid:%u, ppid:%u\n", i, getpid(), getppid());sleep(1);exit(1); // 子进程退出}}getchar(); // getchar()目的是不让父进程退出,则无法回收子进程。return 0;
}
成功创建了 5 个子进程。但程序会一直卡在这里,不会自己退出。
我们发现五个子进程全部变僵尸进程了(Z状态)
那什么是僵尸状态呢?
要知道,进程退出,一般不是立马就让操作系统回收进程的所有资源。
因为创建进程的目的,是为了让它完成某个任务和工作。当它退出时,我们得知道它把任务完成的怎么样,所以需要知道这个进程是正常还是异常退出的。
如果进程是正常退出的,那么交给进程的任务有没有正常完成呢?
所以,进程退出时,会自动将自己的退出信息,保存到进程的 PCB 中,供 OS 或者父进程来进行读取。
进程退出但父进程还没有读取,进程此时就处于僵尸状态。
读取成功后,该进程才算是真正的死亡,变成 X 死亡状态。
僵尸状态的概念:
- 僵死状态(Zombies)是一个比较特殊的状态。当子进程退出,并且父进程没有读取到子进程退出时的返回代码时就会产生僵死(尸)进程。(父进程使用系统调用 wait() 让 OS 回收子进程)
- 僵死进程会以终止状态保持在进程表中,并且会一直在等待父进程读取退出代码。
- 所以,只要子进程退出,父进程还在运行,但父进程没有读取到子进程状态,子进程就会进入 Z 状态。
- 父进程通过调用 getchar() 函数来等待用户输入,这样做可以防止父进程过早退出,在大多数情况下,这也意味着父进程不会立即回收结束的子进程资源,因为没有调用 wait / waitpid 函数来等待子进程结束。
- 虽然父进程通过 getchar() 等待,但这并不是处理僵尸进程(已结束但未被父进程回收的子进程)的正确做法。在实际应用中,父进程应该使用 wait / waitpid 函数来等待子进程结束,并回收它们的资源,以避免僵尸进程的产生。
- 进程处于Z状态,资源会被一直占用,进程相关资源task_struct不能被释放,导致内存泄漏。等后面给大家介绍进程等待能很好的解决内存泄露问题。
孤儿进程
若子进程先退出,父进程没回收,则子进程为僵尸进程。
若父进程先退出,子进程将被1号进程领养(父进程改为1号进程),子进程称作孤儿进程。
我们能看到,父进程退出后,子进程的父进程变成了1。
我们能看到1号进程就是我们的操作系统。
三、进程优先级
优先级 vs 权限,两者有什么区别呢?
- 优先级:在资源有限的前提下,确立多个进程中谁先访问资源,谁后访问资源。
- 权限:决定能不能得到某种资源。
(1)基本概念
在 Linux 或者 Unix 系统中,使用命令 ps -al 查看当前系统进程的信息:
- PRI:优先级,值越小,优先级越大。
- NI:nice,进程优先级的修正数据,范围调整[-20,19]。
- UID:用户的ID名,执行者ID。
- 进程新的优先级:PRI(new) = PRI(old, 默认都是 80) + nice
- 优先级不可能一味的高,也不可能一味的低。因为 OS 的调度器也要考虑公平问题。
- 进程的 nice 值不是进程的优先级,他们不是一个概念,但是进程的 nice 值会影响到进程的优先级变化。
(2)查看进程优先级的命令
通过 top 命令(类似于 Windows 的任务管理器)更改已存在进程的 nice:
- 执行 top 命令后,按 r 键,输入进程的 PID,输入 nice 值。
每次输入 nice 值调整进程优先级,都是默认从 PRI = 80 开始调整的。
输入的 nice 值如果超过 [-20, 19] 这个范围,默认是按照最左/最右范围来取的。
为什么每次都要默认从 PRI = 80 开始调整呢?
- 有一个基准值,方便调整。
- 在设计上,实现比较简单。
为什么 nice 值的范围是 [-20, 19] 呢?
是一种可控状态,保证了进程的优先级始终在 [60, 99] 这个范围内,保证了 OS 调度器的公平。但公平并不是平均。根据每个进程的特性尽可能公平的去调度它们,而不是指每个进程的调度时间必须完全一样。
- 竞争性:系统进程数目众多,而 CPU 的资源很少,甚至只有一个,所以进程之间是具有竞争属性的。为了更高效的完成任务,更合理的竞争相关资源,便有了优先级。
- 独立性:多进程运行,需要独享各种资源,多进程运行期间互不干扰。(这也是 OS 设计进程的一个原则)
- 并发:多个进程在一个 CPU 下采用进程切换的方式,在同一段时间内,让多个进程都得以推进。(描述的时间段)
- 并行:多个进程在多个 CPU 下同时运行。(描述的是时刻,任何一个时刻,都可能有多个进程在运行)
相关文章:

Linux进程 -fork(初识),进程状态和进程优先级
目录 一、通过系统调用创建进程-fork 1.fork的介绍 2.fork的理解 3.fork常规用法 4.fork的三个问题 5.创建多个子进程 二、进程状态 (1)Linux内核源代码 (2)进程的状态 R运行状态(运行态) S 睡眠状态&…...

数据从前端传到后端入库过程分析
数据从前端传到后端入库过程分析 概述 积累了一些项目经验,成长为一个老程序员了,自认为对各种业务和技术都能得心应手的应对了,殊不知很多时候我们借助了搜索引擎的能力,当然现在大家都是通过AI来武装自己。 今天要分析的话题是…...

macOS如何进入 Application Support 目录(cd: string not in pwd: Application)
错误信息 cd: string not in pwd: Application 表示在当前目录下找不到名为 Application Support 的目录。可能的原因如下: 拼写错误或路径错误:确保你输入的目录名称正确。目录名称是区分大小写的,因此请确保使用正确的大小写。正确的目录名…...

第38周:猫狗识别 (Tensorflow实战第八周)
目录 前言 一、前期工作 1.1 设置GPU 1.2 导入数据 输出 二、数据预处理 2.1 加载数据 2.2 再次检查数据 2.3 配置数据集 2.4 可视化数据 三、构建VGG-16网络 3.1 VGG-16网络介绍 3.2 搭建VGG-16模型 四、编译 五、训练模型 六、模型评估 七、预测 总结 前言…...

【2024年华为OD机试】 (A卷,200分)- 计算网络信号、信号强度(JavaScriptJava PythonC/C++)
一、问题描述 题目解析 问题描述 我们有一个 m x n 的二维网格地图,每个格子可能是以下几种情况之一: 0:表示该位置是空旷的。x(正整数):表示该位置是信号源,信号强度为 x。-1:表示该位置是阻隔物,信号无法直接穿透。信号源只有一个,阻隔物可能有多个。信号在传播…...

【go语言】数组和切片
一、数组 1.1 什么是数组 数组是一组数:数组需要是相同类型的数据的集合;数组是需要定义大小的;数组一旦定义了大小是不可以改变的。 1.2 数组的声明 数组和其他变量定义没有什么区别,唯一的就是这个是一组数,需要给…...

2025美赛MCM数学建模A题:《石头台阶的“记忆”:如何用数学揭开历史的足迹》(全网最全思路+模型)
✨个人主页欢迎您的访问 ✨期待您的三连 ✨ 《石头台阶的“记忆”:如何用数学揭开历史的足迹》 目录 《石头台阶的“记忆”:如何用数学揭开历史的足迹》 ✨摘要✨ ✨引言✨ 1. 引言的结构 2. 撰写步骤 (1)研究背景 &#…...

使用 Docker Compose 一键启动 Redis、MySQL 和 RabbitMQ
目录 一、Docker Compose 简介 二、服务配置详解 1. Redis 配置 2. MySQL 配置 3. RabbitMQ 配置 三、数据持久化与时间同步 四、部署与管理 五、总结 目录挂载与卷映射的区别 现代软件开发中,微服务架构因其灵活性和可扩展性而备受青睐。为了支持微服务的…...

新增自定义数据功能|UWA Gears V1.0.7
UWA Gears 是UWA最新发布的无SDK性能分析工具。针对移动平台,提供了实时监测和截帧分析功能,帮助您精准定位性能热点,提升应用的整体表现。 本次版本更新新增了自定义数据功能,支持灵活定义和捕获关键性能指标,满足特…...

docker 简要笔记
文章目录 一、前提内容1、docker 环境准备2、docker-compose 环境准备3、流程说明 二、打包 docker 镜像1、基础镜像2、国内镜像源3、基础的dockerfile4、打包镜像 四、构建运行1、docker 部分2、docker-compose 部分2.1、构建docker-compose.yml2.1.1、同目录构建2.1.2、利用镜…...

在Ubuntu上使用Apache+MariaDB安装部署Nextcloud并修改默认存储路径
一、前言 Nextcloud 是一款开源的私有云存储解决方案,允许用户轻松搭建自己的云服务。它不仅支持文件存储和共享,还提供了日历、联系人、任务管理、笔记等丰富的功能。本文将详细介绍如何在 Ubuntu 22.04 LTS 上使用 Apache 和 MariaDB 安装部署 Nextcl…...

【JavaEE】-- 计算机是如何工作的
文章目录 1. 冯诺依曼体系(VonNeumann Architecture)2. CPU 基本工作流程2.1 寄存器(Register)和 内存(RAM)2.2 控制单元 CU(ControlUnit)2.3 指令(Instruction) 3. 操作系统(OperatingSystem)3.1 操作系统的定位3.2 什么是进程/任务(Process…...

政安晨的AI大模型训练实践三:熟悉一下LF训练模型的WebUI
政安晨的个人主页:政安晨 欢迎 👍点赞✍评论⭐收藏 希望政安晨的博客能够对您有所裨益,如有不足之处,欢迎在评论区提出指正! 目录 启动WebUI 微调模型 LLaMA-Factory 支持通过 WebUI 零代码微调大语言模型。 启动Web…...

基于微信小程序的网上订餐管理系统
作者:计算机学姐 开发技术:SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等,“文末源码”。 专栏推荐:前后端分离项目源码、SpringBoot项目源码、Vue项目源码、SSM项目源码、微信小程序源码 精品专栏:…...

科技快讯 | 理想官宣:正式收费!WeChat 港币钱包拓宽商户网络;百川智能发布深度思考模型Baichuan-M1-preview
理想官宣:正式收费! 1月23日,理想汽车宣布,理想超充站超时占用费正式运营。触发超时占用费的条件为充电结束后15分钟内未将充电枪插回充电桩,收费标准为2元/分钟,单次封顶200元。理想汽车将在充电结束的四个…...

【java数据结构】map和set
【java数据结构】map和set 一、Map和Set的概念以及背景1.1 概念1.2 背景1.3 模型 二、Map2.1 Map说明2.2 Map的常用方法 三、Set3.1 Set说明3.2 Set的常用方法 四、Set和Map的关系 博客最后附有整篇博客的全部代码!!! 一、Map和Set的概念以及…...

飞牛NAS安装过程中的docker源问题
采用CloudFlare进行飞牛NAS的远程访问 【安全免费】无需公网IP、端口号,NAS外网访问新方法_网络存储_什么值得买 sudo mkdir -p /etc/docker sudo tee /etc/docker/daemon.json <<EOF {"registry-mirrors": ["https://docker.1panel.dev&quo…...

Linux(Centos 7.6)命令详解:dos2unix
1.命令安装 dos2unix 命令默认情况下是没有安装的,如配置yum源,可通过yum安装命令如下: yum install dos2unix dos2unix 有一个对立的命令unix2dos,也需要yum安装,一般使用不到这里不做过多解释,具体参数…...

Linux MySQL离线安装
一、准备工作 1. 下载MySQL安装包 访问MySQL官方网站,选择适合您Linux系统的MySQL版本进行下载。通常推荐下载Generic Linux (glibc 2.12)版本的.tar.gz压缩包,例如mysql-8.0.33-linux-glibc2.12-x86_64.tar.xz。将下载好的安装包拷贝到Linux服务器的某…...

声明,这些内容和我无关
声明,下面这些内容和我无关,不是我写的,买了我不负责答疑,也不负责其他相关。 一下内容都不是我写的,系统自己加上去的,和我无关,我不负责答疑也不负责其他。...

ISO:摄影中的光线敏感度密码
目录 一、ISO 究竟是什么 二、ISO 与光线的关系 (一)低 ISO 在充足光线下的表现 (二)高 ISO 在光线不足时的作用 三、ISO 对画质的影响 (一)低 ISO 带来的优质画质 (二)高 IS…...

长短期记忆网络LSTM
视频链接 1.LSTM与RNN的区别 RNN想把所有信息都记住,不管是有用的信息还是没用的信息,并且有梯度爆炸或者梯度消失的问题 而LSTM设计了一个记忆细胞,具备选择记忆功能,可以选择记忆重要信息,过滤掉噪声信息࿰…...

2. 握手问题python解法——2024年省赛蓝桥杯真题
原题传送门:1.握手问题 - 蓝桥云课 问题描述 小蓝组织了一场算法交流会议,总共有 50人参加了本次会议。在会议上,大家进行了握手交流。按照惯例他们每个人都要与除自己以外的其他所有人进行一次握手 (且仅有一次)。但有 7 个人,…...

poi在word中打开本地文件
poi版本 5.2.0 方法1:使用XWPFFieldRun(推荐) 比如打开当前相对路径的aaaaa.docx XWPFFieldRun run paragraph.createFieldRun();CTRPr ctrPr run.getCTR().addNewRPr();CTFonts font ctrPr.addNewRFonts();// 设置字体font.setAscii(&quo…...

国产编辑器EverEdit - 输出窗口
1 输出窗口 1.1 应用场景 输出窗口可以显示用户执行某些操作的结果,主要包括: 查找类:查找全部,筛选等待操作,可以把查找结果打印到输出窗口中; 程序类:在执行外部程序时(如:命令窗…...

整数的个数(信息学奥赛一本通-1067)
【题目描述】 给定k(1<k<100)个正整数,其中每个数都是大于等于1,小于等于10的数。写程序计算给定的k个正整数中,1,5和10出现的次数。 【输入】 输入有两行:第一行包含一个正整数k,第二行包含k个正整数…...

ios swift画中画技术尝试
继上篇:iOS swift 后台运行应用尝试失败-CSDN博客 为什么想到画中画,起初是看到后台模式里有一个picture in picture,去了解了后发现这个就是小窗口视频播放,方便用户执行多任务。看小窗口视频的同时,可以作其他的事情…...

MyBatis 写法
MyBatis 高效使用技巧 常见 MyBatis 使用技巧,这些技巧有助于简化数据库操作,提高开发效率,并增强系统的性能。 1. 动态 SQL 动态 SQL 让开发者能够依据参数灵活地构建 SQL 语句,避免了手动拼接字符串带来的复杂性和错误风险。…...

Three城市引擎地图插件Geo-3d
一、简介 基于Three开发,为Three 3D场景提供GIS能力和城市底座渲染能力。支持Web墨卡托、WGS84、GCJ02等坐标系,支持坐标转换,支持影像、地形、geojson建筑、道路,植被等渲染。支持自定义主题。 二、效果 三、代码 //插件初始化…...

【贪心算法】洛谷P1106 - 删数问题
2025 - 01 - 22 - 第 46 篇 【洛谷】贪心算法题单 - 【贪心算法】 - 【学习笔记】 作者(Author): 郑龙浩 / 仟濹(CSND账号名) 目录 文章目录 目录P1106 删数问题题目描述输入格式输出格式样例 #1样例输入 #1样例输出 #1 提示思路代码 P1106 删数问题 题目描述 键盘输入一个高…...