进程(process):执行中的程序(=jobs=tasks)
进程可以包括:文本段(text section)/代码段(code)、程序计数器(program counter)、堆栈段(stack: function parameters, local vars, return addresses)、数据段(data section: global vars)、堆(heap: malloc system, allocate memory space from heap)等
程序本身不是进程,程序只是被动实体,而进程是活动实体,它有一个程序计数器用来表示下一个要执行的命令和相关资源的集合。当一个可执行文件被装入内存时,一个程序才能成为进程。
memory lay out (linear space):
stack: grow toward smaller address
The space between stack and heap is called hole (unused memory, very very very huge)
Process State
![](https://i-blog.csdnimg.cn/blog_migrate/7f9f3a311eeafaf5d4398cfc13b1c2d8.png)
一个进程在某一时间只能由一个状态(state),当进程运行时,状态会发生改变。
进程状态有以下五种情况:
- new: The process is being created (a very short period of time, will be put into ready soon)
- running: Instructions are being executed (may have a system call and go into waiting state to wait for it to complete)
- waiting: The process is waiting/blocked for some event to occur (waiting for I/O to complete, etc), not ready yet for running
- ready: The process is waiting to be assigned to a processor (ready to run but not running, waiting for CPU, a special case of waiting)
- terminated: The process has finished execution
When a process is ready, it will be put into ready queue, and when the scheduler decide that it can run, it will be put into running state
If a process is running but use too much CPU (or spend too long time --> judged by a timer), it may be put into ready state and wait, and the scheduler decide who will be the next to run
No edge from waiting to running, can’t switch state from waiting to running
进程控制块(Process Control Block, PCB)
每个进程在操作系统内用PCB来表示。
- 进程状态(process state):包括new running waiting ready terminated等,当进程状态改变时,将状态存入PCB
- 程序计数器(program counter):计数器表示进程要执行的下个指令地址
- CPU寄存器(contents of CPU registers):包括累加器、索引寄存器、堆栈指针、通用寄存器和其他条件码信息寄存器。这些状态信息在出现中断时也需要保存,以便进程以后能够正确地继续执行。
- CPU调度信息(CPU scheduling information):包括进程优先级、调度队列的指针和其他调度参数。
- 内存管理信息(memory-management information):包括基址和界限寄存器的值、页表或段表
- 记账信息(accounting information):包括CPU时间、实际使用时间、时间界限、几张数据、作业或进程数量等
- I/O状态信息(I/O status information):包括分配给进程的I/O设备列表、打开的文件列表等
CPU在进程间的切换:(multiprogramming)
进程调度(Process Scheduling)
- job queue – set of all processes in the system (global)
- ready queue – set of all processes residing in main memory, ready and waiting to execute (a special device queue)
- device queue – set of processes waiting for an I/O device
队列图(queueing-diagram)
调度程序(schedulers)
-
Long-term scheduler (job scheduler):从缓冲池中选择要装入内存(ready queue)并准备执行的进程(现在很少用)
very infrequently (seconds, minutes)
控制多道程序设计的程度。- I/O-bound process:执行I/O的方面比执行计算要花费更多时间(例如word)
- CPU-bound process:很少产生I/O请求,执行计算比执行I/O花费更多时间(例如video decoding)
-
Short-term scheduler (CPU scheduler):从准备执行的进程中选择进程并为之分配CPU(ready queue -> CPU)
very frequently (milliseconds,通常每100ms至少执行一次) -
Medium-term scheduler (swapper):能适时将进程从内存(or CPU)中移出,并能在后续再次被调入并且从中断处继续执行
进程操作
进程创建
进程在其执行过程中,能通过创建进程系统调用创建多个新进程。创建进程称为父(parent)进程,新进程成为子(child)进程。
大多数操作系统(UNIX, Windows, etc)根据唯一的进程标识符(process identifier, pid)来识别进程。pid通常为一整数值。
使用ps(ps -el)可以得到进程列表。
进程树示例如下:(Solaris系统)
子进程可能从操作系统直接获得资源,也可能只从父进程获得资源。
父进程可能必须在其子进程之间分配资源或共享资源(如内存或文件)。
限制子进程只能使用父进程的资源能防止创建过多的进程带来的系统超载。
进程创建新进程时的两种执行可能:
1.父进程与子进程并发执行
2. 父进程等待,直到某个或全部子进程执行完
新进程地址空间的两种可能:
1.子进程是父进程的复制品,具有与父进程相同的程序和数据
2.子进程装入另一个新程序
在UNIX系统中,通过fork()系统调用可创建新进程
eg.
#include <sys/types.h>
#include <stdio.h>
#include <unistd.h>
int main()
{
pid_t pid;
/* fork another process */
pid = fork();
if (pid < 0) { /* error occurred */
fprintf(stderr, "Fork Failed");
exit(-1);
}
else if (pid == 0) { /* child process */
execlp("/bin/ls", "ls", NULL);
}
else { /* parent process */
/* parent will wait for the child to complete */
wait (NULL);
printf ("Child Complete");
exit(0);
}
}
进程终止
父进程终止子进程的原因有:
1.子进程使用了超过它所分配的资源
2.分配给子进程的任务不再需要
3.父进程正在推出,而操作系统不允许无父进程的子进程继续执行
有些系统不允许子进程在父进程已终止的情况下存在,即若一个进程终止(不管正常还是不正常),其所有子进程都将终止。这种现象称为级联终止(cascading termination)。
系统可以通过调用exit()
来终止进程,父进程可以通过系统调用wait()
以等待子进程终止,返回终止子进程的进程标识符,获得子进程的退出状态。
进程终止时系统会释放其资源,但进程表中的条目将存在直到父进程调用wait()
。进程以已终止但是父进程未调用wait()
的进程称为僵尸进程(zombie process)。所有进程在终止时都会过渡到这个状态,但一般只短暂存在。一旦父进程调用wait()
,僵尸进程的进程标识符和它在进程表中的条目就会被释放。
父进程执行完后还未执行完的子进程被称作孤儿进程(orphaned process),它们将以init进程作为父进程。
进程间通信
独立的进程(independent)不能影响其他进程或受到其他进程影响,协作的进程(cooperating)将与其他进程共享数据。
协作的好处:
1.信息共享
2.计算加速
3.模块化
3.方便
IPC:进程间通信机制,允许进程相互交换数据与信息。
两种基本模型:消息传递(a)、共享内存(b)
进程通信通过send(message)
和receive(message)
进行。
直接通信:
send(P, message):向进程P发送message
receive(Q, message):从进程Q接收message
间接通信(通过邮箱或端口来发送和接收信息):
send(A, message):向邮箱A发送message
receive(A, message):从邮箱A接受message
生产者-消费者问题(producer-consumer problem):
解决方法:共享内存,在生产者进程和消费者进程的共享内存区域中应有一个可用的缓冲区。
缓冲区的类型分为无界缓冲区(unbounded-buffer,不限制缓冲区大小,消费者可能等待新的项,而生产者总可以产生新项)和有界缓冲区(bounded-buffer,缓冲区大小固定,如果空,则消费者需等待,如果满,则生产者需等待)
共享缓冲通过循环数组和两个逻辑指针(in和out)来实现,in指向缓冲中的下一个空位,out指向缓冲中的第一个满位。当 in==out 时,缓冲为空;当 (in+1) % BUFFER_SIZE == out 时,缓冲为满。
//生产者进程
item nextProduced
while (true){
// produce an item in nextProduced
while( ((in + 1) % BUFFER_SIZE) == out); //do nothing
buffer[in] = nextProduced;
in = (in+1) % BUFFER_SIZE;
}
item nextConsumed
while(true){
while (in == out); //do nothing
nextConsumed = buffer[out];
out = (out+1) % BUFFER_SIZE:
//consume the item in the nextConsumed
}
客户机/服务器通信
套接字(socket)是通信的端点,通过网络通信的每对进程需要一对套接字,每个套接字由一个IP地址和一个端口号组成。通常,套接字采用客户机-服务器架构。实现特定服务的服务器监听众所周知的端口(telnet服务器监听23,ftp监听21,web或http监听80),所有低于1024的端口都是众所周知的。
当客户进程发出连接请求时,它的主机为它分配一个端口(大于1024)
所有连接都必须是唯一的。
RPC:远程过程调用,不同于IPC,RPC通信交换的信息具有明确结构,因此不再仅仅是数据包。
端口(port):处于消息分组头部,每个网络地址有许多端口号,用于区分所支持的多个网络服务。
RPC允许客户调用位于远程主机的过程。通过客户端提供的存根(stub),RPC系统隐藏通信细节。对于每个单独远程过程都有一个存根,当客户调用远程过程时,PRC系统调用适当存根,并且传递远程过程参数
RMI:远程方法调用,类似RPC的Java特性,与RPC的区别在于:
1.RPC支持子程序编程,即只能调用远程的子程序或函数。而RMI基于对象,支持调用远程对象的方法
2.RPC中,远程过程的参数是普通数据结构,而RMI可以将对象作为参数传递给远程方法。