深度理解linux系统—— 进程切换和调度
前言:
了解了进程的状态和进程的优先级,我们现在来看进程是如何被
CPU
调度执行的。
在单CPU
的系统在,程序是并发执行的;也就是说在一段时间呢,进程是轮番执行的;
这也是说一个进程在运行时不会一直占用
CPU
直到运行结束,而是运行一段时间(时间片)然后切换下一个进程运行;
所以,对于一个死循环的进程执行的时候,我们是可以进行其他操作的,它并没有一直占有CPU
。
那这就要涉及到一个问题,那就是进程切换
对于一个进程,如果它在一个时间片内运行结束,那没有什么问题;但是如果一个时间片内没有运行结束呢?
那该进程被切换,然后等待再次被调度;
这里就有一个疑问:那再次调度进程时,如何知道上次运行到哪里呢?
寄存器
在叙述上面问题之前,先来了解一下寄存器
- 寄存器是
CPU
内部的临时空间- 寄存器 != 寄存器中的数据
什么意思呢?
我们应该进程在运行的时候,它是会产生临时数据的,但是这些临时输出存放到哪里呢,就存放在CPU
中的寄存器当中;
而寄存器又分为很多种,这里就不详细描述了;
这里简单理解:寄存器就是CPU
内部的临时空间。
进程切换
思考一个问题:进程的时间片到了,被切换出CPU
,在下次运行时,CPU
是如何知道进程上次运行到哪里了,以及运行时产生的临时数据?
这里举个例子:(比较牵强,简单理解)
博主最近喜欢看一本小说,但是这本小说特别特别长,我今天看了200页,要休息了;但是博主又担心明天看时,忘记看到了哪一页,所以博主在博主搞了一个书签,记录下当前页数(第几页、第几行)。
但是第二天睡醒,博主大脑空空的,再打开这本书时,书签只记录了第几行第几页了,博主不知道这本小说里面都有什么人物了,没办法博主只能再从又开始看,了解小说人物;博主第二天看到了300
页,要休息了,博主学精了,这次我不仅记录了当前第几页第几行,还记录了小说情节中出现的任务,还用其他细节的内容。
这样第三天博主再打开这本小说,虽然不记得第几页第几行,人物和小说细节,但是博主打开书签,上面记录了上次看完时小说的信息;这时就可以接着上次继续阅读了。
我们
CPU
也是如此,当一个进程被切换出CPU
,下次被调度时,CPU
是完全不记得上次进程运行到了哪里,有哪些临时变量等这些信息的;
那怎么办呢?
很简单,在我们一个进程被切换出CPU
时,只需要记录一下上下文内容,和临时变量等这一系列信息即可;
这里又存在问题了:这些信息存放在哪里呢?
很显然,不会存放在
CPU
中;那就只能存在于进程的task_struct
中。在
Linux
中,这些信息存放到了task_struct
中的成员Tss
中。
这样,当我们进程再次被调度时,直接将Tss
中记录的上下文信息加载到CPU
中的寄存器上,CPU
就可以继续上次运行的位置继续去运行。
此外,为了区分首次被调度和已经被调度过的进程,task_struct
中还存在一个整型变量来区分。
进程调度
简单了解了进程是如何切换的,我们现在再来看进程是如何被调度的?
在了解进程状态时,提及到一个
CPU
一个运行队列,那进程不是按照队列顺序被调度的吗?
很显然不是,如果真的是如此,那进程优先级就没有存在的意义了。
我们现在来看,linux
中运行队列的整体结构:
一眼望去,眼花缭乱的,为何如此复杂?
我们这里先注意看上图中的红色区域和蓝色区域内的内容;
运行队列的实现思路
我们先来看,红色区域和蓝色区域内都存在三个成员:
nr_active
bitmap[5]
queue[140]
queue
我们首先来看queue
,它是一个数组一个140
个元素;这里面存储的类型是struct task_struct*
,可以理解为一个task_struct
链表
它可以分为两部分:
[0 , 99]
:这一部分表示的是实时优先级(暂时不探讨);[100 , 139]
:这一部分,每一个位置都对应一个优先级。
[100 , 139]
,一共40个位置,而我们优先级正好也是40
个,所以这四十个位置和我们40
个优先级一一对应。
这样每一个位置存放的都是优先级相同的进程的task_struct
队列;
这样对于一个进程,我们只需要根据它的优先级就可以直接找到它对应的队列。(当然这里需要一个映射关系:对应位置 = (x - 60) + (140 - 100);
)
那这样,我们的queue
本质上不就是一个hash
表吗(这个表中每一个位置存储的是对应优先级的task_struct
队列)
bitmap[5]
在上述中,queue
本质上上一个hash
表,我们可以通过进程的优先级快速的找到其对应的队列;
但是,我们CPU
要挑选一个进程运行,那还是要遍历一遍整个queue
表啊;效率也不是很高啊
为了提高效率,这里存在一个bitmap[5]
位图;
它的类型是:
unsigned int bitmap[5]
我们知道一个字节等于8个bit位,一个整形4个字节;那一个整形用二进制表示就存在
32
位(32个0/1
)。这里有
5
给个整形,也就是160
个二进制数;而queue
中一共存在140
个位置,我们是不是可以用一个bit
对应queue
中的一个task_struct
队列;所以这里
bitmap[5]
中每一个bit
位如果为1
就表示其对应位置的队列中存在task_struct
;为0
表示其对应位置不存在task_struct
。
所以,有了bitmap[5]
,CPU
在选择进程运行时,就不需要再遍历queue
表,而是在bitmap
中找到bit
位为1
的位置,然后直接去其queue
对应位置去寻找进程的task_struct
即可。
这里检测的过程,简单来说急速对
bitmap[5]
中的5
个元素通过int
的方式进行位操作,(例如,bitmap[0] & xFFFFFFF == 0
就表示bitmap[0]
的二进制的每一位都等于0
,就表示第一个整型位对应位置队列中是不存在task_struct
的;一次类推);这样相比于去遍历queue
表,效率快了非常对,已经接近O(1)
了。
nr_avtive
这个就简单多了,它指的就是当前CPU
队列中所有可运行队列的数量。
进程饥饿
进程饥饿(Process Starvation) 是指某个进程因长期无法获得所需的系统资源(如CPU时间、I/O资源等)而无法执行的现象。
简单来说就是应该进程它一直在等待被调度,但是一直没有被调度;
我们知道一个进程它运行时间片结束后,被切换出CPU
,它是要重新回到等待队列,等待下一次被调度的;
在我们上述的理解中,应该优先级为
60
的进程,它是一个死循环,时间片到了,被切换出CPU
之后,它又回到了优先级是60
的队列中;那CPU
在下一次调度时,还是有限调度优先级高的进程;此时还存在一个优先级为90
的队列,那此时CPU
就会一直调用优先级为60
的进程,直到该进程执行结束,才会去调度优先级为90
队列中的进程;那如果这样去设计的话,非常容易就造成了进程饥饿的问题;
Linux
中是如何去解决的呢?
Linux
中,它不仅存在一个queue
,在内核中,将queue
、bitmap
、nr_avtive
包装成rqueue_elem
;然后定义struct rqueue_elem[2]
;
这样一个
struct rqueue_elem
表示活动队列,另外一个struct rqueue_elem
表示过期队列;在
Linux
内核中还存在两个指针,一个是active
指向当前的活动队列,另一个expired
指向当前的过期队列。(初始情况下:struct rqueue_elem[0]
是活动队列,struct rqueue_elem[1]
是过期队列;active
指向rqueue_elem[0]
,expired
指向rqueue_elem[1]
)
这样CPU
只在active
指向的活动队列中选取进程调度,进程被调度结束后,放入到expired
指向的过期队列中。
这样,active
执行的活动队列中进程被调度完时,CPU
就要去调度expired
指向的队列。
交换一下active
和expired
的值,active
就指向了新的活动队列;expired
就指向了新的过期队列。
到这里,本篇文章大致内容就结束了,感谢各位支持
简单总结:
- 进程切换:进程是如何切换的,切换时上下文信息存储到哪里
- 进程调度:
linux
中的调度算符:O(1)
调度算法实现的思路。
相关文章:
深度理解linux系统—— 进程切换和调度
前言: 了解了进程的状态和进程的优先级,我们现在来看进程是如何被CPU调度执行的。 在单CPU的系统在,程序是并发执行的;也就是说在一段时间呢,进程是轮番执行的; 这也是说一个进程在运行时不会一直占用CPU直…...
【凑修电脑的小记录】vscode打不开
想把vscode的数据和环境从c盘移到d盘 大概操作和这篇里差不多 修改『Visual Studio Code(VS Code)』插件默认安装路径的方法 - 且行且思 - 博客园 在原地址保留了个指向新地址的链接文件。 重新安装vscode后双击 管理员身份运行均无法打开࿰…...
2025五一数学建模竞赛A题完整分析论文(共45页)(含模型、可运行代码、数据)
2025年五一数学建模竞赛A题完整分析论文 摘 要 一、问题分析 二、问题重述 三、模型假设 四、符号定义 五、 模型建立与求解 5.1问题1 5.1.1问题1思路分析 5.1.2问题1模型建立 5.1.3问题1参考代码 5.1.4问题1求解结果 5.2问题2 5.2.1问题2思路分析 …...
从0搭建Transformer
0. 架构总览: 1. 位置编码模块: import torch import torch.nn as nn import mathclass PositonalEncoding(nn.Module):def __init__ (self, d_model, dropout, max_len5000):super(PositionalEncoding, self).__init__()self.dropout nn.Dropout(pdrop…...
生物化学笔记:神经生物学概论07 躯体感受器 传入方式 自主神经系统
功能各异的躯体感受器 解释张力: 形形色色的传入方式 脑中的“倒立小人” 自主神经系统...
滑动窗口leetcode 209和76
一、leetcode 209. 长度最小的子数组 代码: class Solution { public:int minSubArrayLen(int target, vector<int>& nums) {int n nums.size();int left 0;int sum 0;int res 100001;for(int right 0;right <n;right){sum nums[right];while(s…...
FPGA:介绍几款高速ADC及其接口形式
本文介绍了几款采样率至少为500Msps的高速ADC芯片,并详细介绍ADC与FPGA之间的常见接口形式,以及FPGA如何正确读取高速ADC的输出数据。以下内容基于当前的高速ADC技术趋势和常见的工程实践。 一、推荐的高速ADC芯片(采样率≥500Msps࿰…...
未使用连接池或配置不当的性能陷阱与优化实践
目录 前言一、传统连接管理的性能缺陷与风险1. 未使用连接池的致命代价2. 连接池配置不当的典型表现 二、高性能连接池选型与核心参数优化1. HikariCP:零开销连接池的标杆2. Druid:功能完备的国产连接池 三、连接池性能调优的黄金法则1. 科学设定最大连接…...
亚马逊云服务器性能深度优化方案(2025版)
亚马逊云服务器性能深度优化方案(2025版) 一、计算架构全面升级 1. 新一代AI算力引擎 • Trn2 UltraServer实例:搭载64颗第二代Trainium芯片,单节点FP8算力达83.2 PFlops,支持千亿参数大模型训练,训…...
【IPMV】图像处理与机器视觉:Lec9 Laplace Blending 拉普拉斯混合
【IPMV】图像处理与机器视觉 本系列为2025年同济大学自动化专业**图像处理与机器视觉**课程笔记 Lecturer: Rui Fan、Yanchao Dong Lec0 Course Description Lec3 Perspective Transformation Lec7 Image Filtering Lec8 Image Pyramid Lec9 Laplace Blending 持续更新中 …...
【东枫电子】AMD / Xilinx Alveo™ UL3422 加速器
AMD / Xilinx Alveo™ UL3422 加速器 AMD / Xilinx Alveo™ UL3422 加速器提供超低延迟网络和灵活应变的硬件,支持纳秒级交易策略。AMD Virtex™ UltraScale™ VU2P FPGA 为 AMD / Xilinx Alveo UL3422 加速器提供强大的支持。该加速器采用延迟优化的收发器技术&am…...
Linux架构篇、第一章_03安装部署nginx
Linux_基础篇 欢迎来到Linux的世界,看笔记好好学多敲多打,每个人都是大神! 题目:安装部署nginx 版本号: 1.0,0 作者: 老王要学习 日期: 2025.05.02 适用环境: Centos7 文档说明 本文档聚焦于 CentOS 7 环境下 Nginx 的安装部…...
Semantic Kernel 快速入门
文章目录 Semantic Kernel 快速入门一、什么是 Semantic Kernel?1.1 核心特性 二、安装和配置2.1 安装 .NET SDK2.2 创建新的 .NET 项目2.3 安装 Semantic Kernel 三、快速入门3.1 导入依赖包3.2 添加 AI 服务3.3 添加企业服务3.4 生成内核并检索服务3.5 添加插件创…...
MySQL进阶(一)
一、存储引擎 1. MySQL体系结构 连接层: 最上层是一些客户端和链接服务,主要完成一些类似于连接处理、授权认证、及相关的安全方案。服务器也会为安全接入的每个客户端验证它所具有的操作权限 服务层: 第二层架构主要完成大多数的核心服务…...
ThreadLocal理解
1.thread是线程,threadLocal是对象? 在 Java 中: Thread 是线程类,其实例代表线程:Thread 类用于创建和管理线程,每个线程都是 Thread 类的一个实例,用于执行具体的任务,例如&…...
PyTorch、Flash-Attn、Transformers与Triton技术全景解析+环境包
PyTorch、Flash-Attn、Transformers与Triton技术全景解析 包好难找 这里是下载链接 添加链接描述 摘要 本文系统性地介绍了深度学习领域的四大关键技术框架:PyTorch、Flash-Attn、Hugging Face Transformers和Triton,分别从核心特性、技术优势、应用场…...
mindyolo填坑
1、按照gitee上的文档跑预测代码,跑不通 更改: 将predict.py复制到跟目录。如果是cpu(本地测试比较常见),那么正确的命令行是: python predict.py --device_targetCPU --config ./configs/yolov7/yolov7.…...
【C++】平衡二叉树(AVL树)迭代版
目录 前言: 一:判断一棵树是否为平衡二叉树 二:明确思路 1.为什么使用平衡二叉树 2.旋转 2.1 左旋 2.2 右旋 3.冲突节点 4.平衡因子 5.双旋 5.1 左右双旋(LR) 5.2 右左双旋(RL) 6.平衡因子的更新 7.冲突节点问题补充 三&…...
双链表详解
一、双向链表介绍 二、实现双向链表 1.定义双向链表的结构 2.双向链表的初始化 3.双向链表的尾插 4.双向链表的头插 5.双向链表的打印 6.双向链表的尾删 7.双向链表的头删 8.查找指定位置的数据 9.在指定位置之后插入数据 10.删除指定位置的数据 11.链表的销毁 三、…...
6.9.单源最短路径问题-BFS算法
一.前言: 问题1: 以上述图片为例,比如从G港到Y城,可以是G港->R城->Y城,也可以是G港->P城->Y城等,有很多条路径都可以实现从G港到Y城,但要从中找出G港到Y城距离最短的那一条路径&am…...
react js 查看字体效果
起因, 目的: 想查看某个字体,对中英文的支持情况。 效果图: 完整项目见这里, 需要积分下载,不然的话,显得太水了。 过程: AI 对话, 生成代码。我检查运行, 来回修改。写个博客,…...
GZIPInputStream 类详解
GZIPInputStream 类详解 GZIPInputStream 是 Java 中用于解压缩 GZIP 格式数据的流类,属于 java.util.zip 包。它是 InflaterInputStream 的子类,专门处理 GZIP 压缩格式(.gz 文件)。 1. 核心功能 解压 GZIP 格式数据(RFC 1952 标准)自动处理 GZIP 头尾信息(校验和、时…...
数字智慧方案6206丨智慧园区大数据整体解决方案(45页PPT)(文末有下载方式)
资料解读:智慧园区大数据整体解决方案 详细资料请看本解读文章的最后内容。 在数字化快速发展的当下,智慧园区成为推动产业升级和城市发展的关键力量。这份智慧园区大数据整体解决方案,融合前沿技术与创新理念,为园区的高效管理、…...
Linux系统常用命令、标准C库函数和系统调用
目录 一、常用命令 env echo $name 键值 export name unset name gcc -c xxx.c ar 命令 ar -r libxxx.a xxx1.o xxx2.o gcc -c -fpic xxx.c gcc -shared -fpic xxx1.c xxx2.c -o libxxx.so kill [-信号] PID kill -l 软链接:ln -s xxx yyy 硬链接&…...
【Linux】基础指令(2)
man linux中有很多指令,我们不可能全部记住,man是linux/unix系统中的手册页指令,当我们遇到不熟悉的命令可以用man来查看命令,函数,配置文件的详细使用说明。 man手册分为多个章节,详情如下: …...
“会话技术”——Cookie_(2/2)原理与使用细节
经过Cookie的快速入门与代码使用。如果想深入理解Cookie的技术实现,就得去理解它的原理。 且有些时候使用Cookie,还要根据需求设置存活期限以及确定Cookie获取范围等其他细节。最后,我们会总结Cookie这门客户端会话技术的作用。 一、原理 注…...
Linux操作系统--进程间通信(中)(命名管道)
目录 1.命名管道: 1.1创建一个命名管道 1.2匿名管道与命名管道的区别 1.3命名管道的打开规则 1.4例子1-用命名管道实现文件拷贝 1.5例子2-用命名管道实现server&client通信 1.命名管道: 毫不相关的进程进行进程间通信管道应用的一个限制就是只能…...
数据结构6 · BinaryTree二叉树模板
代码函数功能顺序如下: 1:destroy:递归删除树 2:copy:复制二叉树 3:preOrder:递归前序遍历 4:inOrder:递归中序遍历 5:postOrder:递归后续遍…...
ubuntu的libc 库被我 sudo apt-get --reinstall install libc6搞没了
我系统的libc 没了 今天为了运行一个开源的yuv 播放器,在运行的时候提醒 Inconsistency detected by ld.so: dl-call-libc-early-init.c: 37: _dl_call_libc_early_init: Assertion sym ! NULL failed!然后听从AI 的建议 当我去执行ls 时,系统提示 就这…...
cat file.tar.gz | tar -xzf - -C /target/dir两个减号之间为什么有个空格?是写错了吗?(管道命令后续)
在 tar 命令的参数 -xzf - -C 中,两个减号(-)之间的空格是故意保留的语法,没有写错。具体原因如下: 1. -xzf - 的语法解析 -xzf 是 tar 命令的组合参数: x:表示解压(extract&#x…...
手机的数据楚门世界是如何推送的
手机推送,也叫茧影算法,手机的数据“楚门世界”:信息推送机制的深度剖析与社会影响 在数字化时代,手机已然成为人们生活中不可或缺的伴侣。当我们沉醉于手机带来的便捷与娱乐时,或许未曾察觉,自己正置身于…...
体系结构论文(八十二):A Comprehensive Analysis of Transient Errors on Systolic Arrays
研究背景与动机 TPU架构(Tensor Processing Unit)广泛应用于DNN推理,其核心是脉动阵列,由大量的乘加单元(MAC)组成。 由于使用了纳米级CMOS技术,TPU对辐射引发的瞬态错误(SET&#…...
综合案例:使用vuex对购物车的商品数量和价格等公共数据进行状态管理
文章目录 0.实现需求1.新建购物车模块cart2.使用json-server模拟向后端请求数据3.在vuex请求获取并存入数据,并映射到组件中,在组件中渲染【重点】3.1.安装axios3.2.准备actions和mutations,获取和存入数据到vuex中3.3.动态渲染:用mapState映射 其他1.为什么在axios在项目中要局…...
二叉搜索树的判断(双指针解决)
98. 验证二叉搜索树 - 力扣(LeetCode) class Solution { public:TreeNode*preNULL;bool isValidBST(TreeNode* root) {if(rootNULL){return true;}bool leftisValidBST(root->left);if(pre!NULL&&pre->val>root->val){return fals…...
关于CSDN创作的常用模板内容
🤟致敬读者 🟩感谢阅读🟦笑口常开🟪生日快乐⬛早点睡觉 📘博主相关 🟧博主信息🟨博客首页🟫专栏推荐🟥活动信息 文章目录 好文评论新文推送 📃文章前言 &…...
不小心误删了文件,找Windows数据恢复工具来帮忙
相信很多人都遇到过这样的情况:不小心在电脑上删除了一些重要的文件,等到想要找回来时,却感觉特别棘手。 今天我要给大家推荐一款超棒的Windows数据恢复工具,它能轻松帮你找回那些被误删的文件。 (文末附下载链接&…...
[Verilog]跨时钟域数据传输解决方案
跨时钟域数据传输解决方案 摘要:跨时钟域数据传输 (Clock Domain Crossing, CDC) 是 SoC 设计中常见且关键的问题,因为现代 SoC 通常包含多个时钟域,不同模块可能运行在不同频率或相位的时钟下。跨时钟域传输数据时,如果处理不当,可能会导致亚稳态 (Metastability)…...
Linux——进程终止/等待/替换
前言 本章主要对进程终止,进程等待,进程替换的详细认识,根据实验去理解其中的原理,干货满满! 1.进程终止 概念:进程终止就是释放进程申请的内核数据结构和对应的代码和数据 进程退出的三种状态 代码运行…...
数据结构与算法:图论——最短路径
最短路径 先给出一些leetcode算法题,以后遇见了相关题目再往上增加 最短路径的4个常用算法是Floyd、Bellman-Ford、SPFA、Dijkstra。不同应用场景下,应有选择地使用它们: 图的规模小,用Floyd。若边的权值有负数,需要…...
双指针(5)——有效三角形个数
题目: 这道题我们首先可能会想到暴力解法,三个for循环然后进行check()。时间复杂度肯定是不允许的。 同时,验证可以组成三角形的条件是任意两边之和大于第三边,这就意味着我们每组要进行三次比较。但也有捷…...
Qt QGraphicsScene 的用法
背景,为什么要写这篇博客 今天学习 model - view 模式的时候还看到有 scene - view 模式。不知道还有这个模式,所以学习了下。 学习后总体的感觉是:其实没有也是可以的,但有了方便许多。 从两种画图的方法开始说 以前有个项目也…...
使用 Tesseract 实现藏文OCR
要识别藏文,最常用且有效的方法是使用Tesseract OCR(谷歌开源的OCR工具),因为它拥有针对藏文的预训练模型支持。 🚀 一、安装 Tesseract OCR 软件: 下载链接:Tesseract OCR 下载页面 Windows用…...
数字智慧方案5873丨智慧交通设计方案(57页PPT)(文末有下载方式)
资料解读:智慧交通设计方案 详细资料请看本解读文章的最后内容。 智慧交通设计方案是一份详尽的交通规划文件,旨在通过科学的交通设计方法,优化交通系统,提升交通效率,确保交通安全,并促进可持续发展。该…...
【quantity】6 温度单位实现(temperature.rs)
一源码 以下代码实现了一个温度单位系统,支持开尔文(Kelvin)和摄氏度(Celsius)之间的转换和运算。 /// Temperature (kelvin) / 温度 (开尔文) use super::{Quantity, prefix::*}; use crate::unit::Kelvin; use derive_more::{Add, Sub, AddAssign, SubAssign};/…...
ARConv的复现流程
使用环境 Python 3.10.16 torch 2.1.1cu118 torchvision 0.16.1cu118 其它按照官方提供代码的requirements.txt安装 GitHub - WangXueyang-uestc/ARConv: Official repo for Adaptive Rectangular Convolution 数据准备 从官方主页下载pancollection数据集PanCollection…...
安卓游戏APK文件解密与编辑的完整攻略
在移动游戏开发中,保护游戏数据不被篡改是开发者的重要任务。然而,随着逆向工程技术的发展,破解游戏数据也变得可能。本文将详细介绍如何分析、解密和编辑APK安装包中的加密JSON文件,特别关注assets/task目录下的文件,并提供一种绕过checkfile.json中MD5校验的有效方法。通…...
JVM——JVM 是如何执行方法调用的?
JVM 是如何执行方法调用的? 在 Java 世界的底层运作中,方法调用机制是理解 Java 虚拟机(JVM)行为的关键之一。JVM 作为 Java 程序运行的核心,承担着执行字节码、管理内存、调度线程等多项职责。而方法调用作为程序逻辑…...
一天学完JDBC!!(万字总结)
文章目录 JDBC是什么 1、环境搭建 && 入门案例2、核心API理解①、注册驱动(Driver类)②、Connection③、statement(sql注入)④、PreparedStatement⑤、ResultSet 3、jdbc扩展(ORM、批量操作)①、实体类和ORM②、批量操作 4. 连接池①、常用连接池②、Durid连接池③、Hi…...
【愚公系列】《Manus极简入门》011-习惯养成教练:“习惯塑造师”
🌟【技术大咖愚公搬代码:全栈专家的成长之路,你关注的宝藏博主在这里!】🌟 📣开发者圈持续输出高质量干货的"愚公精神"践行者——全网百万开发者都在追更的顶级技术博主! …...
精益数据分析(38/126):SaaS模式的流失率计算优化与定价策略案例
精益数据分析(38/126):SaaS模式的流失率计算优化与定价策略案例 在创业和数据分析的领域中,我们不断探索如何更精准地把握业务发展的关键要素。今天,带着与大家共同进步的想法,深入研读《精益数据分析》&a…...