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

曲靖企业网站建设嘉兴百度快照优化排名

曲靖企业网站建设,嘉兴百度快照优化排名,免费域名注册,地方网站开发大家好#xff0c;我是老吴。今天要分享的是抢占相关的基础知识。本文以内核抢占为引子#xff0c;概述一下 Linux 抢占的图景。我尽量避开细节问题和源码分析。什么是内核抢占#xff1f;别急#xff0c;咱们慢慢来。先理解抢占 (preemption) 这个概念#xff1a;involun… 大家好我是老吴。今天要分享的是抢占相关的基础知识。本文以内核抢占为引子概述一下 Linux 抢占的图景。我尽量避开细节问题和源码分析。什么是内核抢占别急咱们慢慢来。先理解抢占 (preemption) 这个概念involuntarily suspending a running process is called preemption夺取一个进程的 cpu 使用权的行为就叫做抢占。根据是否可以支持抢占多任务操作系统 (multitasking operating system) 分为 2 类1、cooperative multitasking os这种 os进程会一直运行直到它自愿停下来。这种自愿停止运行自己的行为称为 yielding。协作式多任务系统一听就知道这是一个乌托邦式的系统只有当所有进程都很 nice 并乐意经常 yielding 时系统才能正常工作。如果某个进程太傻或者太坏系统很快就完蛋了。2、preemptive multitasking os这种 os会有一个调度器 (scheduler其实就是一段用于调度进程的程序)scheduler 决定进程何时停止运行以及新进程何时开始运行。当一个进程的 cpu 使用权被 scheduler 分配给另一个进程时就称前一个进程被抢占了。你可以把 sheduler 想象成非常智能的交警交警按照一定的交通规则、当前的交通状况以及车辆的优先级 (救护车之类的)决定了哪些车可以行驶、哪些车要停下来等待。很明显现阶段preemptive os 优于 cooperative os。所以 Linux 被设计成 preemptive。抢占的核心操作包括 2 个步骤1、从用户态陷入到内核态 (trap kernel)3 个路径a. 系统调用本质是 soft interrupt通常就是一条硬件指令 (x86 的 int 0x80)。b. 硬件中断最典型的就是会周期性发生的 timer 中断或者其他各种外设中断.c. exception例如 page fault、div 0。点击查看大图2、陷入到内核态后在合适的时机下调用 sheduler 选出一个最重要的进程如果被选中的不是当前正在运行的进程的话就会执行 context switch 切换到新的进程。根据抢占时机点的不同抢占分为 2 种类型1、user preemption这里的 user 并不是指在 user-space 里进行抢占而是指在返回 user-space 前进行抢占具体的When returning to user-space from a system callWhen returning to user-space from an interrupt handler即从 system call 和 interrupt handler 返回到 user-space 前进行抢占这时仍然是在 kernel-space 里抢占是需要非常高的权限的事情user-space 没权利也不应该干这事。2、kernel preemptionLinux 2.6 之前是不支持内核抢占的。这意味着当处于用户空间的进程请求内核服务时在该进程阻塞进入睡眠等待某事通常是 I/O或系统调用完成之前不能调度其他进程。支持内核抢占意味着当一个进程在内核里运行时另一个进程可以抢占第一个进程并被允许运行即使第一个进程尚未完成其在内核里的工作。支持内核抢占 vs 不支持内核抢占举个例子点击查看大图在上图中进程 A 已经通过系统调用进入内核也许是对设备或文件的 write() 调用。内核代表进程 A 执行时具有更高优先级的进程 B 被中断唤醒。内核抢占进程 A 并将 CPU 分配给进程 B即使进程 A 既没有阻塞也没有完成其在内核里的工作。内核抢占的时机When an interrupt handler exits, before returning to kernel-spaceWhen kernel code becomes preemptible againIf a task in the kernel explicitly calls schedule()If a task in the kernel blocks (which results in a call to schedule() )为什么要引入内核抢占根本原因trade-offs between latency and throughput在系统延迟和吞吐量之间进行权衡。并不是说内核抢占就是绝对的好使用什么抢占机制最优是跟你的应用场景挂钩的。如果不是为了满足用户内核其实是完全不想进行进程切换的因为每一次 context switch都会有 overhead这些 overhead 就是对 cpu 的浪费意味着吞吐量的下降。但是如果你想要系统的响应性好一点就得尽量多的允许抢占的发生这是 Linux 作为一个通用操作系统所必须支持的。当你的系统做到随时都可以发生抢占时系统的响应性就会非常好。为了让用户根据自己的需求进行配置Linux 提供了 3 种 Preemption Model。点击查看大图CONFIG_PREEMPT_NONEy不允许内核抢占吞吐量最大的 Model一般用于 Server 系统。点击查看大图CONFIG_PREEMPT_VOLUNTARYy在一些耗时较长的内核代码中主动调用cond_resched()让出CPU对吞吐量有轻微影响但是系统响应会稍微快一些。点击查看大图CONFIG_PREEMPTy除了处于持有 spinlock 时的 critical p其他时候都允许内核抢占响应速度进一步提升吞吐量进一步下降一般用于 Desktop / Embedded 系统。点击查看大图另外还有一个没有合并进主线内核的 Model: CONFIG_PREEMPT_RT这个模式几乎将所有的 spinlock 都换成了 preemptable mutex只剩下一些极其核心的地方仍然用禁止抢占的 spinlock所以基本可以认为是随时可被抢占。点击查看大图抢占前的检查这里的检查是同时针对所有的 preemption 的。如果你理解了前面的 4 种 preempiton model 的话应该能感觉到其实是不用太严格区分 user / kernel preemption所有抢占的作用和性质都一样降低 lantency完全可以将它们一视同仁。抢占的发生要同时满足两个条件需要抢占;能抢占;1、是否需要抢占判断是否需要抢占的依据是thread_info 的成员 flags 是否设置了 TIF_NEED_RESCHED 标志位。相关的 APIset_tsk_need_resched() 用于设置该 flag。tif_need_resched() 被用来判断该 flag 是否置位。resched_curr(struct rq *rq)标记当前 runqueue 需要抢占。2、是否能抢占抢占发生的前提是要确保此次抢占是安全的 (preempt-safe)。什么才是 preempt-safe不产生 race condition / deadlock。值得注意的是只有 kernel preemption 才有被禁止的可能而 user preemption 总是被允许因此这时马上就要返回 user space 了肯定是处于一个可抢占的状态了。在引入内核抢占机制的同时引入了为 thread_info 添加了新的成员preempt_count 用来保证抢占的安全性获取锁时会增加 preempt_count释放锁时则会减少。抢占前会检查 preempt_count 是否为 0为 0 才允许抢占。相关的 APIpreempt_enable()使能内核抢占可嵌套调用。preempt_disable()关闭内核抢占可嵌套调用。preempt_count()返回 preempt_count。什么场景会设置需要抢占 (TIF_NEED_RESCHED 1)通过 grep resched_curr 可以找出大多数标记抢占的场景。下面列举的是几个我比较关心的场景。1、周期性的时钟中断时钟中断处理函数会调用 scheduler_tick()它通过调度类(scheduling class) 的 task_tick 方法 检查进程的时间片是否耗尽如果耗尽则标记需要抢占// kernel/sched/core.c void scheduler_tick(void) {[...]curr-sched_class-task_tick(rq, curr, 0);[...] } Linux 的调度策略被封装成调度类例如 CFS、Real-Time。CFS 调度类的 task_tick() 如下// kernel/sched/fair.c task_tick_fair()- entity_tick()- resched_curr(rq_of(cfs_rq)); 2、唤醒进程的时候当进程被唤醒的时候如果优先级高于 CPU 上的当前进程就会触发抢占。相应的内核代码中try_to_wake_up() 最终通过 check_preempt_curr() 检查是否标记需要抢占// kernel/sched/core.c void check_preempt_curr(struct rq *rq, struct task_struct *p, int flags) {const struct sched_class *class;if (p-sched_class  rq-curr-sched_class) {rq-curr-sched_class-check_preempt_curr(rq, p, flags);} else {for_each_class(class) {if (class  rq-curr-sched_class)break;if (class  p-sched_class) {resched_curr(rq);break;}}}[...] } 参数 p 指向被唤醒进程rq 代表抢占的 CPU。如果 p 的调度类和 rq 当前的调度类相同则调用 rq 当前的调度类的 check_preempt_curr() (例如 cfs 的 check_preempt_wakeup()) 来判断是否要标记需要抢占。如果 p 的调度类 rq 当前的调度类则用 resched_curr() 标记需要抢占反之则不标记。3、新进程创建的时候如果新进程的优先级高于 CPU 上的当前进程会需要触发抢占。相应的代码是 sched_fork()它再通过调度类的 task_fork() 标记需要抢占// kernel/sched/core.c int sched_fork(unsigned long clone_flags, struct task_struct *p) {[...]if (p-sched_class-task_fork)p-sched_class-task_fork(p);[...] }// kernel/sched/fair.c static void task_fork_fair(struct task_struct *p) {[...]if (sysctl_sched_child_runs_first  curr  entity_before(curr, se)) {resched_curr(rq);}[...] } 4、进程修改 nice 值的时候如果修改进程 nice 值导致优先级高于 CPU 上的当前进程也要标记需要抢占代码见 set_user_nice()。// kernel/sched/core.c void set_user_nice(struct task_struct *p, long nice) {[...]// If the task increased its priority or is running and lowered its priority, then reschedule its CPUif (delta  0 || (delta  0  task_running(rq, p)))resched_curr(rq); } 还有很多场景这里就不一一列举了。什么场景下要禁止内核抢占 (preempt_count 0)有几种场景是明确需要关闭内核抢占的。1、访问 Per-CPU data structures 的时候看下面这个例子struct this_needs_locking tux[NR_CPUS]; tux[smp_processor_id()]  some_value; /* task is preempted here... */ something  tux[smp_processor_id()]; 如果抢占发生在注释所在的那一行当进程再次被调度时smp_processor_id() 值可能已经发生变化了这种场景下需要通过禁止内核抢占来做到 preempt safe。2、访问 CPU state 的时候这个很好理解你正在操作 CPU 相关的寄存器以进行 context switch 时肯定是不能再允许抢占。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());sched_update_worker(tsk); } 3、持有 spinlock 的时候支持内核抢占这意味着进程有可能与被抢占的进程在相同的 critical p 中运行。为防止这种情况当持有自旋锁时要禁止内核抢占。static inline void __raw_spin_lock(raw_spinlock_t *lock) {preempt_disable();spin_acquire(lock-dep_map, 0, 0, _RET_IP_);LOCK_CONTENDED(lock, do_raw_spin_trylock, do_raw_spin_lock); } 还有很多场景这里就不一一列举了。真正执行抢占的地方这部分是 platform 相关的下面以 ARM64 Linux-5.4 为例快速看下执行抢占的具体代码。执行 user preemption系统调用和中断返回用户空间的时候它们都是在 ret_to_user() 里判断是否执行用户抢占。// arch/arm64/kernel/entry.S ret_to_user() // 返回到用户空间work_pending()do_notify_resume()schedule() // arch/arm64/kernel/signal.c asmlinkage void do_notify_resume(struct pt_regs *regs,unsigned long thread_flags) {do {[...]// 检查是否要需要调度if (thread_flags  _TIF_NEED_RESCHED) {local_daif_restore(DAIF_PROCCTX_NOIRQ);schedule();} else {[...]} while (thread_flags  _TIF_WORK_MASK); } 执行 kernel preemption中断返回内核空间的时候// arch/arm64/kernel/entry.S el1_irqirq_handlerarm64_preempt_schedule_irqpreempt_schedule_irq__schedule(true) // kernel/sched/core.c /* This is the entry point to schedule() from kernel preemption */ asmlinkage __visible void __sched preempt_schedule_irq(void) {[...]do {preempt_disable();local_irq_enable();__schedule(true);local_irq_disable();sched_preempt_enable_no_resched();} while (need_resched());exception_exit(prev_state); } 内核恢复为可抢占的时候前面列举了集中关闭抢占的场景当离开这些场景时会恢复内核抢占。例如 spinlock unlock 时static inline void __raw_spin_unlock(raw_spinlock_t *lock) {spin_release(lock-dep_map, 1, _RET_IP_);do_raw_spin_unlock(lock);preempt_enable();  // 使能抢占时如果需要就会执行抢占 }// include/linux/preempt.h #define preempt_enable() \ do { \barrier(); \if (unlikely(preempt_count_dec_and_test())) \__preempt_schedule(); \ } while (0) 内核显式地要求调度的时候内核里有大量的地方会显式地要求进行调度最常见的是cond_resched() 和 sleep()类函数它们最终都会调用到 __schedule()。内核阻塞的时候例如 mutexsemwaitqueue 获取不到资源或者是等待 IO。这种情况下进程会将自己的状态从 TASK_RUNNING 修改为 TASK_INTERRUPTIBLE然后调用 schedule() 主动让出 CPU 并等待唤醒。// block/blk-core.c static struct request *get_request(struct request_queue *q, int op,int op_flags, struct bio *bio,gfp_t gfp_mask) {[...]prepare_to_wait_exclusive(rl-wait[is_sync], wait,TASK_UNINTERRUPTIBLE);io_schedule();  // 会调用 schedule();[...] } 相关参考《Linux Kernel Development, Third Edition》《Understanding the Linux Kernel, Third Edition》《Linux Device Drivers, Third Edition》《深入理解 Linux 设备驱动程序内核机制》《Embedded Linux Primer》https://www.kernel.org/doc/Documentation/preempt-locking.txt推荐阅读专辑|Linux文章汇总专辑|程序人生专辑|C语言我的知识小密圈关注公众号后台回复「1024」获取学习资料网盘链接。欢迎点赞关注转发在看您的每一次鼓励我都将铭记于心~嵌入式Linux微信扫描二维码关注我的公众号
http://www.sadfv.cn/news/34680/

