目录
include/linux/sched.h(2.6.15)
进程控制块(PCB、Process Control Block)
3.3 进程操作(Operating on Processes)
3.4 进程通信(Interprocess Communication)
Examples of IPC Systems – Windows
3.1 进程概念
进程是正在执行中的程序(活动实体,即是正在运行的程序)
计算机、作业、任务的基本工作单位
包含一些资源的指令容器:-例如,CPU时间(CPU执行指令)、内存、文件、I/O设备来完成其任务
示例:编译进程、字处理进程、调度程序(sched、swapper)进程或守护进程: ftpd、httpd
jobs作业=user programs用户程序= tasks任务= process 进程
进程是什么?
一个具有一定独立功能的程序在一个数据集合上的一次动态执行
过程。
正在执行中的程序 a program in execution
内存中的进程
一个进程包括:
程序代码,也叫代码段(text section)
程序计数器(PC,program counter)
寄存器(registers)
数据段(全局数据)(global data)
栈(临时数据)(stack,temporary data)
堆(动态分配的内存)(heap,dynamically allocated memory)
一个C程序的内存布局
进程状态
当一个进程被执行时,它会改变状态
新(New):正在创建该进程。
运行、执行(Running):正在执行指令。
就绪(Ready):该进程正在等待是否被分配给一个处理器(CPU)。
等待、阻塞(Waiting,blocked):该进程正在等待某个事件的发生
终止(Terminated):该进程已完成执行
状态转换
进程可能因此改变状态:
程序操作(系统调用)
操作系统操作(调度决策)
外部动作(中断)
进程状态的转换
三个基本状态之间可能转换和转换原因如下:
就绪→运行:当处理机空闲时,进程调度程序必将处理机分配给一个处于就绪状态的进程 ,该进程便由就绪状态转换为运行状态。
运行→等待:处于运行状态的进程在运行过程中需要等待某一事件发生后(例如因I/O请求等待I/O完成后),才能继续运行,则该进程放弃处理机,从运行状态转换为等待状态。
等待→就绪:处于等待状态的进程,若其等待的事件已经发生,于是进程由等待状态转换为就绪状态。
运行→就绪:处于运行状态的进程在其运行过程中,因分给它的处理机时间片已用完,而不得不让出(被抢占)处理机,于是进程由运行态转换为就绪态。
等待→运行,就绪→等待这二种状态转换一般不可能发生
进程状态与处理机
处于运行状态进程:如系统有一个处理机,则在任何一时刻,最多只有一个进程处于运行状态。
处于就绪状态进程:一般处于就绪状态的进程按照一定的算法(如先来的进程排在前面,或采用优先权高的进程排在前面)排成一个就绪队列。
处于等待状态进程:处于等待状态的进程排在等待队列中。由于等待事件原因不同,等待队列也可以按事件分成几个队列。
Windows NT/2000 线程有7种状态;Linux进程有6种状态;Windows 2003 server线程有9种状态.
include/linux/sched.h(2.6.15)
124 #define TASK_RUNNING 0
125 #define TASK_INTERRUPTIBLE 1
126 #define TASK_UNINTERRUPTIBLE 2
127 #define TASK_STOPPED 4
128 #define TASK_TRACED 8
129 /* in tsk->exit_state */
130 #define EXIT_ZOMBIE 16
131 #define EXIT_DEAD 32
132 /* in tsk->state again */
133 #define TASK_NONINTERACTIVE 64
include/linux/sched.h(4.2)
#define TASK_RUNNING 0
#define TASK_INTERRUPTIBLE 1
#define TASK_UNINTERRUPTIBLE 2
#define __TASK_STOPPED 4
#define __TASK_TRACED 8
/* in tsk->exit_state */
#define EXIT_DEAD 16
#define EXIT_ZOMBIE 32
#define EXIT_TRACE
(EXIT_ZOMBIE | EXIT_DEAD)
/* in tsk->state again */
#define TASK_DEAD 64
#define TASK_WAKEKILL 128
#define TASK_WAKING 256
#define TASK_PARKED 512
#define TASK_NOLOAD 1024
#define TASK_STATE_MAX 2048
#define TASK_STATE_TO_CHAR_STR "RSDTtXZxKWPN"
extern char ___assert_task_state[1 - 2*!!(
sizeof(TASK_STATE_TO_CHAR_STR)-1 != ilog2(TASK_STATE_MAX)+1)];
/* Convenience macros for the sake of set_task_state */
#define TASK_KILLABLE (TASK_WAKEKILL | TASK_UNINTERRUPTIBLE)
#define TASK_STOPPED (TASK_WAKEKILL | __TASK_STOPPED)
#define TASK_TRACED (TASK_WAKEKILL | __TASK_TRACED)
进程与程序
进程与程序的区别
进程是动态的,程序是静态的:程序是有序代码的集合;进程是程序的执行。
进程是暂时的,程序是永久的:进程是一个状态变化的过程,程序可长久保存。
进程与程序的组成不同:进程的组成包括程序、数据和进程控制块(即进程状态信息)。
进程与程序的对应关系:通过多次执行,一个程序可对应多个进程;通过调用关系,一个进程可包括多个程序
实例
例:一个只有一个处理机的系统中,OS的进程有运行、就绪、等待三个基本状态。假如某时刻该系统中有10个进程并发执行,在略去调度程序所占用时间情况下(在user mode下),请问:
这时刻系统中处于运行状态的进程数最多有几个?最少有几个? 1 0
这时刻系统中处于就绪状态的进程数最多有几个?最少有几个? 9 0
这时刻系统中处于等待状态的进程数最多有几个?最少有几个?10 0
进程由什么构成
用户资源/操作系统资源:
程序代码(文本)
数据
全局变量
堆(动态分配的内存)
进程栈
函数参数
返回地址
局部变量和函数
操作系统资源,环境
打开文件,套接字
凭据安全
寄存器
程序计数器,栈指针
进程控制块(PCB、Process Control Block)
跟踪一个进程需要什么?
每个进程在操作系统内用进程控制块来表示,它包含与特定进程相关的许多信息:
进程状态
程序计数器
CPU寄存器
CPU调度信息
内存管理信息
记账信息
文件管理
I/O状态信息
Linux PCB
在Linux中,进程的信息保存在一个称为struc task_strust的结构中
#include/linux/sched.h
struct task_struct
pid_t pid; /* process identifier */
long state; /* state for the process */
unsigned int time_slice /* scheduling information */
struct mm_struct *mm /* address space of this process */
……
Open Solaris
Windows 执行进程块
Windows内核进程(kprodes)块
Windows进程环境块(PEB)
3.2 进程调度(Process Scheduling)
3.2.1 调度队列
作业队列-包含系统中所有进程的作业队列集
就绪队列-驻留在主存中、准备好并等待执行的所有进程的队列集。
设备队列-等待I/O设备的进程集。
在不同队列之间的进程迁移
已准备就绪的队列和各种I/O设备队列
进程调度的表示
3.2.2 调度
长程调度(或作业调度)
选择应将哪些进程带入已就绪队列
控制多道程序的道数
大多数现代操作系统都没有长程的调度(例如Windows、UNIX、Linux)
短程调度(或CPU调度)
选择下一个应该执行的进程,并分配CPU。
非常频繁地调用(毫秒)--->(必须很快)
中程调度
增加中程调度
调度(续)
进程可以描述为:
I/O型进程
花在I/O上的时间比计算要多,有很多短的CPU突发。
CPU型进程
花更多的时间做计算;很少有很长的CPU突发。
3.2.3 上下文切换(Context Switch)
当CPU切换到另一个进程时,系统必须保存旧进程的状态,并通过上下文切换为新进程加载已保存的状态
在PCB中表示的过程的上下文
上下文切换时间是一种开销;系统在切换时没有做任何有用的工作
时间取决于硬件支持
CPU从进程切换到进程
移动系统中的多任务处理
一些移动系统(例如,iOS的早期版本)只允许有一个进程运行,而另一些进程则暂停运行
Due to screen real estate, user interface limits iOS provides for a
单个前台进程(Single foreground process)-通过用户接口进行控制
多个后台进程(Multiple background process)-在内存中,正在运行,但不在显示器上,并有限制
限制包括单个、短的任务,接收事件通知,特定的长时间运行的任务,如音频播放
安卓系统运行前台和后台,限制更少
后台进程使用一个服务来执行任务
即使后台进程被挂起,服务也可以继续运行
服务没有用户接口,内存使用量小
3.3 进程操作(Operating on Processes)
3.3.1 进程创建(Process Creation)
父进程创建子进程,子进程又创建其他进程,形成进程树。
通常,通过进程标识符(pid,process identifier)来标识和管理流程
资源共享:
父进程和子进程共享所有资源
子进程共享父进程资源的子集。
父进程和子进程不共享任何资源
进程操作(续)
执行
父进程和子进程并发执行
父进程等待直到子进程终止
地址空间
子进程是父进程的复制品
子进程有一个程序被调入
进程创建(续)
UNIX例子
fork系统调用将创建新的进程
int pid1 = fork();
从系统调用 fork 中返回时,两个进程除了返回值 pid 1不同外,具
有完全一样的用户级上下文。在子进程中,pid1 的值为0;父进程中,pid 1的值为子进程的进程号。
在fork后使用的exec系统调用,用新程序替换进程的内存空间。
典型solaris上的进程树
C程序fork一个单独的进程
#include <stdio.h> void main(int argc, char *argv[ ]) { int pid1; pid1=fork(); /* fork another process */ if (pid1<0){ fprintf(stderr, “Fork Failed”); exit(-1); } else if (pid1==0) { execlp(“/bin/ls”,”ls”,NULL); } /* child process */ else { wait(NULL); printf(“child Complete”); exit(0); } /*parent process */ }
fork算法演示
WINDOWS example: CreateProcess.cpp
算法演示(27分钟)——http://classroom.zju.edu.cn/livingroom course_id=46044&sub_id=785061&tenant_code=112
3.3.2 进程终止
引起进程终止的事件
正常结束
异常结束
外界干预
进程执行最后一条语句,并要求操作系统进行决定(退出)
将数据从子进程输出到父进程(通过等待)
进程的资源被操作系统进行释放。
如果父进程正在退出,则其子进程将发生什么变化?
某些操作系统不允许子进程继续运行
子进程可能通过级联终止被终止
子进程可能由不同的父进程继承
父进程可能通过使用wait()系统调用来等待子进程的终止。调用返回状态信息和终止进程的状态信息和pid
pid = wait(&status);
If no parent waiting (did not invoke wait()) process is a zombie(僵尸进程:子进程运行终止,父进程尚未调用wait())
If parent terminated without invoking wait(), process is an orphan(孤立进程:父进程没有调用wait()就终止了)
安卓进程重要性层次结构
移动操作系统通常不得不终止进程来回收系统资源,如内存。从大多数到最不重要的部分:
前台进程
可见进程
服务进程
后台进程
空进程
安卓系统将开始终止那些最不重要的进程。
3.4 进程通信(Interprocess Communication)
独立的进程不能影响或受到另一个进程的执行的影响。
合作进程可能会影响或受到另一个过程的执行的影响
合作进程的优点:
信息共享
计算加速
模块化
方便
合作进程需要进程间通信(IPC)
IPC提供了一种机制,使进程能够通信和同步它们的操作,而不共享相同的地址空间
通信模型
models of IPC
shared memory(共享内存)
message passing(消息传递)
a) Shared memory. (b) Message passing.
IPC
直接通信
间接通信
常用通信机制:
信号(signal)
共享存储区(shared memory)
管道(pipe)
消息(message)
套接字(socket)
Linux进程通信机制
Linux实现进程间通信(IPC Inter Process Communication):
System V IPC机制:
信号量、 消息队列、 共享内存
管道(pipe)、命名管道
套接字(socket)
信号( signal )
文件锁(file lock)
POSIX线程:
互斥锁(互斥体、互斥量)(mutex)、条件变量(condition variables)
POSIX:
消息队列、信号量、共享内存
UNIC IPC工具分类
Windows 2000/XP进程线程通信机制
基于文件映射的共享存储区
无名管道和命名管道
server32pipe.c、client32pipe.c
启动多个client进程进行通信
邮件槽
套接字
剪帖板(Clipboard)
信号
其他同步机制
3.5 共享存储系统中的IPC
在希望进行通信的进程之间共享的一个内存区域
该通信是由用户进程控制的,而不是由操作系统控制的。
主要问题是提供一种机制,允许用户进程在访问共享内存时同步他们的操作。
同步化将在第6章和第7章中进行非常详细的讨论。
有限缓存区——共享内存
合作进程的并发执行需要允许进程彼此通信并同步它们的操作的机制(第6章)
合作过程的常见范例——生产者-消费者问题(Producer-Consumer Problem)
生产者进程产生由消费者进程消耗的信息。
无限缓冲区(unbounded-buffer)对缓冲区的大小没有任何实际的限制。
有限缓冲区(bounded-buffer)假设有一个固定的缓冲区大小。
有界缓冲区-共享内存解决方案
缓冲区可以由操作系统通过使用进程间通信(IPC,interprocess-communication)工具来提供,也可以由应用程序程序员使用共享内存进行显式编码
针对边界缓冲区问题的共享内存解决方案:共享数据
#define BUFFER_SIZE 10 Typedef struct { . . . } item; item buffer[BUFFER_SIZE]; int in = 0; int out = 0;
解决方案是正确的,但只能使用BUFFER_SIZE-1元素
有界缓冲区-生产进程
生产者:
item nextProduced; while (1) { produce an item in nextProduced ; while (((in + 1) % BUFFER_SIZE) == out) ; /* do nothing */ buffer[in] = nextProduced; in = (in + 1) % BUFFER_SIZE; }
解决方案是正确的,但只能使用BUFFER_SIZE-1元素
有界缓冲区-消费进程
消费者:
item nextConsumed; while (1) { while (in == out) ; /* do nothing */ nextConsumed = buffer[out]; out = (out + 1) % BUFFER_SIZE; consume the item in nextConsumed ; }
在第6章中,我们将讨论了如何实现合作进程之间的同步
3.6 消息传递中的IPC
进程间通信——消息传递
进程之间会相互通信,而不诉诸于共享的变量
IPC设施提供两种操作:
send(message)
receive(message)
消息的大小是固定的或可变的
如果进程P和Q希望进行沟通,他们需要:
建立他们之间的沟通联系
通过收发方式交换信息
实施问题:
如何建立链接?
一个链接是否可以与两个以上的进程相关联?
每对通信进程之间可以有多少个链接?
一个链接的容量是多少?
链接可以容纳的消息的大小是固定的还是可变的?
链接是单向的还是双向的?
通信环节的实现
物理上:
共享内存
硬件总线
网络
逻辑上:
直接或间接
同步或异步
自动或显式缓冲
直接通信(Direct Communication)
进程必须显式地为彼此命名:
send(P,massage)——向进程P发送一条消息
receive(Q,massage)——从进程Q接收到一条消息
一个链接可能与许多进程相关联。
每对进程可以共享多个通信链路
链接可以是单向的或双向的。
操作
创建新邮箱
通过邮箱发送和接收邮件
销毁邮箱
间接通信(Indirect Communication)
从邮箱(也称为端口)定向和接收邮件
每个邮箱都有一个唯一的ID
进程只有在它们共享一个邮箱时才能进行通信
只有在进程共享公共邮箱时才建立的链接属性
一个链接可能与许多进程相关联
每对进程可以共享多个通信链路
链接可以是单向的或双向的。
通信链接
操作
创建新邮箱
通过邮箱发送和接收邮件
销毁邮箱
原语定义为:
send(A,message)——将邮件发送到邮箱A
send(A,message)——从邮箱a接收邮件
邮箱共享
P1、P2和P3共享邮箱A。
P1,发送;P2和P3接收
谁会得到信息?
解决办法
允许一个链接最多与两个进程相关联
每次只允许一个进程来执行一个接收操作
允许系统任意选择接收机。发件人会被通知接收人是谁。
同步
消息传递可以是阻塞的或非阻塞的
阻塞被认为是同步的
阻止发送——发送方被阻止,直到收到消息
阻止接收——接收方将被阻止,直到有消息可用为止
非阻塞被认为是异步的
非阻塞发送——发件人发送消息并继续
非阻塞接收,接收人接收:
有效消息,或
空消息
不同的组合可能
如果发送和接收都是阻塞的,我们就有一个集合
生产者——消息传递
message next_produced; while (true) { /* produce an item in next_produced */ send(next_produced); }
消费者——消息传递
message next_consumed; while (true) { receive(next_consumed) /* consume the item in next_consumed */ }
缓存
附加到该链接的消息的队列。
以三种方式之一实现
1.零容量-链接上没有消息排队。发件人必须等待接收方(会合)
2.限制容量-有限长度的n条消息发送者必须等待,如果链接满
3.无限长的容量-无限长的发送者从不等待
3.7 IPC系统的例子
POSIX共享内存
进程首先创建共享内存段
shm_fd = shm_open(name, O CREAT | O RDWR, 0666);
也用于打开一个现有的线段
设置对象的大小
ftruncate(shm_fd, 4096);
使用mmap()来内存映射文件指针到共享内存对象
读写共享内存是通过使用mmap()返回的指针来完成的
IPC POSIX生产者
IPC POSIX消费者
Examples of IPC Systems – Windows
通过高级的本地过程调用(LPC)设施,以消息传递为中心
仅在同一系统上的进程之间工作
使用端口(如邮箱)来建立和维护通信通道
通信工作如下:
客户端将打开一个指向子系统的连接端口对象的句柄
客户端发送一个连接请求
服务器创建两个私有通信端口,并将其中一个句柄返回给客户端。
客户端和服务器使用相应的端口句柄来发送消息或回调,并侦听应答
Windows中的本地程序调用
管道(Pipes)
作为一个管道,允许两个进程进行通信
问题:
通信是单向的还是双向的?
在双向通信中,它是半双工还是全双工?
通信过程之间是否存在一种关系(即父子关系)?
这些管道可以通过一个网络来使用吗?
普通管道-无法从创建它的进程外部访问。通常,父进程创建一个管道,并使用它与它创建的子进程进行通信
命名管道-可以访问没有父子关系
普通管道(Ordinary Pipes)
普通管道允许以标准的生产者-消费者风格进行沟通
生产者写入到一端(管道的写入端)
消费者从另一端(管道的读取端)进行读取
因此,普通的管道是单向的
需要通信进程之间的父子关系
Windows调用这些匿名管道
命名管道
命名管道比普通管道更强大
通信是双向的
通信进程之间不需要任何父子关系
多个进程可以使用命名管道进行通信
在UNIX和Windows系统上都有提供
3.8 客户端-服务器系统中的通信
套接字(Sockets)
套接字被定义为通信的端点
IP地址和端口的连接——在消息包开始时包含的数字,以区分主机上的网络服务
套接字161.25.19.8:1625是指主机161.25.19.8上的端口1625
通信由一对套接字之间组成
所有低于1024的端口都是众所周知的,用于标准服务
特殊IP地址127.0.0.1(环回),指进程正在运行的系统
通信使用套接字
远过程调用(Remote Produre Calls)
远程过程调用(RPC)抽象了网络系统上的进程之间的过程调用。
再次使用端口来进行服务区分
存根(Stubs)-在服务器上的实际过程的客户端代理
客户端存根可以定位服务器并整理参数
服务器端存根接收此消息,解包已封送的参数,并在服务器上执行此过程
在Windows上,存根代码从用微软接口定义语言(MIDL)编写的规范中编译
通过外部数据表示(XDL)格式处理的数据表示,以考虑不同的架构
大的和小的
远程通信比本地通信有更多的故障场景
消息可以只传递一次,而不是最多传递一次
操作系统通常提供一个连接 (或介绍人)服务来连接客户端和服务器