Process

Process

进程独有的:

  1. PC
  2. stack pointer(存储返回地址)
  3. address space
  4. file descriptors

想要去 manipulate一个 process,我们必须交给内核态去做这件事,也就是在用户程序中调用 syscall。对于每个syscall,我们都必须检查它的 return value,如果为-1,则说明出错了。

内核空间

  1. 内核空间位于整个地址空间的顶部,是所有用户进程共有的。用户进程的地址空间处于地址空间的底部。
  2. 只要系统在运行,内核就始终处于活动状态。它不像其他进程那样启动和停止;而是在系统启动时初始化,并持续运行直到系统关闭。

进程的终止

终止一个进程有三种方法:

  1. 收到了default action为terminate的signal
  2. 从main方法返回
  3. 显式call exit

fork()

Alt text 在fork()创建子进程时,子进程会继承父进程的一切,包括PC,stack pointer, fd; 子进程拥有和父进程一模一样,但是分开的虚拟地址空间,并且这两个虚拟地址空间指向同一块物理内存。因为父进程把 page table复制给子进程。

写时复制:父子进程拥有的同一块物理内存,在 fork()初期被设为 read-only. 当父进程或子进程尝试修改内存,会发生 page fault, 也叫写保护中断。 此时,OS 会给该进程复制一份新的内存页,然后修改应用于新的内存页上。

僵尸进程

子进程在执行完成时,会把它的return value放在某个地方,然后通知 OS 它完成工作了。此时父进程可以通过 wait()/waitpid()来获得子进程的 return value。

如果子进程已经 terminate,但是父进程没有通过wait()/waitpid()来获得退出状态,那么子进程仍然会消耗系统资源。比如OS需要存储它的 return value,也需要在process table 中留着它的 pid.

孤儿进程

如果父进程 terminate 了,它的子进程还没有 terminate,那么所有未完成的子进程被OS分配给init进程(pid=1)。init进程会负责在它们 terminate 之后,回收它们的资源。

文件描述符

在内存中,每个进程都有一个PCB,PCB中存在每个进程独有的file descriptor table,文件描述符就是这个table中的索引

table 中存放指向资源的指针,比如指向heap中的一个 FILE 结构,这个 FILE 结构又指向磁盘中的".txt"文件。

进程间通信方式

管道

  1. 匿名管道:允许亲缘进程通信。
1
cat xx.txt | grep -n 'xxx'

shell先用fork()创建一个进程,并把它替换为cat进程;再对 grep做同样的事。

shell接着创建一个管道(内核中的一串缓存),允许cat的输出作为 grep的输入。

  1. 命名管道
1
mkfifo myfifo

相当于在磁盘中创建了一个名为myfifo的文件,进程之间通过文件描述符实现读写,不过只允许单向传输

消息队列

优点:可以异步通信
缺点:占用内存多

系统调用:用 msgget 函数创建消息队列,再用 msgsnd 发消息,msgrcv 收消息。

共享内存

shmget创建共享内存。让不同进程的虚拟地址空间映射到同一块物理内存,这样进程间就能直接读写这块区域来交换数据,效率很高。

优点:读写效率高
缺点:需要实现互斥

信号量

实现进程的同步/互斥。

同步:信号量初始化为0,代表资源数
互斥:信号量初始化为1

信号

有同步信号和异步信号,可以为信号定义一个捕捉函数,或者可以忽略
SIGKILL无法被捕获或忽略

异步信号

  • 编号2-SIGINT-键盘中断(异步信号(中断)),由外部事件触发,与当前指令执行无关
  • 编号9-SIGKILL-强制终止

同步信号

  • SIGTRAP(调试断点),traps(有意触发的异常)
  • SIGSEGV(段错误),faults,由无效内存访问或非法操作码引起,unintentional but recoverable