2.3 解释进程模型
传统的操作系统必须提供创建进程和终止进程的方法。下面列出了4个引发创建进程的主要事件:
系统初始化;
正在运行的进程执行创建进程的系统调用;
用户要求创建新进程;
启动批处理作业。
操作系统启动后,会创建多个进程。一些是前台进程,与用户(人)交互,并根据用户的要求执行操作。一些是后台进程,执行特定的功能,与用户行为不相关。例如,可以把接收电子邮件设计成后台进程,因为大部分时间都用不到这一功能,只需在有电子邮件到达时处理即可。后台进程通常处理诸如电子邮件、打印等活动。
在Windows中,CreateProcess
(一个Win32函数调用)负责创建进程和加载进程上下文。欲详细了解CreateProcess
,请参阅MSDN(http://msdn.microsoft.com/en-us/library/windows/desktop/ms682425%28v=vs.85%29.aspx)。我们将在下面的示例中演示基本的进程创建和同步。
准备就绪
确定安装并运行了Visual Studio。
操作步骤
现在,我们按下面的步骤创建一个程序,稍后再详细解释。
1. 创建一个新的默认C++控制台应用程序,名为ProcessDemo
。
2. 打开ProcessDemo.cpp
。
3. 添加下面的代码:
#include "stdafx.h"
#include <Windows.h>
#include <iostream>
using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
STARTUPINFO startupInfo = { 0 };
PROCESS_INFORMATION processInformation = { 0 };
BOOL bSuccess = CreateProcess(
TEXT("C:\\Windows\\notepad.exe"), NULL, NULL,
NULL, FALSE, NULL, NULL, NULL, &startupInfo,
&processInformation);
if (bSuccess)
{
cout << "Process started." << endl
<< "Process ID:\t"
<< processInformation.dwProcessId << endl;
}
else
{
cout << "Cannot start process!" << endl
<< "Error code:\t" << GetLastError() << endl;
}
return system("pause");
}```
该程序的输出如图2.4所示:
<div style="text-align: center">
<img src="https://yqfile.alicdn.com/5f5a99c8dfdc5b6ea1df2f5a23fa8317f31bad94.png" >
</div>
图2.4
如图2.4所示,开始了一个新的进程(记事本)。
示例分析
`CreateProcess`函数用于创建一个新进程及其主线程。新进程在主调进程的安全上下文中运行。
操作系统为进程分配进程标识符。进程标识符用于标识进程,在进程终止之前有效。或者,对于一些API(如`OpenProcess`函数),进程标识符用于获得进程的句柄。最初线程的线程标识符由进程分配,该标识符可用于在`OpenThread`中打开一个线程的句柄。线程标识符在线程终止之前有效,可作为系统中线程的唯一标识。这些标识符都返回`PROCESS_INFORMATION`结构中。
主调线程可以使用`WaitForInputIdle`函数,在新进程完成其初始化且正在等待用户输入时等待。这在父进程和子进程的同步中很有用,因为`CreateProcess`不会等到新进程完成初始化才返回。例如,创建的进程会在查找与新进程相关的窗口之前使用`WaitForInputIdle`函数。
终止进程较好的做法是调用`ExitProcess`函数,因为它会给所属进程的所有DLL都发送一条即将终止的通知。而关闭进程的其他方法就不会这样做(如,`TerminateProcessAPI`)。注意,只要进程中有一个线程调用`ExitProcess`函数,该进程的其他线程都会立即终止,根本没机会执行其他代码(包括相关DLL的线程终止代码)。
更多讨论
虽然每个进程都是一个独立的实体,有各自的指令指针和内部状态,但是进程之间也要经常交互。一个进程生成的输出数据可能是另一个进程所需的输入数据。根据两个进程的相对运行速度,可能会发生这种情况:读操作已准备运行,但是却没有输入。在能读到输入数据之前,该进程必定被阻塞。从逻辑上看,如果进程被阻塞就不能继续运行,因为该进程正在等待尚未获得的输入。如果操作系统在这时决定把CPU暂时分配给另一个进程,正在等待的进程就有可能停止。这是两种完全不同的情况。第一种情况是问题本身造成的(即,在用户键入数据之前无法解析用户的命令行);而第二种情况是系统的技术原因造成的(即,进程用完了分配给它的时间,又没有足够的CUP能单独运行该线程)。
图2.5中的状态图演示了一个进程可能处于的3种状态。
<div style="text-align: center">
<img src="https://yqfile.alicdn.com/27e7d713e4cf9c5de3d2b758ac39a9cd48be8ccd.png" >
</div>
图2.5 进程的三种状态
运行:此时,该进程正在使用CPU。
就绪:该进程可运行,但是它暂时停止让其他进程运行。
阻塞:在某些外部事件发生前,该进程不能运行。
运行和就绪状态有些类似。处于这两种状态的进程都可以运行,只是在就绪状态中,进程暂时没有CPU可用。阻塞状态与前两种状态不同,在阻塞状态中,即使CPU空闲,进程也不能运行。
假设有进程A。当调度器选择另一个进程在CPU上执行时,进程A发生转换过程2。当调度器选择执行进程A时,发生转换过程3。为了等待输入,转换过程1把进程A设置为阻塞状态。当进程A获得所需的输入时,发生转换过程4。
时间: 2024-08-02 03:28:53