gpt4 book ai didi

assembly - 迷你操作系统中针对不同信号量的现成队列的优缺点

转载 作者:行者123 更新时间:2023-12-03 12:53:13 28 4
gpt4 key购买 nike

在操作系统中,我们最初有一个线程就绪队列,只注意一个队列。为什么对每个信号量使用不同的队列会更好。利弊可能与效率,复杂性或其他有关。

最佳答案

好的,要回答这个问题,请引用我的answer到SO上的另一个问题,关于“Linux时分进程或线程吗?”,让我们从这里使用x86处理器和C代码的虚构内核的结构继续。 ..我已修改此结构,以考虑到信号量

struct regs {
int eax,ebx,ecx,edx,es,ds,gs,fs,cs,ip,标志;
struct tss * task_sel;
}

struct mem_loc {
int * phys_mem_begin;
int * phys_mem_end;
int *信号量;
}
结构线程{
struct regs * regs;
struct mem_loc * memlocTracker;
int parent_id;
结构线程* next;
}
结构任务{
struct regs * regs;
struct mem_loc * task_mem;
int *文件句柄;
int优先级;
int * num_threads;
整数量子
int持续时间;
int start_time,end_time;
int parent_id;
结构线程* task_thread;
/* ... */
struct任务* next;
}

struct queue_ready {
结构任务* task_id;
struct queue_ready *下一步;
}

在这里,我认为看起来更好,对,相对于上面链接的上一个答案,让我们看看当涉及到队列时会发生什么,现在,看看这个queue_ready结构,现在,假设在那个结构中有一个任务是通过内核设置,更精确的queue_ready链表是基于priority结构本身的task字段的,例如,优先级为1,可以执行了。

让我们看一个基于C的假想调度程序,如下所示,好吧,它可能是笨拙的,很容易被挑剔,但将其放在一边可以专注于问题的方面...

静态void Scheduler(void){
struct queue_ready * rdy_sched;

while(1) {

为(rdy_sched = head_queue_ready;
rdy_sched!= NULL;
rdy_sched = rdy_sched-> next){

如果(时间> = rdy_sched-> task_id-> start_time + rdy_sched-> task_id-> quantum){

save_cpu_task_state(&task_reg-> regs);

save_mem_task_state(&rdy_sched-> task_id-> task_mem);

}别的{
struct regs * task_reg = rdy_sched-> task_id-> regs;

struct mem_loc * mem_task = rdy_sched-> task_id-> task_mem;

load_cpu_task_state(task_reg);

load_mem_task_state(mem_task);

jmp_and_exec_cpu_task(task_reg-> cs,task_reg-> ip);

save_cpu_task_state(&rdy_sched-> task_id-> regs);

如果(rdy_sched-> task_id-> num_threads> 0){

结构线程* threads = rdy_sched-> task_id-> task_thread;

while(threads-> next!= NULL){

struct regs * thread_reg =线程-> regs;

load_cpu_thread_state(thread_reg);

如果(threads-> memlocTracker-> semaphores){

/*与信号量相关的房屋保管*/
lock_memory(threads-> memlocTracker);

jmp_and_exec_cpu_thread(thread_reg-> cs,thread_reg-> ip);

save_cpu_thread_state(&thread-> regs);

unlock_memory(threads-> memlocTracker);

}别的{

jmp_and_exec_cpu_thread(thread_reg-> cs,thread_reg-> ip);

save_cpu_thread_state(&thread-> regs);
}
}
}
}
}
}
}

内核的调度程序看起来过于复杂,但并非如此,我在代码中唯一遗漏的是优先级...对于讨论OP的问题,现在可以将其忽略。

让我们分解一下调度程序函数scheduler ...但是首先快速浏览一下scheduler函数中使用了哪些函数:

  • 'load_cpu_task_state'和'load_cpu_thread_state'-加载CPU寄存器的状态,为简洁起见,它们分别用于任务和线程。
  • 'load_mem_task_state'和'save_mem_task_state'-分别从该任务的phys_mem_begin结构的phys_mem_endmem_loc字段指定的磁盘页面上加载或保存内存状态,也许到磁盘上的页面。为简便起见,这将加载该任务拥有的所有内存,包括线程。
  • 'jmp_and_exec_cpu_task'和'jmp_and_exec_cpu_task'-导致内核发出魔术,以跳转到该任务的cs结构的指定ipreg寄存器,这对于线程也分别相同。
  • 'save_cpu_task_state'和'save_cpu_thread_state'-这导致内核分别保存任务和线程的CPU寄存器状态。
  • 'lock_memory'和'unlock_memory'-这是用于使用即将使用的信号量来锁定存储区域...

  • 现在, scheduler永远运行,并遍历任务的链接列表,需要进行检查以查看任务是否运行了一段时间并超过了 quantum的数量(例如20ms)。然后,它将保存cpu状态和内存状态(可能已保存到磁盘上的页面文件),准备继续执行列表中的下一个任务。

    如果还有剩余时间,它将加载任务的CPU寄存器并加载内存(可能是从页面加载),然后跳转到最后一条指令指针和代码段所在的位置(分别为IP和CS),然后继续执行任务。

    如果所述任务具有线程,则它将遍历线程的链表,加载CPU寄存器,并跳入代码以恢复线程执行,然后保存CPU寄存器。

    现在,这变得有些繁琐,尤其是在此玩具操作系统中,如何管理信号量!嗯,看起来就像是头大的头痛笼罩着OS开发人员的脑袋...。

    我想像一下,如果内存部分被锁定, semaphores将为非NULL,并且将由运行时管理器以请求信号量的用户态代码获取,它将锁定内存以防止线程践踏数据,并跳转到该线程的代码中,并在该线程的持续时间完成时将其解锁,并保存该线程的CPU寄存器的状态...

    结论:

    这是我基于此得出的结论,至于为我提供支持的事实,由于无法详尽地讲授这一理论,因此我无法在显微镜下从无数书籍中对它进行检查,从美国的一所大学一直到从我的观察和我的理解中,这是从新西兰大学获得的,正如您从该代码中看到的那样,这是开发人员在编写此代码(而不是唯一的)时所施加的约束,设计决策(包括硬件和软件)的复杂性和负担。

    如果存在更多队列,要回答这种情况下的信号量要复杂得多,就像我一直努力将其保持在一个队列 queue_ready一样,在内存使用,磁盘使用以及最重要的时间使用方面的成本因数会增加由于内核必须跟踪那些缓解因素,另一个同样重要的因素是将有多少优先级,所以我认为为不同的信号量准备就绪队列不是一个可行的选择。

    关于assembly - 迷你操作系统中针对不同信号量的现成队列的优缺点,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2907004/

    28 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com