PPT in Operating system

在学习并行程序设计之前,我们需要先了解 Program 、 Process 、 Thread 的定义,这边笔者举一个简单的例子:

当我们开启 APP 时,APP 会被载入到 Memory 中,而执行中的 APP 在一般情况下仅有一个执行绪 (Thread)。

在上面的例子中,尚未执行的 APP 就是 Program,执行中的 APP 则是 Process

Program

Program 在 Operating System 这门学科中并没有太多的介绍,读者可以把 Program 当成是可执行档 (*.out, *.elf, *.exe...) 即可。

Process

考虑上图,Process 在执行时,状态会不断的改变,Process 的状态共有:

  • new: Process 被初始化。
  • running: 正在被处理。
  • waiting: 等待某些条件成立,及 Blocked (待会儿就会提到)。
  • ready: 等待作业系统处理,此时作业系统可能将运算资源交给 I/O 或是其他 Process 了。
  • terminated: Process 完成执行。

记忆体配置

以 32 位元的电脑为例,每个暂存器有 32 个位元可以利用,这也代表它做多可以定址 4GB 的记忆体位置。

下图反映了当作业系统运行时,RAM 是如何被分配的:

Memory Layout

  • Stack
    存放函数的参数、区域变数等。

  • Heap
    记忆体扩充区,程序设计者可以运用 Heap 的空间让程序在记忆体使用上有更多的弹性。以 C 语言为例,使用 malloc 时,便会从 Heap 分配出一段空间:

    #include <stdlib.h>
    // ...
    int* p = (int*)malloc(sizeof(int));
    
  • Data

    • UBSS
      又称 bss segment (block started by symbol, bss),包含未明确初始的 global 和 static 变数,当执行程序时会将其这区段的记忆体初始为零。那之所以把未初始化的在分隔到一区段的原因是因为,当程序存放在硬碟中的时候,没有那个必要留空间存放这些未初始化的资料,只需要执行时去纪录位置和所需大小,在 run time 的时候利用 program loader 分配。
    • IBSS
      存放已正确初始化的 global 和 static 变数,当程序被读进记忆体中时,这些值就会从执行档被读入。
  • Text
    Text segment 存放最重要的东西: 可执行的机器码 (也就是编译组语後的结果,内容是一堆 0 和 1 )。

Process ID

为了方便追踪每个 Process,作业系统会分配给每个 Process 一个独立的 ID (又称 Process ID)。此外 Process 也会有一个纪录 Parent PID 的 PPID。

#include <unistd.h>
pid_t get_pid(void);
pid_t get_ppid(void);

Process Tree

Parent process 会建立 Children processes,而 Children processes 又可以建立自己的 Children processes 形成树状结构:

PID in PThread

再以 POSIX Thread 为例,可以用 pthread_self() 查询 PID :

#include <pthread.h>
#include <stdio.h>
 
void* thread_func(void *arg)
{
    printf("thread id=%lu\n", pthread_self());
    return arg;
}
 
int main(void)
{
    pid_t pid;
    pthread_t tid;
    pid = getpid();
    printf("process id=%d\n", pid);
    pthread_create(&tid, NULL, thread_func, NULL);
    pthread_join(tid,NULL);
    return 0;
}

补充 1 :
你可能会问 get_pid()pthread_self() 都会返回 PID 阿,那差别在哪?
答: POSIX Thread 是 User-level 的 Thread 而 get_pid() 可以查询 Kernel-level 的 Process ID。
补充 2 :
pthread_self() 的定义也可以在 Linux Programmer's Manual 中查到:

More details...

