Linux 进程状态:TASK_INTERRUPTIBLE 和 TASK_UNINTERRUPTIBLE
文章目录
- 1. 前言
- 2. TASK_INTERRUPTIBLE 和 TASK_UNINTERRUPTIBLE
- 2.1 语义
- 2.2 实现
- 2.2.1 TASK_INTERRUPTIBLE 实现
- 2.2.1.1 等待的条件成立时 唤醒
- 2.2.1.2 信号 唤醒
- 2.2.1.3 中断 唤醒
- 2.2.1.3.1 内核态的处理过程
- 2.2.1.3.2 用户态的处理过程
- 2.2.2 TASK_UNINTERRUPTIBLE 实现
- 2.3 小结
- 3. 参考资料
1. 前言
限于作者能力水平,本文可能存在谬误,因此而给读者带来的损失,作者不做任何承诺。
2. TASK_INTERRUPTIBLE 和 TASK_UNINTERRUPTIBLE
2.1 语义
以下是从文章 Process Scheduling in the Kernel 摘录的对进程状态 TASK_INTERRUPTIBLE
和 TASK_UNINTERRUPTIBLE
的说明:
TASK_INTERRUPTIBLEidentifies a process that is suspended (sleeping) until some condition becomes true.
Raising an interrupt, releasing a system resource the process is waiting for, or
delivering a signal are examples of conditions that might wake up the process,
that is put its state back to TASK_RUNNNING. TASK_UNINTERRUPTIBLEidentifies a process that is suspended like in the TASK_INTERRUPTIBLE state, except that
in this case delivering a signal will not wake up the process. This process state is
seldom used.
简单翻译一下:
. TASK_INTERRUPTIBLE进程进入睡眠直到等待,中断、信号、或等待的条件成立时,可唤醒进程,并可能将进程重新置为运行态(TASK_RUNNNING)。. TASK_UNINTERRUPTIBLE类似于 TASK_INTERRUPTIBLE,但无法通过信号唤醒进程。
2.2 实现
从 2.1
了解了对 TASK_INTERRUPTIBLE
和 TASK_UNINTERRUPTIBLE
的语义,本小节从代码层面看内核是如何实现
它们的。
2.2.1 TASK_INTERRUPTIBLE 实现
以 socket
通信 TCP 三次握手 过程中的 accept()
调用为例,来说明 TASK_INTERRUPTIBLE
的语义实现。
服务端调用 accept()
等待 TCP 连接的三次握手完成:
sys_accept()sys_accept4()sock->ops->accept() = inet_accept()sk1->sk_prot->accept() = inet_csk_accept()if (reqsk_queue_empty(queue)) {/* 阻塞模式下,永不超时,即 timeout 为 MAX_SCHEDULE_TIMEOUT */long timeo = sock_rcvtimeo(sk, flags & O_NONBLOCK);.../* 等待 TCP 连接的三次握手完成 */error = inet_csk_wait_for_connect(sk, timeo);...}static int inet_csk_wait_for_connect(struct sock *sk, long timeo)
{struct inet_connection_sock *icsk = inet_csk(sk);DEFINE_WAIT(wait);int err;for (;;) {/* 将进程添加到等待队列 sk_sleep(sk) */prepare_to_wait_exclusive(sk_sleep(sk), &wait,TASK_INTERRUPTIBLE);release_sock(sk);if (reqsk_queue_empty(&icsk->icsk_accept_queue))timeo = schedule_timeout(timeo); /* (1) 主动调度:进入 TASK_INTERRUPTIBLE 睡眠等待 */...lock_sock(sk);err = 0;if (!reqsk_queue_empty(&icsk->icsk_accept_queue)) /* 有连接完成三次握手, */break; /* 正常结束等待 */err = -EINVAL;if (sk->sk_state != TCP_LISTEN)break;err = sock_intr_errno(timeo);if (signal_pending(current)) /* 进程有信号挂起, */break; /* 终止等待过程,处理信号 */err = -EAGAIN; /* 非阻塞方式下等待超时错误码 EAGAIN */if (!timeo) /* 非阻塞方式下等待超时, */break; /* 终止等待过程,用户收到错误码 EAGAIN,提示可以重试 */}/* 将进程从等待队列 sk_sleep(sk) 移除,重新进入 TASK_RUNNING 状态 */finish_wait(sk_sleep(sk), &wait);return err;
}void finish_wait(struct wait_queue_head *wq_head, struct wait_queue_entry *wq_entry)
{unsigned long flags;__set_current_state(TASK_RUNNING); /* 进程重新进入 TASK_RUNNING 状态 *//** We can check for list emptiness outside the lock* IFF:* - we use the "careful" check that verifies both* the next and prev pointers, so that there cannot* be any half-pending updates in progress on other* CPU's that we haven't seen yet (and that might* still change the stack area.* and* - all other users take the lock (ie we can only* have _one_ other CPU that looks at or modifies* the list).*/if (!list_empty_careful(&wq_entry->entry)) {spin_lock_irqsave(&wq_head->lock, flags);list_del_init(&wq_entry->entry);spin_unlock_irqrestore(&wq_head->lock, flags);}
}
看下调度细节:
/* kernel/time/timer.c */signed long __sched schedule_timeout(signed long timeout)
{...schedule();...
}
/* kernel/sched/core.c */asmlinkage __visible void __sched schedule(void)
{struct task_struct *tsk = current;sched_submit_work(tsk);do {preempt_disable(); /* 关闭抢占 */__schedule(false); /* 主动调度 */sched_preempt_enable_no_resched(); /* 开启抢占 */} while (need_resched());
}static void __sched notrace __schedule(bool preempt)
{struct task_struct *prev, *next;...struct rq *rq;int cpu;cpu = smp_processor_id();rq = cpu_rq(cpu);prev = rq->curr;...local_irq_disable();...if (!preempt && prev->state) {/** 如果进程 @prev 当前有信号挂起,不进入睡眠,* 而是继续保持 可运行 状态,以备后续被调度时处理信号。*/if (unlikely(signal_pending_state(prev->state, prev))) {prev->state = TASK_RUNNING;} else {/* 从可运行队列移除 */deactivate_task(rq, prev, DEQUEUE_SLEEP | DEQUEUE_NOCLOCK);prev->on_rq = 0;...}}next = pick_next_task(rq, prev, &rf); /* 挑选要执行的进程 */...if (likely(prev != next)) { /* 切换到不同进程 */rq->nr_switches++;rq->curr = next;.../* Also unlocks the rq: */rq = context_switch(rq, prev, next, &rf); /* 进程上下文切换 */} else {...}
}
从上面分析看到,服务端在 accept()
中在 TASK_INTERRUPTIBLE
状态睡眠等待
。接着看在 3
种不同场景下唤醒进程的过程。
2.2.1.1 等待的条件成立时 唤醒
正常唤醒过程,在连接三次握手完成唤醒过程
:
tcp_child_process(sk, nsk, skb)...parent->sk_data_ready(parent) = sock_def_readable()wq = rcu_dereference(sk->sk_wq);if (skwq_has_sleeper(wq))/* 唤醒在 accept() 中等待连接的进程 */wake_up_interruptible_sync_poll(&wq->wait, POLLIN | POLLPRI POLLRDNORM | POLLRDBAND);......
2.2.1.2 信号 唤醒
异常唤醒过程,通过信号唤醒进程
:
static int __send_signal(int sig, struct siginfo *info, struct task_struct *t,int group, int from_ancestor_ns)
{...complete_signal(sig, t, group);
}static void complete_signal(int sig, struct task_struct *p, int group)
{...signal_wake_up(t, sig == SIGKILL);...
}static inline void signal_wake_up(struct task_struct *t, bool resume)
{signal_wake_up_state(t, resume ? TASK_WAKEKILL : 0);
}void signal_wake_up_state(struct task_struct *t, unsigned int state)
{set_tsk_thread_flag(t, TIF_SIGPENDING); /* 标记进程 @t 有信号挂起 *//** TASK_WAKEKILL also means wake it up in the stopped/traced/killable* case. We don't check t->state here because there is a race with it* executing another processor and just now entering stopped state.* By using wake_up_state, we ensure the process will wake up and* handle its death signal.*//* * 唤醒 TASK_INTERRUPTIBLE 状态的进程处理信号: * . 将进程设置为 TASK_RUNNING 状态* . 选择运行的 CPU* . 设置 TIF_NEED_RESCHED 标志* . 其它 ......*/if (!wake_up_state(t, state | TASK_INTERRUPTIBLE))kick_process(t);
}
2.2.1.3 中断 唤醒
异常唤醒过程,中断唤醒
。如果被中断的进程因信号投递而被唤醒(设置了 TIF_NEED_RESCHED
标志),将发生中断处理结束时的抢占。由于进程被中断时,可能处于 内核态 和 用户态,所以有两种不同的执行路径。本文以 ARMv7
架构中断处理过程为例分别加以说明。
2.2.1.3.1 内核态的处理过程
/* arch/arm/kernel/entry-armv.S */.align 5
__irq_svc:svc_entryirq_handler /* 处理 内核态 中断 */#ifdef CONFIG_PREEMPT/* 开启了 内核态抢占 的情形 */ldr r8, [tsk, #TI_PREEMPT] @ get preempt countldr r0, [tsk, #TI_FLAGS] @ get flagsteq r8, #0 @ if preempt count != 0movne r0, #0 @ force flags to 0tst r0, #_TIF_NEED_RESCHED /* 在本文场景,检查因信号投递而设置的 TIF_NEED_RESCHED 标志 */blne svc_preempt /* 中断处理结束后,发起 内核态 抢占 */
#endifsvc_exit r5, irq = 1 @ return from exceptionUNWIND(.fnend )
ENDPROC(__irq_svc)#ifdef CONFIG_PREEMPT
svc_preempt:mov r8, lr/* 发起内核态抢占 */
1: bl preempt_schedule_irq @ irq en/disable is done insideldr r0, [tsk, #TI_FLAGS] @ get new tasks TI_FLAGStst r0, #_TIF_NEED_RESCHEDreteq r8 @ go againb 1b
#endif
asmlinkage __visible void __sched preempt_schedule_irq(void)
{enum ctx_state prev_state;/* Catch callers which need to be fixed */BUG_ON(preempt_count() || !irqs_disabled());prev_state = exception_enter();do {preempt_disable();local_irq_enable();__schedule(true); /* 抢占调度 */local_irq_disable();sched_preempt_enable_no_resched();} while (need_resched());exception_exit(prev_state);
}
2.2.1.3.2 用户态的处理过程
/* arch/arm/kernel/entry-armv.S */.align 5
__irq_usr:usr_entrykuser_cmpxchg_checkirq_handler /* 处理 用户态 中断 */get_thread_info tskmov why, #0b ret_to_user_from_irqUNWIND(.fnend )
ENDPROC(__irq_usr)
/* arch/arm/include/asm/thread_info.h *//** Change these and you break ASM code in entry-common.S*/
#define _TIF_WORK_MASK (_TIF_NEED_RESCHED | _TIF_SIGPENDING | \_TIF_NOTIFY_RESUME | _TIF_UPROBE)
/* arch/arm/kernel/entry-common.S */ENTRY(ret_to_user_from_irq)...ldr r1, [tsk, #TI_FLAGS]tst r1, #_TIF_WORK_MASK /* 检查 是否需要调度、是否有信号要处理 等等 */bne slow_work_pending /* 处理 调度、信号 等等 */
no_work_pending:...restore_user_regs fast = 0, offset = 0
ENDPROC(ret_to_user_from_irq)/* 处理 调度、信号 等等 */
slow_work_pending:...bl do_work_pending...
/* arch/arm/kernel/signal.c */asmlinkage int
do_work_pending(struct pt_regs *regs, unsigned int thread_flags, int syscall)
{...do {if (likely(thread_flags & _TIF_NEED_RESCHED)) {schedule(); /* 执行调度 */} else {...}...thread_flags = current_thread_info()->flags;} while (thread_flags & _TIF_WORK_MASK);...
}
2.2.2 TASK_UNINTERRUPTIBLE 实现
TASK_UNINTERRUPTIBLE
状态的典型场景是 msleep()
调用:
void msleep(unsigned int msecs)
{unsigned long timeout = msecs_to_jiffies(msecs) + 1;while (timeout)timeout = schedule_timeout_uninterruptible(timeout);
}signed long __sched schedule_timeout_uninterruptible(signed long timeout)
{__set_current_state(TASK_UNINTERRUPTIBLE);return schedule_timeout(timeout);
}
msleep()
将进程设置为 TASK_UNINTERRUPTIBLE
状态,不会让人觉得意外。毕竟,msleep()
的本意就是让进程睡够指定时间才被唤醒,睡眠过程不可被中断(即 UNINTERRUPTIBLE
)。如果时间没睡够中途就被唤醒,这不符合 msleep()
的语义。
再看一个驱动代码片段示例:
/* drivers/hwmon/abituguru.c */static int abituguru_send_address(struct abituguru_data *data,u8 bank_addr, u8 sensor_addr, int retries)
{...for (;;) {...if (abituguru_wait(data, ABIT_UGURU_STATUS_INPUT)) {if (retries) {/* 进入 TASK_UNINTERRUPTIBLE 等待超时时间 ABIT_UGURU_RETRY_DELAY 到达 */set_current_state(TASK_UNINTERRUPTIBLE);schedule_timeout(ABIT_UGURU_RETRY_DELAY);retries--;continue;}...}...}
}
/* kernel/time/timer.c */signed long __sched schedule_timeout(signed long timeout)
{struct timer_list timer;unsigned long expire;switch (timeout){case MAX_SCHEDULE_TIMEOUT:schedule();goto out;default:...}expire = timeout + jiffies;setup_timer_on_stack(&timer, process_timeout, (unsigned long)current);__mod_timer(&timer, expire, false);schedule(); /* 主动调度出去,等待超时时间、或等待的事件 到达 */...timeout = expire - jiffies; /* 剩余的超时时间: 有可能 等待的事件到达 而被 提前唤醒 */out:return timeout < 0 ? 0 : timeout;
}/* 超时后唤醒进进程 */
static void process_timeout(unsigned long __data)
{wake_up_process((struct task_struct *)__data);
}/* Convenience macros for the sake of wake_up(): */
#define TASK_NORMAL (TASK_INTERRUPTIBLE | TASK_UNINTERRUPTIBLE)int wake_up_process(struct task_struct *p)
{return try_to_wake_up(p, TASK_NORMAL, 0);
}
为什么 TASK_UNINTERRUPTIBLE
进程不会因中断或信号而唤醒?从前面的 signal_wake_up_state()
分析已经有了答案,这里再重复一下:
void signal_wake_up_state(struct task_struct *t, unsigned int state)
{...if (!wake_up_state(t, state | TASK_INTERRUPTIBLE))kick_process(t);
}
可见,信号只会唤醒 TASK_INTERRUPTIBLE
状态的进程。既然信号不唤醒 TASK_UNINTERRUPTIBLE
状态进程,自然也不会进一步设置进程的 TIF_NEED_RESCHED
标志,因此中断也无法唤醒它:中断处理结束时的抢占调度依赖于 TIF_NEED_RESCHED
标志。
在本小节驱动例子中,TASK_UNINTERRUPTIBLE
状态的进程等到超时时间 ABIT_UGURU_RETRY_DELAY
到达时被唤醒。
2.3 小结
TASK_INTERRUPTIBLE
在主动调度出去时,如果当前没有信号挂起
,就会从 CPU 的运行队列中移除
,但如果当前有信号挂起
,会继续保持 TASK_RUNNING
状态,且不会从 CPU 的运行队列中移除;TASK_UNINTERRUPTIBLE
在主动调度出去时,直接从 CPU 的运行队列中移除
。TASK_INTERRUPTIBLE
可看作浅度睡眠
。
TASK_INTERRUPTIBLE
睡眠期间,可能被等待的事件、信号、中断唤醒;TASK_UNINTERRUPTIBLE
睡眠期间,无法被 信号、中断唤醒,只能被等待的事件唤醒,如前面例子中的超时时间到达。TASK_UNINTERRUPTIBLE
可看作深度睡眠
。
TASK_INTERRUPTIBLE
工具观察为 S
态,TASK_UNINTERRUPTIBLE
工具观察为 D
态。
Linux 内核提供对长时间处于 TASK_UNINTERRUPTIBLE
态进程的监测机制,细节可参考博文 Linux: hung task 检测机制简析 。
3. 参考资料
[1] Process Scheduling in the Kernel
相关文章:
Linux 进程状态:TASK_INTERRUPTIBLE 和 TASK_UNINTERRUPTIBLE
文章目录 1. 前言2. TASK_INTERRUPTIBLE 和 TASK_UNINTERRUPTIBLE2.1 语义2.2 实现2.2.1 TASK_INTERRUPTIBLE 实现2.2.1.1 等待的条件成立时 唤醒2.2.1.2 信号 唤醒2.2.1.3 中断 唤醒2.2.1.3.1 内核态的处理过程2.2.1.3.2 用户态的处理过程 2.2.2 TASK_UNINTERRUPTIBLE 实现 2.…...

vue3使用vant4的列表vant-list点击进入详情自动滚动到对应位置,踩坑日记(一天半的踩坑经历)
1.路由添加keepAlive <!-- Vue3缓存组件,写法和Vue2不一样--><router-view v-slot"{ Component }"><keep-alive><component :is"Component" v-if"$route.meta.keepAlive"/></keep-alive><component…...

Linux的fwrite函数
函数原型: 向文件fp中写入writeBuff里面的内容 int fwrite(void*buffer,intsize,intcount,FILE*fp) /* * description : 对已打开的流进行写入数据块 * param ‐ ptr :指向 数据块的指针 * param ‐ size :指定…...
python udsoncan 详解
python udsoncan 详解 udsoncan 是一个Python库,用于实现汽车统一诊断服务(Unified Diagnostic Services,UDS)协议。UDS是一种用于汽车诊断的标准化通信协议,它定义了一系列的服务和流程,用于ECUÿ…...

基于自组织长短期记忆神经网络的时间序列预测(MATLAB)
LSTM是为了解决RNN 的梯度消失问题而诞生的特殊循环神经网络。该网络开发了一种异于普通神经元的节点结构,引入了3 个控制门的概念。该节点称为LSTM 单元。LSTM 神经网络避免了梯度消失的情况,能够记忆更长久的历史信息,更能有效地拟合长期时…...

240629_昇思学习打卡-Day11-Vision Transformer中的self-Attention
240629_昇思学习打卡-Day11-Transformer中的self-Attention 根据昇思课程顺序来看呢,今儿应该看Vision Transformer图像分类这里了,但是大概看了一下官方api,发现我还是太笨了,看不太明白。正巧昨天学SSD的时候不是参考了太阳花的…...

代码随想录-Day43
52. 携带研究材料(第七期模拟笔试) 小明是一位科学家,他需要参加一场重要的国际科学大会,以展示自己的最新研究成果。他需要带一些研究材料,但是他的行李箱空间有限。这些研究材料包括实验设备、文献资料和实验样本等…...

C++——探索智能指针的设计原理
前言: RAII是资源获得即初始化, 是一种利用对象生命周期来控制程序资源地手段。 智能指针是在对象构造时获取资源, 并且在对象的声明周期内控制资源, 最后在对象析构的时候释放资源。注意, 本篇文章参考——C 智能指针 - 全部用法…...

办公效率新高度:利用办公软件实现文件夹编号批量复制与移动,轻松管理文件
在数字化时代,我们的工作和生活都围绕着海量的数据和文件展开。然而,随着数据量的不断增加,如何高效地管理这些数字资产成为了摆在我们面前的一大难题。今天,我要向您介绍一种革命性的方法——利用办公软件实现文件夹编号批量复制…...

Windows kubectl终端日志聚合(wsl+ubuntu+cmder+kubetail)
Windows kubectl终端日志聚合 一、kubectl终端日志聚合二、windows安装ubuntu子系统1. 启用wsl支持2. 安装所选的 Linux 分发版 三、ubuntu安装kubetail四、配置cmder五、使用 一、kubectl终端日志聚合 k8s在实际部署时,一般都会采用多pod方式,这种情况下…...

【MySQL】数据库——事务
一.事务概念 事务是一种机制、一个操作序列,包含了一组数据库操作命令,并且把所有的命令作为一个整体一起向系统提交或撤销操作请求,即这一组数据库命令要么都执行,要么都不执行事务是一个不可分割的工作逻辑单元,在数…...
python代码缩进规范(2空格或4空格)
C、C、Java、C#、Rust、Go、JavaScript 等常见语言都是用"{“和”}"来标记一个块作用域的开始和结束,而Python 程序则是用缩进来表示块作用域的开始和结束: 作用域是编程语言里的一个重要的概念,特别是块作用域,编程语言…...

前后端分离的后台管理系统开发模板(带你从零开发一套自己的若依框架)上
前言: 目前,前后端分离开发已经成为当前web开发的主流。目前最流行的技术选型是前端vue3后端的spring boot3,本次。就基于这两个市面上主流的框架来开发出一套基本的后台管理系统的模板,以便于我们今后的开发。 前端使用vue3ele…...

【C++ | 委托构造函数】委托构造函数 详解 及 例子源码
😁博客主页😁:🚀https://blog.csdn.net/wkd_007🚀 🤑博客内容🤑:🍭嵌入式开发、Linux、C语言、C、数据结构、音视频🍭 🤣本文内容🤣&a…...
iCloud邮件全攻略:设置与使用终极指南
标题:iCloud邮件全攻略:设置与使用终极指南 摘要 iCloud邮件是Apple提供的一项邮件服务,允许用户在所有Apple设备上访问自己的邮件。本文将详细介绍如何在各种设备和邮件客户端上设置和使用iCloud邮件账户,确保用户能够充分利用…...

【计算机毕业设计】基于微信小程序的电子购物系统的设计与实现【源码+lw+部署文档】
包含论文源码的压缩包较大,请私信或者加我的绿色小软件获取 免责声明:资料部分来源于合法的互联网渠道收集和整理,部分自己学习积累成果,供大家学习参考与交流。收取的费用仅用于收集和整理资料耗费时间的酬劳。 本人尊重原创作者…...
CSS实现动画
CSS实现动画主要有三种方式:transition,transform,和animation1。以下是一些详细的逻辑,实例和注意事项: Transition:transition可以为一个元素在不同状态之间切换的时候定义不同的过渡效果。例如ÿ…...

Python+Pytest+Allure+Yaml+Jenkins+GitLab接口自动化测试框架详解
PythonPytestAllureYaml接口自动化测试框架详解 编撰人:CesareCheung 更新时间:2024.06.20 一、技术栈 PythonPytestAllureYamlJenkinsGitLab 版本要求:Python3.7.0,Pytest7.4.4,Allure2.18.1,PyYaml6.0 二、环境配置 安装python3.7&…...

[OtterCTF 2018]Bit 4 Bit
我们已经发现这个恶意软件是一个勒索软件。查找攻击者的比特币地址。** 勒索软件总喜欢把勒索标志丢在显眼的地方,所以搜索桌面的记录 volatility.exe -f .\OtterCTF.vmem --profileWin7SP1x64 filescan | Select-String “Desktop” 0x000000007d660500 2 0 -W-r-…...

计算机视觉全系列实战教程 (十四):图像金字塔(高斯金字塔、拉普拉斯金字塔)
1.图像金字塔 (1)下采样 从G0 -> G1、G2、G3 step01:对图像Gi进行高斯核卷积操作(高斯滤波)step02:删除所有的偶数行和列 void cv::pyrDown(cv::Mat &imSrc, //输入图像cv::Mat &imDst, //下采样后的输出图像cv::Si…...
Cursor实现用excel数据填充word模版的方法
cursor主页:https://www.cursor.com/ 任务目标:把excel格式的数据里的单元格,按照某一个固定模版填充到word中 文章目录 注意事项逐步生成程序1. 确定格式2. 调试程序 注意事项 直接给一个excel文件和最终呈现的word文件的示例,…...

树莓派超全系列教程文档--(61)树莓派摄像头高级使用方法
树莓派摄像头高级使用方法 配置通过调谐文件来调整相机行为 使用多个摄像头安装 libcam 和 rpicam-apps依赖关系开发包 文章来源: http://raspberry.dns8844.cn/documentation 原文网址 配置 大多数用例自动工作,无需更改相机配置。但是,一…...
椭圆曲线密码学(ECC)
一、ECC算法概述 椭圆曲线密码学(Elliptic Curve Cryptography)是基于椭圆曲线数学理论的公钥密码系统,由Neal Koblitz和Victor Miller在1985年独立提出。相比RSA,ECC在相同安全强度下密钥更短(256位ECC ≈ 3072位RSA…...
在鸿蒙HarmonyOS 5中实现抖音风格的点赞功能
下面我将详细介绍如何使用HarmonyOS SDK在HarmonyOS 5中实现类似抖音的点赞功能,包括动画效果、数据同步和交互优化。 1. 基础点赞功能实现 1.1 创建数据模型 // VideoModel.ets export class VideoModel {id: string "";title: string ""…...

练习(含atoi的模拟实现,自定义类型等练习)
一、结构体大小的计算及位段 (结构体大小计算及位段 详解请看:自定义类型:结构体进阶-CSDN博客) 1.在32位系统环境,编译选项为4字节对齐,那么sizeof(A)和sizeof(B)是多少? #pragma pack(4)st…...

【HarmonyOS 5.0】DevEco Testing:鸿蒙应用质量保障的终极武器
——全方位测试解决方案与代码实战 一、工具定位与核心能力 DevEco Testing是HarmonyOS官方推出的一体化测试平台,覆盖应用全生命周期测试需求,主要提供五大核心能力: 测试类型检测目标关键指标功能体验基…...

LeetCode - 394. 字符串解码
题目 394. 字符串解码 - 力扣(LeetCode) 思路 使用两个栈:一个存储重复次数,一个存储字符串 遍历输入字符串: 数字处理:遇到数字时,累积计算重复次数左括号处理:保存当前状态&a…...

相机从app启动流程
一、流程框架图 二、具体流程分析 1、得到cameralist和对应的静态信息 目录如下: 重点代码分析: 启动相机前,先要通过getCameraIdList获取camera的个数以及id,然后可以通过getCameraCharacteristics获取对应id camera的capabilities(静态信息)进行一些openCamera前的…...

Spring数据访问模块设计
前面我们已经完成了IoC和web模块的设计,聪明的码友立马就知道了,该到数据访问模块了,要不就这俩玩个6啊,查库势在必行,至此,它来了。 一、核心设计理念 1、痛点在哪 应用离不开数据(数据库、No…...

微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据
微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据 Power Query 具有大量专门帮助您清理和准备数据以供分析的功能。 您将了解如何简化复杂模型、更改数据类型、重命名对象和透视数据。 您还将了解如何分析列,以便知晓哪些列包含有价值的数据,…...