操作系统中的进程

进程不只是运行的程序(text section/code section),还包括

  • 当前活动如程序计数器(program counter)的值和处理器寄存器的内容
  • 进程堆栈(stack),存储临时数据,如函数参数,返回地址和局部变量
  • 数据段(data section),包括全局变量
  • 堆(heap),进程在运行时动态分配的内存

进程状态

fig1

操作系统内的每个进程用PCB(task control block)表示,包含许多与进程相关的信息

上下文切换

切换CPU到另一个进程,需要保存当前进程状态,并恢复另一个进程的状态,这称为上下文切换。

进程的创建

Linux中进程init为所有用户进程的父进程

通过系统调用fork()可以创建新进程,新进程中(子进程)该函数返回0,原进程中(父进程)该函数返回新进程的pid

若在新进程中调用exec(), 则其不会在执行原程序中的后续代码

进程终止

如果一个进程终止,那么它的所有子进程也会终止,称为级联终止

当进程已经终止,但是其父进程尚未调用wait(), 则称该进程为僵尸进程

如果父进程没有调用wait()就终止了,则其子进程就会变为孤儿进程

孤儿进程会称为init进程的子进程,而init会周期性的调用wait(),来释放孤儿进程的pid和进程表条目

进程间通信

  1. 共享内存

POSIX共享内存的实现为内存映射文件,它将共享内存区域与文件相关联。通过如下方式创建共享内存对象:

1
shm_fd = shm_open(name, O_CREAT | O_RDRW, 0666);

共享内存可能快于消息传递,因为消息传递需要频繁?的系统调用,因此需要消耗更多时间(涉及内核,切换)

  1. 消息队列

对于多核系统,消息传递的性能要优于共享内存,因为共享内存会有缓存一致性问题。

  1. 套接字(socket)

套接字是由一个IP地址和一个端口号组成的,即类似于146.85.5.20:1625

套接字属于分布式进程之间的一种低级通信形式,主要原因是套接字只允许在通信线程之间交换无结构的字节流,可能需要额外自定义传输数据的数据结构

  1. 远程过程调用(remote procedure call)

RPC通信交换的信息具有明确的结构。

客户端需要获取服务端执行RPC的端口,因此服务器端有个类似于DNS的服务程序matchmaker由于提供和RPC名称对应的端口号

  1. 管道

管道是一种特殊类型的文件,可由子进程继承

管道用于同一台机器上的进程通信

  • 匿名管道

    • 匿名管道是单向的
    • 进程通信需要有父子关系
    • 创建采用pipe(int fd[])
    • fd[0]为读出端,fd[1]为写入端
    • 一旦通信完成并终止,匿名管道就不存在了
  • 命名管道

    • 半双工(允许双向,但同一时间只能单向)
    • 不需要父子关系
    • 一旦创建,表现为文件系统的典型文件
    • 命名管道为FIFO,通过调用mkfifo()
    • 通信进程完成后,命名管道依然存在,直到它被显示的从文件系统中删除
    • 只允许字节流的数据
  1. 信号(signal)
  • 信号是由特定事件的发生而产生的
  • 信号被传递给某个进程
  • 信号一旦收到就应处理

信号可分为同步信号和异步信号,同步信号是指信号会发送到产生该信号的进程;异步信号则可发送到另一进程。

每个信号都有一个默认信号处理程序(default signal handler),用户也可定义信号处理程序