相关文章:

  • 网站建设费用怎么做分录wordpress 深色主题
  • 做百度网站排名软件企业网站报价模板
  • 网站是先备案 还是先做网站中国建筑工程网施工资料
  • 网站建设客户功能详细要求制作班徽的小程序
  • 网站内容很少如何做seoWordpress虚拟域名
  • 企业建设网站的好处有哪些免费的网站建设
  • 建网站都用什么字体wordpress 修改评论框
  • jsp网站开发什么框架室内设计联盟电脑版
  • 360广告联盟怎么做网站seochan是什么意思
  • 三明鑫龙建设工程网站网页制作设计模板
  • 人事处网站建设绩效目标概述怎么在网上卖东西到国外
  • dw做网站字体做多大项目网站有哪些
  • 安徽合肥做网站wordpress弹窗网页
  • 可以做彩页的网站美容加盟的网站建设
  • html做简单网站实例福州网页定制
  • 贵州省城乡和住房建设厅官方网站吾索源码网
  • 网站建设工作领导小组网站代码规范性
  • 美的集团网站建设建设网站外包
  • 福田莲花网站建设郑州专业做淘宝网站
  • 网站建设银行北京冬奥会纪念币发行时间企业官网建设
  • wordpress怎么建立空白文档河南seo优化
  • 鲜花网网站开发的意义网站开发技术服务协议
  • Joomla外贸网站模板wordpress好用么
  • 万润 企业网站建设大岭山网站仿做
  • 免费建站系统有哪些莱芜二手房出售信息最新房源
  • 产品营销型网站建设携程的网站建设项目规划书
  • 南平做网站中山网站建设文化
  • 阿里云 做网站东莞网站建设优化方案
  • 怎么样将网站内容做的漂亮即墨网站建设招聘
  • 关于建设公司网站的建议不会写代码怎么做网站