不只如此, Processes 还包含:

  • Running State
    前面已经提到:

  • File Descriptors
    该 Process 使用到的 File。

    注意!这里的 File 是指档案,而非文件。
    在 UNIX 的设计中,所有东西都是档案!

  • Arguments - 一个存放参数的文字列表,不同的程序语言都有不同的参数代入方法,以 C 语言为例:

    #include <stdio.h>
    int main(int argc, char *argv[]) {
      printf("We have %d arguments:\n", argc);
      for (int i = 0; i < argc; ++i) {
        printf("[%d] %s\n", i, argv[i]);
      }
      return 0;
    }
    

    将上面的程序编译过後并执行:

    gcc source.c
    ./a.out 1 2 3
    

    可以得到:

    We have 4 arguments:
    [0] ./a.out
    [1] 1
    [2] 2
    [3] 3
    
  • Environment List
    纪录环境变数,如下图:

Thread

Thread 是可被作业系统排程的最小单位,它被包含在 Process 中,一个 Process 可以拥有多个 Thread。

像是前面提到的 POSIX Thread 便可以让我们撰写具有多个执行绪的程序。

Thread 包含了以下内容:

  • Thread ID
  • Thread State
  • Program counter
  • Register set
  • Stack

而在同一个 Process 中的 Thread 共享以下资源:

  • Code section
  • Data section
  • OS Resources

图片取自该连结

User-level v.s. Kernel-level

常见的 User-level thread 有这些实作方法:

  • Pthread
  • Win32 Threads
  • JAVA Threads

常见的 Kernel-level thread:

  • Windows XP/2000
  • Solaris
  • Linux
  • Tru64 UNIX

而两者有以下差异:

  • 前者由程序编写者分配,後者由作业系统分配。
  • 作业系统察觉不到 User-level thread 的存在。

举一个例子,假设有两个 Process 存在,前者有 3 条 Thread,後者有 2 条 Thread。
如果作业系统采取均匀分配处理器资源的话,在不同的 Case 下会形成巨大的效能差异:

  • User-level thread
    因为作业系统无法察觉 User-level thread 的存在,纵使两个 Process 有不同数量的执行绪,仍只能分配到同样的处理器资源。
  • Kernel-level thread
    2 个 Process 共有 5 条执行绪,在作业系统能察觉的情况下,Process 1 能获得 3 x 20% 的处理器资源,Process 2 则获得剩余的 40% 处理器资源。

Thread 的优势

无论是建立成本或是 Context switch 的成本,Thread 都比 Process 有更显着的效能。
不过也因为在同个 Process 底下的 Thread 享有 Code section 以及 Data,若设计不良可能会造成 Race condition 以及 Critical section 的原因。对於这个部分,会在之後的议题做更详细的介绍。

执行绪一定是越多越好吗?

设计优良的多执行绪程序的确能榨出更多的处理器资源,但多执行绪程序在设计上需要特别小心,错误的设计可能会导致整个 Process 被 Block。此外,建立执行绪以及内文交换都是需要成本的,如果程序的功能非常单一、简单,将其改写成多执行绪程序有时候反而会造成性能下降!

Reference


<<:  [Day26] HTB Jerry

>>:  [Day26] Micro Services

如何衡量万事万物 (3) 厘清问题 & 量化不确定性

衡量前的议题 这项衡量要支援什麽决策? 要衡量的事物,若用可观察到的结果来定义,会是什麽? 这个事物...

.Net Core Web Api_笔记08_HTTP资源操作模式PATCH

在HTTP请求中 PUT 跟 PATCH 都代表更新 然後他们之间比较主要的差异在於 PUT 用在更...

Day 04 | 渲染元件

要渲染 Livewire 元件也非常简单,主要会分成两种常用的方法,以下会分别对照 官方文件 来做示...

赌场线仙 - K棒与移动平均线的华尔滋

最近研究K棒,跟着某知名投顾分析师看盘後解析,「站上五日线买,跌破五日线卖,投信看十日」各种台词朗朗...

什麽是零信任(What is Zero Trust)?

零信任是一种网络安全范式,用於支持可见性的细粒度,动态和以数据为中心的访问控制。 (访问控制基於需要...