zoukankan      html  css  js  c++  java
  • [Operating System] {ud923} P2L4: Thread Design Considerations

    These are older papers, but they provide us with some historic information on how threading system evolved over time.

    Sun/Solaris Papers

    First, let's start by revisiting the illustration we used in the threads and concurrency lecture.

    There, we explained that threads can be supported at user level, and at kernel level, or both.

    Supporting threads at kernel level means that the OS kernel itself is multithreaded. 

    Supporting threads at user level means that there's a user-level library, that is linked with the application. 

    (In fact, different processes may use entirely different user-level libraries that have different ways to represent threads that support the different scheduling mechanisms, et cetera.)

    We also discussed several mechanisms, how user level threads can be mapped onto the underlying kernel level threads (1to1, Many to 1, many2many )

      

    all the information about the state of this process is contained in its process control block.

    however, we dont want to replicate this entire (PCB) data structure with just so as to represent different stack and register values for the kernel-level entities.

    So, we will start splitting up this PCB structure to separate the information that's specifically useful to represent the execution state of kernel-level threads. 

    ※ virtual address mapping

    ※ stack

    ※ registers

    ※ virtual address mapping

    ※ stack

    ※ registers

     

    here we first assume there's only one CPU. Multithreads make the situation like there are multi virtual CPUs.

    3 user level processes

    7 kernels

    3 CPUs

    -----------------

    When the kernel needs to schedule, or context switch among kernel-level threads that belong to different processes, 

    it can quickly determine that they point to a different process control block.

    So they will have different virtual address mappings, and therefore can easily decide that it needs to invalidate the existing address mappings and restore new ones.

     

    There's a portion of this PCB information that we want to preserve, like all the virtural address mappings.

    But then there's a portion of it, that's really specific to the particular kernel level thread and it depends on what is the user level thread that's currently executing.

    So we will split up the information that was contained originally in the PCB, and we will separate it into hard process state that's relevant for all the user level threads that execute within that process.

    And the lightweight portion of process state is just a subset of the user level threads that are currently associated with a particular kernel-level thread.

     

     

    Errata

    ktread_worker is mispelled; it should read kthread_worker. You can find this struct defined in Linux Cross References such as free-electrons.com.

     

    credit to Stein and Shah Paper, Figure 1.

    it quickly illustrates quickly the threading model supported in the OS.

    Going from the bottom up, the OS is intended for multiprocesser systems, with multiple CPUs

    and the kernel itself is multi-threaded.

    Both one2one and many2many mapppings are supported.

    each kernel level thread that's executing a user-level thread, has a lightweight process data structure associated with it.

    When a thread is created, the library returns a thread ID.

    And this is not a direct pointer to the actual thread data structure

    Instead, it's an index in a table of pointers.

    it is the table pointers that in turn point to the actual thread data structure.

    With this data structure, if the memory is corrupt, we can still get some useful feedback.

    Also this table of pointers help the scheduler to coordinate.

    The problem is stack can grow and overflow.

    The solution to separate threads with a red zone.

    Sun/Solaris Papers

    Sun/Solaris Figures

     

     

    at any given point of time, the actual level of concurrency is 2. 

    first there's only one kernel level thread (by default), and the accompanying lightweight thread.

     

    Then the process will request additional kernel-level thread, and the way it's done is that 

    the kernel now supports a system call, called "set_concurrency", creating a new thread.

    Now the 2 user level threads, mapping to the kernel level threads, need to perform I/O operation.

    So the kernel level threads are blocked as well.

     kernel should notify the user level library before it blocks the kernel-level threads. 

    Then the user level library can look at its run queue, it can see that it has multiple runnable user-level threads, 

    and in response can let the kernel know, call a system call, to request more kernel level threads (KLT) or light weight processes.

    Now the kernel allocates an additional KLT, and the 

    at a later time, the kernel knows that one thread is pretty much constantly idle, 

    because we said that's the natural state of this particular application.

    Maybe the kernel can tell the user level library that the KLT is no longer available

      

     

     

    complex situation: multiple CPUs

    at first, T2 holds a block, and T3 is blocked, alghough having the highest priority.

    and also T1 is running on the other kernel level thread on the other cpu

      

    at some point T2 releases that mutex. The user level library is notified.

    Now T1 T2 and T3 are runnable.

    So we need to make sure that T3 gets to execute.

    T1 needs to be preempted, and context switched to T3

     However, T1 is on the other CPU and we need to notify this other CPU to update its registers and program counters.

     We can not directly modify registers on one CPU when executing on another CPU

    What we need to do instead is to send some signal or interrupt from the context of one thread on one CPU to the other thread ont the other CPU. 

     Then CPU-2 will schedule T3 to be executed and T1 to be blocked.

    Another interesting case: adaptive mutexes

    here the information of certain mutex is pretty useful and we have stored it in the data structure.

     

    interrupts vs signals 

     

     

    interrupt 

    MSI: message signal interrupter

    signals

     

    illegal memory requirement => signal being generated => called SiGSEGV

     disable interrupts and signals

      

    PC => some instruction in thread

    if the handler code needs some state that other threads in the system may be accessing, then we have to use mutexes.

    howeve, if that thread to be interrupted have already had that mutex, then we have a deadlock. (WHY???)

    one possible solution is to keep handler code simple to avoid the deadlock

     masks allow us to enable and disable the interrupt or signal dynamically.

    so the masks should be checked first.

    inturrupts on multicore systems


     

     

     http://pubs.opengroup.org/onlinepubs/9699919799/

    in sunOS, the interrupts become full fledged threads

    but there're some concerns

    Interrupts: Top vs. Bottom Half

    this allows the interrupts to be arbitrarily complex and not to be worried about deadlocks.

    top half: executes immediately when an interrupt occurs

    bottom half,: like any other thread, can be scheduled for a later time if blocked

    Performance of Threads as Interrupts 

    Threads and Signal Handling

     

    Sun/Solaris Papers

    Sun/Solaris Figures


     

    Threads and Signal Handling: Case 1

    Threads and Signal Handling: Case 2

     

    Threads and Signal Handling: Case 3

    Threads and Signal Handling: Case 4

     

     Tasks in Linux

    pid is not process id (name is misleading)

    pid => task identifier

    tgid => task group id 


     https://stackoverflow.com/questions/3042717/what-is-the-difference-between-a-thread-process-task


     

     

  • 相关阅读:
    excel读取表,并将结果保存为键值对的字典列表;
    [置顶] MQ选型对比RabbitMQ RocketMQ ActiveMQ Kafka
    ActiveMQ持久化消息的三种方式
    getConstructor、getDeclaredConstructor区别
    JavaEE 保存文件获取绝对路径getResource("")和servletContext.getRealPath("/")
    Java中getResourceAsStream的用法
    Java 输出流中的flush方法
    Java之关闭流
    Java中的字节流、缓冲流
    java原生序列化和Kryo序列化性能比较
  • 原文地址:https://www.cnblogs.com/ecoflex/p/10898900.html
Copyright © 2011-2022 走看看