实验2:Windows应用程序基础和进程控制

实验2:Windows应用程序基础和进程控制
实验2:Windows应用程序基础和进程控制

实验二:Windows进程控制

1. 实验目的

每个进程都有一个独立的受到保护的地址空间,其他进程不能访问。一个进程可以包含一个或更多的线程。进程能够在其内部创建新的、独立的线程,并且管理对象间的通信和同步。

通过对Windows系统编程,进一步熟悉操作系统的基本概念,较好地理解Windows操作系统的系统结构和编程特点。

2. 进程控制

Windows所创建的每个进程都从调用CreateProcess() API函数开始,该函数的任务是在对象管理器子系统内初始化进程对象。每一进程都以调用ExitProcess()或TerminateProcess() API函数终止。通常应用程序的框架负责调用ExitProcess()函数。对C++运行库来说,这一调用发生在应用程序的main()函数返回之后,如果采用C运行库,则调用WinMain()函数。

通过创建进程、观察正在运行的进程和终止进程的程序设计和调试操作,进一步熟悉操作系统的进程概念,理解Windows进程的生命周期。

2.1 进程控制相关的API

基本的Win32进程管理函数是CreateProcess,它可以创建拥有单个线程的进程。因为进程需要代码,所以有必要指定可执行程序文件名作为CreateProcess调用的一部分。

CreateProcess有10个参数支持其灵活性和强大功能。该函数并不返回一个HANDLE,而是在一个结构(在调用中指定)中返回表示进程和线程的两个句柄。

2.1.1 创建进程CreateProcess()函数

函数格式:

BOOL CreateProcess(LPCTSTR lpApplicationName, LPTSTR lpCommandLine,

LPSECURITY_ATTRIBUTES lpProcessAttributes,

LPSECURITY_ATTRIBUTES lpThreadAttributes,

BOOL bInheritHandles, DWORD dwCreationFlags,

LPVOID lpEnvironment, LPCTSTR lpCurrentDirectory,

LPSTRATUPINFO lpStartupInfo,

LPPROCESS_INFORMATION lpProcessInformation);

参数:

(1)lpszApplicationName和lpCommandLine指定了新进程将使用的可执行文件和传递给新进程的命令行字符串。

lpszCommandLine可以设定CreateProcess中用于创建新进程的命令行。CreateProcess在解析lpszCommandLine字符串的时候,它先查看字符串中的第一个符号。如果它是一个可执行文件名且不含有扩展名,就假定它的扩展名为EXE。CreateProcess将按照以下顺序来搜索可执行文件:

1)含有调用进程的EXE文件的目录;

2)调用进程的当前目录;

3)Windows系统目录,该目录由GetSystemDirectory函数得到;

4)Windows目录,该目录由GetWindowsDirectory函数得到;

5)列在PATH环境变量中的目录。

当然,如果文件名中包含完整的路径,系统就使用完整路径搜索可执行文件。如果系统找到了可执行文件,就创建一个新进程,并为它生成一个4GB的地址空间,从而使可执行文件的代码和数据映射到这个地址空间。

(2)lpProcessAttribute和lpThreadAttribute是指向进程和线程安全属性结构的指针。当用NULL表示时,为默认的安全性。

(3)FInheritHandles表明新进程是否继承调用进程的打开句柄的副本。继承的句柄与原来的句柄具有相同属性。

(4)FdwCreate是几个标志的组合。其中包含以下几个标志:

1)CREATE_SUSPENDED : 新进程的主线程创建时处于挂起状态,直到调用ResumeThread 函数时才能运行。

2)DETACHED_PROCESS和CREATE_NEW_CONSOLE相互排斥,二者不能同时使用。第一个标志是创建没有控制台的进程,第二个标志是创建新的有控制台的进程。如果二者都没有设置,进程将继承父进程的控制台。

3)CREATE_NEW_PROCESS_GROUP指定新进程是新进程组的根进程。如果组中所有的进程都共享同一控制台,则它们都将接收控制台的控制信号。

(5)lpvEnvironment指向新进程的环境块。如果此值为NULL,进程会使用父进程的环境块。环境块包含名称和值字符串,如搜索路径。

(6)lpszCurDir指向新进程的驱动器和目录。若为NULL,将使用父进程的工作目录。(7)lpsiStartInfo指向新进程的主窗口外观和标准设备句柄。使用GetStartupInfo函数得到父进程信息。

(8)lpProcessInformation指向包含返回的进程和线程句柄、进程和线程标识符的PROCESS_INFORMATION结构的指针。

返回值:如果进程和主线程创建成功,则返回TRUE。

该函数可使系统创建一个进程内核对象和一个线程内核对象。且打开进程和线程对象,并将与进程相关的每个对象句柄放入PROCESS_INFORMATION的结构中。

PROCESS_INFORMATION结构定义如下:

Typedef struct _PROCESS_INFORMATION

{

HANDLE hProcess;//新创建进程的句柄

HANDLE hThread;//新创建进程的主线程的句柄

DWORD dwProcessId;//新创建进程的标识

DWORD dwThreadId;//新创建进程的主线程的标识

}PROCESS_INFORMATION, *LPPROCESS_INFORMATION;

为了保护被创建的对象,系统定义了对象的安全属性结构,其定义如下:

Typedef struct _SECURITY_AFFRIBUTES

{

LPVOID lpSecurityDesriptor;

BOOL hInheritHandle;

}

其中,nLength代表这个结构的以字节为单位的大小,lpSecurityDescriptor是控制共享该对象的安全描述符的指针。如果该值为0,该对象被赋予默认的安全描述符。bInheritHandle是一个布尔值,指示返回的对象句柄是否可被新创建进程继承,TRUE表示可以继承。

2.1.2 获得当前进程的标识符GetCurrentProcessId()函数

函数格式:

DWORD GetCurrentProcessId(void);

参数:无。

返回值:返回当前进程的标识符。

2.1.3 挂起当前的执行线程Sleep()函数

函数格式:

VOID Sleep(DWORD dwMilliseconds);

参数:dwMilliseconds为指定的挂起执行线程的时间,以毫秒为单位。取值为0时,该线程将余下的时间片交给处于就绪状态的同一优先级的其他线程。若没有处于就绪状态的同一状态的其他线程,则函数立即返回,该线程继续执行。若取值为INFINITE则造成无限延迟。返回值:该函数没有返回值。

2.1.4 关闭对象CloseHandle()函数

函数格式:

BOOL CloseHandle(HANDLE hObject);

参数:hObject代表一个已打开对象的句柄。

返回值:执行成功返回TRUE,否则返回FALSE,并可以调用GetLastError()获知失败原因。

关闭一个内核对象,其中包括文件、文件映射、进程、线程、安全和各种同步对象等。在创建或打开对象成功返回该对象的句柄时,系统会为该对象设置一个打开计数,且将该内核对象的计数加1.该函数的作用与释放动态申请的内存空间类似,这样可以保证系统资源不会泄漏,程序可以在安全的状态下运行。CloseHandle()函数使指定的对象句柄数减1。当对象的句柄计数为0时,该对象就从系统中被删除。

2.1.5 创建子进程的程序示例

例1:通过显示创建子进程的基本框架,来观察子进程的创建情况。

// 创建子进程的文件proc_create.cpp

#include

#include

#include

// 创建一个克隆的进程并赋予其ID值

void StartClone(int nCloneID)

{

// 获得用于当前可执行文件的文件名

TCHAR szFilename[MAX_PATH];

GetModuleFileName(NULL, szFilename, MAX_PATH);

// 创建子进程命令行的格式化,获得应用程序的EXE文件名和克隆进程的ID值

TCHAR szCmdLine[MAX_PATH];

sprintf((char *)szCmdLine, "\"%s\"%d", szFilename, nCloneID);

STARTUPINFO si;// 用于子进程的STARTUPINFO结构

ZeroMemory(reinterpret_cast(&si), sizeof(si));

si.cb =sizeof(si);

PROCESS_INFORMATION pi;// 说明一个用于记录子进程的相关信息的结构变量

BOOL bCreateOK = CreateProcess(

szFilename, //可执行的应用程序的名称

szCmdLine, //指定创建一个子进程的符号标识

NULL, //缺省的进程安全性

NULL, //缺省的线程安全性

FALSE, //不继承打开文件的句柄

CREATE_NEW_CONSOLE, //使用新的控制台

NULL, //新的环境

NULL, //当前目录

&si, //启动信息

&pi); //返回进程和线程信息

// 运行结束,关闭进程和其线程的句柄

if(bCreateOK)

{

CloseHandle(pi.hProcess);

CloseHandle(pi.hThread);

}

}

int main(int argc, char* argv[])

{

// 确定进程在列表中的位置

int nClone(0);

if(argc > 1)

{

// 从第二个参数中提取克隆ID

sscanf(argv[1], "%d", &nClone);

}

// 显示进程位置

printf("Process ID:%d,Clone ID: %d\n", GetCurrentProcessId(), nClone);

// 创建2个子进程

const int c_nCloneMax = 2;

if(nClone < c_nCloneMax)

{

StartClone(++nClone); // 发送新进程的命令行和克隆号

Sleep(1000); // 暂停1秒

}

Sleep(500); // 在终止之前暂停0.5秒

return 0;

}

本例程序展示了一个简单的使用CreateProcess() API函数的例子。首先形成简单的命令行,提供当前的EXE文件的指定文件名和代表生成克隆进程的号码。大多数参数都可取缺省值,但是创建标志参数使用了CREATE_NEW_CONSOLE标志,指示新进程分配它自己的控制台,这使得运行该程序时,在任务栏上产生许多活动标记。然后该克隆进程的创建方法关闭传递过来的句柄并返回main()函数。在关闭程序之前,每一进程的执行主线程暂停一下,以便让用户看到其中的至少一个窗口。

2.2 进程对象相关的函数

操作系统将当前运行的应用程序看做是进程对象,利用系统提供的进程唯一的称为句柄(HANDLE)的标识,就可与对应的进程对象交互。

2.2.1 得到当前进程的句柄GetCurrentProcess()函数

函数格式:

HANDLE GetCurrentProcess(void);

参数:无。

返回值:若函数调用成功,返回值为当前进程的句柄。

2.2.2 打开一个进程OpenProcess()函数

函数格式:

HANDLE OpenProcess(DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwProcessId); 参数:

(1)dwDesiredAccess为指定进程对象的访问权限;

(2)bInheritHandle为若要子进程获得对该对象的访问权限,就设置为TRUE,否则设为FALSE;(3)dwProcessId为指定系统范围内的进程标识符。

返回值:若函数调用成功,则返回进程对象的句柄;否则,返回FALSE。

2.2.3 获得进程的优先级类别GetPriorityClass()函数

函数格式:

DWORD GetPriorityClass(HANDLE);

参数:HANDLE为指定进程的句柄。

返回值:若函数调用成功,返回值为指定进程的基本优先级;若函数调用失败,返回值为0。如果要了解进程的返回码,就需要有PROCESS_QUERY_INFORMATION的访问权限。

2.2.4 改变进程优先级SetPriorityClass()函数

函数格式:

BOOL SetPriorityClass(HANDLE hProcess, DWORD dwPriorityClass);

参数:hProcessy为指定进程的句柄,dwPriorityClass为指定进程要设置的优先级。

返回值:函数成功调用,返回值为非0,否则返回值为0。

2.2.5 获得系统中当前进程的快照CreateToolhelp32Snapshot()函数

函数格式:

HANDLE CreateToolhelp32Snapshot(DWORD dwFlags, DWORD th32ProcessID);

参数:dwFlags为指定快照中包括系统线程的情况,th32ProcessID为指定进程的标识符。返回值:若函数调用成功,则返回值为进程快照的句柄。

若参数dwFlags为TH32CS_SNAPPROCESS,则表明快照包括系统中的所有线程,且th32ProcessID为包括在快照中进程的进程标识符。

2.2.6 将指定的存储空间清0 ZeroMemory函数

函数格式:

VOID ZeroMemoery(PVOID Destination, SIZE_T Length);

参数:Destination为指向分配空间的起始地址,Length为分配空间字节长度。

返回值:该函数没有返回值。

2.2.7 获得系统中的第一个进程的快照信息Process32First()函数

函数格式:

BOOL Process32First(HANDLE hSnapshot, LPPROCESSENTRY32 lppe);

参数:hSnapshot为指定快照句柄;lppe为指向PROCESSENTRY32结构指针。

返回值:如果返回值为真,则将系统快照中的第一个进程信息复制到PROCESSENTRY32结构的缓冲区中,否则不做任何操作。

用Process32Next()函数指向下一个进程信息,其中PROCESSENTRY32结构定义如下:typedef struct tagPROCESSENTRY32

{

DWORD dwSize;

DWORD cntUsage;

DWORD th32ProcessID;

ULONG_PTR th32DefaultHeapID;

DWORD th32ModuleID;

DWORD cntThreads;

DWORD th32ParentProcessID;

LONG pcPriClassBase;

DWORD dwFlags;

TCHAR szExeFile[MAX_PATH];

}PROCESSENTRY32, *PROCESSENTRY32;

2.2.8 获得系统下一个进程的快照信息Process32Next()函数

函数格式:

BOOL Process32Next(HANDLE hSnapshot, LPPROCESSENTRY32 lppe);

参数:hSnapshot为指定快照句柄;lppe为指定一个PROCESSENTRY32结构。

返回值:如果进程列表的下一入口点被复制到缓冲区,返回值为真。

2.2.9 获得与进程相关的时间信息GetProcessTimes()函数

函数格式:

BOOL GetProcessTimes(HANDLE hProcess, LPFILETIME lpCreationTime,

LPFILETIME lpExitTime, LPFILETIME lpKernelTime,

LPFILETIME lpUserTime);

参数:

·hProcess为一个进程句柄;

·lpCreationTime为包含有进程的创建时间的一个FILETIME结构;

·lpExitTime为包含有进程的终止时间的一个FILETIME结构;

·lpKernelTime为包含有进程在核心态执行时所消耗的时间的一个FILETIME结构;·lpUserTime为包含有进程在用户态执行时所消耗的时间的一个FILETIME结构。返回值:如果函数成功调用,返回值为非0;如果失败,返回值为0。

2.2.10 获得进程句柄的程序示例

例2:获得和使用进程句柄

// 获得进程句柄的文件prochandle.cpp

#include

#include

// 确定自身优先级的一个简单应用程序

void main()

{

// 调用如下函数,以获得当前进程的句柄

HANDLE hProcessThis = GetCurrentProcess();

// 获得该进程所属的优先级类

DWORD dwPriority = GetPriorityClass(hProcessThis);

// 显示当前进程所属的优先级类型

printf("current process priority: ");

switch (dwPriority)

{

case HIGH_PRIORITY_CLASS:

printf("High\n");

break;

case NORMAL_PRIORITY_CLASS:

printf("Normal\n");

break;

case IDLE_PRIORITY_CLASS:

printf("Idle\n");

break;

case REALTIME_PRIORITY_CLASS:

printf("Realtime\n");

break;

default:

printf("\n");

break;

}

}

本例列出的是一种获得进程句柄的方法。对于进程句柄可进行的唯一有用的操作是在API 调用时,将其作为参数传送系统,如程序中的GetPriorityClass()函数的调用所带的就是进程的句柄。在这种情况下,系统查看进程对象,以决定其优先级,然后将此优先级返回给应用程序。使用OpenProcess()和CreateProcess()函数也可以用于获得进程句柄,前者获得的是已经存在的进程的句柄,而后者创建一个新进程,并返回其句柄。

2.2.11 利用句柄获得进程详细信息的程序示例

例3:利用进程句柄查出进程的详细信息。

// 获得进程句柄信息的文件proclist.cpp

//

#include

#include

#include

using namespace std;

#pragma comment(lib, "kernel32.lib")

// 当在用户态及核心态下都提供所耗时间时,计算在核心态下所消耗的时间(以64位表示)DWORD GetKernelModePercentage(const FILETIME& ftKernel,

const FILETIME& ftUser)

{

// 将FILETIME结构转化为64位整数

ULONGLONG qwKernel = (((ULONGLONG)ftKernel.dwHighDateTime) << 32) +

ftKernel.dwLowDateTime;

ULONGLONG qwUser = (((ULONGLONG)ftUser.dwHighDateTime) << 32) +

ftUser.dwLowDateTime;

// 将消耗时间相加,然后计算消耗在核心态下所用时间百分比

ULONGLONG qwTotal = qwKernel + qwUser;

DWORD dwPct = (DWORD)(((ULONGLONG)100 * qwKernel) / qwTotal);

return dwPct;

}

// 应用程序显示当前运行进程名字以及在核心态下占用时间的百分比

void main()

{

// 获得系统当前进程的快照

HANDLE hSnapshot = CreateToolhelp32Snapshot(

TH32CS_SNAPPROCESS, // 获得当前进程

0); // 如果是当前进程,就将其忽略

//初始化过程入口

PROCESSENTRY32 pe; // 声明进程入口的PROCESSNTRY32结构的变量

ZeroMemory(&pe, sizeof(pe) );// 为pe分配存储空间,并将其清0

pe.dwSize = sizeof(pe);

BOOL bMore = Process32First(hSnapshot, &pe); // 获得系统快照中的第一个进程信息

while(bMore)

{

// 打开用于读取的进程

HANDLE hProcess = OpenProcess(

PROCESS_QUERY_INFORMATION, // 指明要得到信息

FALSE, // 不必继承这一句柄

pe.th32ProcessID); // 要打开的进程

if(hProcess != NULL)

{

// 找出进程的时间

FILETIME ftCreation, ftKernelMode, ftUserMode, ftExit;

GetProcessTimes(

hProcess, // 所感兴趣的进程

&ftCreation, // 进程的启动时间

&ftExit, // 结束时间

&ftKernelMode, // 在核心态下消耗的时间

&ftUserMode); // 在用户态下消耗的时间

// 计算核心态下消耗的时间百分比

DWORD dwPctKernel = GetKernelModePercentage(ftKernelMode, ftUserMode);

// 向用户显示进程的某些信息

cout << "process ID:" << pe.th32ProcessID

<< ", EXE file:" << pe.szExeFile

<< ",%in Kernel mode:" << dwPctKernel << endl;

// 关闭句柄

CloseHandle(hProcess);

}

// 转向下一个进程

bMore = Process32Next(hSnapshot, &pe);

}

}

该程序首先利用Windows系统的新特性(即工具帮助库),来获得当前运行的所有进程的快照。然后应用程序进入快照中的每一个进程,得到其以PROCESSENTRY32结构表示的属性,这一结构用来向OpenProcess()函数提供进程的ID。Windows跟踪每一个进程的有关时间,本例是通过打开的进程句柄和GetProcessTimes()函数来直接得到有关时间,接下来计算进程在核心态下消耗的时间占总时间的百分比。程序的其余部分比较简单,只是将有关信息显示给用户,关闭进程句柄,然后继续循环,直到所有进程都计算完为止。

2.3 运行进程

对于在系统中的每个进程来说,当它至少拥有一个执行线程时就是运行的。通常情况下,进程使用主线程作为其生命周期指示器。当主线程结束时,调用ExitProcess() API函数,通知系统终止它所拥有的所有正在运行、准备运行或正在挂起的其他线程。当进程正在进行时,通过下面一些API,可以获得它的许多特性。

2.3.1 获得一个进程的启动信息GetStartupInfo()函数

函数格式:

VOID GetStartupInfo(LPSTARTUPINFO lpStartupInfo);

参数:lpStartupInf为STARTUPINFO结构,该结构用于最终载入进程的启动信息。

返回值:无。

2.3.2 获得系统关闭时当前进程与其他进程关闭的迟早情况GetProcessShutdownParameters()函数

函数格式:

BOOL GetProcessShutdownParameters(LPDWORD lpdwLevel, LPDWORD lpdwFlags);

参数:lpdwLevel为指定关闭优先级;lpdwFlags为指定关闭标志,若指定为SHUTDOWN-NORETRY标志,如关闭一个进程超时,会出现一个重试对话框。通过设置这个标志,就可以禁止那个对话框显示出来,并直接关闭进程。

返回值:如果函数执行成功,返回值为非0,否则返回值为0。

2.3.3 系统关闭时指定当前进程与其他进程关闭的顺序SetProcessShutdownParameters()函数

函数格式:

BOOL SetProcessShutdownParameters(DWORD dwlevel, DWORD dwFlags);

参数:dwLevel指向进程关闭优先级,系统按照从高到低优先级关闭进程;dwFlags可以为SHUTDOWN_NORETRY值,当系统关闭进程时不出现RETRY对话框。

返回值:函数成功调用,返回值为非0,否则返回值为0。

2.3.4 获得当前命令行缓冲区的一个指针GetCommandLine()函数

函数格式:

LPTSTR GetCommandLine(void);

参数:无

返回值:如果函数执行成功,返回命令缓冲区在内存中的地址。

2.3.5 查询进程进行的操作系统版本信息GetProcessVersion()函数

函数格式:

DWORD GetProcessVersion(DWORD processId)

参数:ProcessId为指定进程的标识符

返回值:如果函数执行成功,返值为系统版本信息。

2.3.6 提取与平台和操作系统的版本相关的信息GetVersionEx()函数

函数格式:

BOOL GetVersionEx(LPOSVERSIONINFO lpVersionInfo);

参数:lpVersionInfo为指向OSVERSIONINFOEX结构,该结构用于装载操作系统的版本信息。返回值:如果函数执行成功,返回值为非0,否则返回值为0。

2.3.7 关闭前台应用程序优先级的提升SetProcessPriorityBoost()函数

函数格式:

BOOL SetProcessPriorityBoost(HANDLE hProcess, BOOL DisablePriorityBoost);

参数:hProcess为指定进程的句柄,DisablePriorityBoost为指定可否提升优先级的标志。

返回值:如果函数执行成功,返回值为非0,否则返回值为0。

2.3.8 设置进程可用的最大和最小工作集SetProcessWorkingSet()函数

函数格式:

BOOL SetProcessWorkingSet(HANDLE hProcess, SIZE_T dwMinimumWorkingSetSize,

SIZE_T dwMaximumWorkingSetSize);

参数:hProcess为指定进程的句柄,dwMinimumWorkingSetSize为进程最小工作区域的字节数,dwMaximumWorkingSetSize为进程最大工作区域的字节数。

返回值:如果函数执行成功,返回值为非0,否则返回值为0。

2.3.9 获取运行进程操作系统版本号的程序示例

例4:获得运行进程的操作系统版本号。

// 获取运行进程操作系统版本号的程序version.cpp

#include

#include

using namespace std;

// 利用进程和操作系统的版本信息的简单示例

void main()

{

DWORD dwIdThis = GetCurrentProcessId();// 获得进程的ID号

DWORD dwVerReq = GetProcessVersion(dwIdThis);// 获得进程和报告使用的操作系统版本WORD wMajorReq = (WORD)(dwVerReq > 16);// 获得操作系统的主版本号

WORD wMinorReq = (WORD)(dwVerReq & 0xffff);// 获得操作系统的次版本号

printf("Process ID: %d, requires OS: %d%d\n", dwIdThis, wMajorReq, wMinorReq);

// 设置版本信息的数据结构,以便保存操作系统的版本信息

OSVERSIONINFOEX osvix;

ZeroMemory(&osvix, sizeof(osvix));

osvix.dwOSVersionInfoSize = sizeof(osvix);

// 提取版本信息和报告

GetVersionEx(reinterpret_cast(&osvix));

printf("Running on OS: %d.%d\n", osvix.dwMajorVersion, osvix.dwMinorVersion);

// 如果是Windows NT5(windows 2000)系统,那么将其提升为高优先权

if(osvix.dwPlatformId == VER_PLATFORM_WIN32_NT && osvix.dwMajorVersion >= 5)

{

// 改变当前运行进程的优先级为高

SetPriorityClass(

GetCurrentProcess(), // 得到该进程的ID

HIGH_PRIORITY_CLASS);// 改变为high

// 报告给用户

printf("Task Manager should now indicate this");

printf("process is high priority.\n");

}

}

该程序展示了如何获得当前进程标识符PID和进程运行的操作系统的版本信息。为了运行这一程序,系统处理了所有的版本不兼容问题。接着,程序演示了如何使用GetVersionEx()函数来提取OSVERSIONINFOEX结构。该结构中包括了操作系统的版本信息,Window 2000/XP 在此数据块内是以Windows NT 5.1表示的。

程序的最后一段利用了操作系统的版本信息,以确认运行的是Windows 2000还是Windows XP。代码接着将当前进程的优先级提升到高优先级。在Windows2000/XP中增加了ABOVE_NORMAL_PRIORITY_CLASS级别,它介于NORMAL_PRIORITY_CLASS和HIGH_PRIORITY_CLASS之间。当右击该进程名以改变其优先级时,任务管理器”应用程序应该立即反映这个变化。

2.4 进程终止和进程同步等待

所有进程都是以调用ExitProcess()或TerminateProcess()函数结束的。前者是在进程完成了它的所有的关闭“职责”之后以正常的终止方法来调用的,而后者是外部进程发生故障后终止进程时调用的,由于关闭时的途径不太正常,有可能引起错误的行为。

只要用PROCESS_TERMINATE访问权打开一个进程对象,TerminateProcess()函数就可以终止该进程,并向系统返回指定的终止码。这是一种“野蛮”的终止进程的方式,但是有时却是需要的。

进程同步是指一个进程或线程可以通过调用系统的相应函数等待一个或多个内核对象变为有信号状态,来实现进程或线程与这些内核对象的同步。能够作为同步对象的有进程、线程、文件、事件、互斥体、信号量等。这里主要介绍等待一个或多个内核对象WaitForsingleObject()或WaitForMultipleObjects()函数。

2.4.1 进程终止

1)进程正常终止ExitProcess()函数

函数格式:

VOID ExitProcess(UINT uExitCode);

参数:uExitCode为终止进程的退出码。

返回值:无。

2)中止指定句柄的进程TerminateProcess()函数

函数格式:

BOOL TerminateProcess(HANDLE hProcess, UINT uExitCode);

参数:hProcess为指定要中断的那个进程的句柄,uExitCode为进程的一个退出码。

返回值:函数成功调用,返回值为非0,否则返回值为0。

2.4.2 获取指定句柄的已终止进程的退出码GetExitCodeProcess()函数

函数格式:

BOOL GetExitCodeProcess(HANDLE hProcess, LPDWORD lpExitCode);

参数:hProcess为指定终止的那个进程的句柄;uExitCode为用于获得进程退出码的一个整数变量。

返回值:函数成功调用,返回值为非0,否则返回值为0。

2.4.3 等待一个同步对象WaitForsingleObject()函数

函数格式:

DWORD WaitForsingleObject(HANDLE hHandle, DWORD dwMilliseconds);

参数:

(1)hHandle为被等待对象的句柄

(2)dwMilliseconds为指定以毫秒为单位的等待时间。

2.4.4 等待多个同步对象WaitForMultipleObjects()函数

函数格式:

DWORD WaitForMultipleObjects(DWORD cObjects,

LPHANDLE lphandles, BOOL bwaitALL, DWORD dwtimeout);

参数:cobjects是被等待的对象个数;lphandles是被等待对象的句柄数组指针;bwaitALL为TRUE时,则等待全部同步对象变为有信号状态,否则,有一个变为有信号状态就结束等待。立即返回;wtimeout为等待的时间,含义同WaitForsingleObject()。

返回值:如果函数调用成功,返回值表明引起函数返回的事件。

2.4.5 终止进程的示例程序

例5:终止进程

// 终止进程应用程序procterm.cpp

#include

#include

#include

static LPCTSTR g_szMutexName = (LPCTSTR)"w2kdg.ProcTerm.mutex.Suicide";

// 创建当前进程的克隆进程的简单方法

HANDLE StartClone()

{

TCHAR szFilename[MAX_PATH];

// 获得当前可执行文件的文件名

GetModuleFileName(NULL, szFilename, MAX_PATH);

// 格式化子进程的命令行,指明它是一个EXE文件和子进程

TCHAR szCmdLine[MAX_PATH];

sprintf((char*)szCmdLine, "\"%s\"child", szFilename);

STARTUPINFO si; // 子进程的启动信息结构

ZeroMemory(reinterpret_cast(&si), sizeof(si));

si.cb = sizeof(si);

PROCESS_INFORMATION pi; // 说明一个结构,用来返回子进程信息

// 利用同样的可执行文件名和命令行创建进程,并指明它是一个子进程

BOOL bCreateOK = CreateProcess(

szFilename,

szCmdLine,

NULL,

NULL,

FALSE,

CREATE_NEW_CONSOLE,

NULL,

NULL,

&si,

&pi);

// 关闭子进程及它的线程

if(bCreateOK)

{

CloseHandle(pi.hProcess);

CloseHandle(pi.hThread);

return pi.hProcess;

}

else

return INVALID_HANDLE_VALUE;

}

void Parent()

{

printf("Creating the child process and waited child process to quit.\n");

HANDLE hchild = StartClone();

if(hchild != INVALID_HANDLE_VALUE)

{

// 等待子进程终止

WaitForSingleObject(hchild, INFINITE);

printf("The child process had quited.\n");

}

else

printf("Create child process failed.\n");

}

void Child()

{

printf("Child is quiting.\n");

Sleep(5000);

}

int main(int argc, char* argv[])

{

// 决定其行为是父进程还是子进程

if(argc > 1 && strcmp(argv[1], "child") == 0)

{

Child();

}

else

{

Parent();

}

return 0;

}

该程序说明一个进程从“生”到”死”的整个生命期。第一次执行时,它创建一个子进程,其如同“父进程”。然后父进程调用WaitForSingleObject()等待子进程终止。子进程终止后,父进程也终止,该程序运行结束。

子进程被创建后,调用sleep()模拟处理事务,之后终止自己,并唤醒父进程。

ExitProcess() API是终止进程的比较好的方法,但使用时要小心。当调用该函数时,进程中的所有线程都被立刻通知停止。在设计应用程序时,必须让主线程在正常的C++运行期关闭(这是由编译器提供的缺省行为),之后来调用这一函数,通常情况下,应用程序的框架负责调用ExitProcess()函数。对于C++运行库来说,这一调用发生在应用程序的main()函数返回之后(如果采用C运行库,则调用WinMain()函数)。当它转向有信号状态时,通常创建一个其所有活动线程都可等待和停止的终止事件。

linux进程控制 实验报告

长安大学 操作系统实验报告 实验课程:操作系统 实验名称:linux进程控制 学院:信息学院 专业:软件工程 学号:2406090106 姓名:刘建 日期:2012-5-09

一、实验目的 熟悉进程的创建过程,了解系统调用函数fork() 和execl()。 二、实验内容 1、阅读实例代码fork1,并编辑、编译、运行,记录程序的运行结果,尝试给出合理的解释,查阅有关资料,掌握系统调用fork( )的用法,返回值的意义。 2、阅读实例代码fork2,并编辑、编译、运行,记录程序的运行结果,尝试给出合理的解释,查阅有关资料,掌握在程序中运行一个操作系统命令和运行一个程序的方法。 3、修改fork2,使之能把运行的命令和程序作为参数传给fork2。 三、设计思想 1、程序框架

pid = -1 pid = 0pid> 0 2、用到的文件系统调用函数 fork() 和execl() 四、调试过程 1、测试数据设计 (1)fork1 命名程序1: 编写程序1:

编译程序1: 运行程序1: (2)fork2

编写程序2: 运行程序2:

(3)修改fork2 编写修改程序2: 修改后的运行结果: 2、测试结果分析 (1)对于程序1:因为系统调用fork()函数是一次调用两次返回值,而且先生成子进程还是父进程是不确定的,所以第一次执行生成子进程的时候返回的pid = 0,判断pid!=-1,所以输出了I’m the child. I’m the parent. 第二次,执行父进程的时候,返回的是子进程的进程号pid> 0,即pid的值仍然不为-1,所以又输出了一次I’m the child. I’m the parent。 (2)对于程序2:第一次调用fork()函数时,由于执行的是子进程还是父进程是随机的,所以第一次对父进程返回的是子进程的进程号(大于0),即pid> 0,所以输出I’m the parent. Program end.当第二次执行子进程时返回值是0,即pid = 0,所以输出I’m the child. 并调用了execl()函数,查看了指定路径中的文件。

实验二进程管理

实验二进程管理 (一)实验目的或实验原理 1.加深对进程概念的理解,明确进程和程序的区别。 2.进一步认识并发执行的实质。 3.分析进程竞争资源现象,学习解决进程互斥的方法。 4.了解Linux系统中进程通信的基本原理。 (二)实验内容 1.进程的创建。 2.进程的控制。 3.①编写一段程序,使其现实进程的软中断通信。 要求:使用系统调用fork()创建两个子进程,再用系统调用signal()让父进程捕捉键盘上来的中断信号(即按DEL键);当捕捉到中断信号后,父进程用系统调用Kill()向两个子进程发出信号,子进程捕捉到信号后分别输出下列信息后终止: Child Processll is Killed by Parent! Child Processl2 is Killed by Parent! 父进程等待两个子进程终止后,输出如下的信息后终止 Parent Process is Killed! ②在上面的程序中增加语句signal (SIGNAL, SIG-IGN) 和signal (SIGQUIT, SIG-IGN), 观察执行结果,并分析原因。 4.进程的管道通信。 编制一段程序,实现进程的管理通信。 使用系统调用pipe()建立一条管道线;两个子进程P1和P2分别向管道中写一句话: Child 1 is sending a message! Child 2 is sending a message! 而父进程则从管道中读出来自于两个子进程的信息,显示在屏幕上。 要求父进程先接收子进程P1发来的消息,然后再接收子进程P2发来的消息。 实验2 指导 [实验内容] 1.进程的创建 〈任务〉 编写一段程序,使用系统调用fork( )创建两个子进程。当此程序运行时,在系统中有一个父进程和两个子进程活动。让每一个进程在屏幕上显示一个字符;父进程显示字符“a”,子进程分别显示字符“b”和“c”。试观察记录屏幕上的显示结果,并分析原因。 〈程序〉 #include<> main() { int p1,p2; if(p1=fork()) /*子进程创建成功*/ p utchar('b'); else { if(p2=fork()) /*子进程创建成功*/

自动控制原理实验报告

《自动控制原理》 实验报告 姓名: 学号: 专业: 班级: 时段: 成绩: 工学院自动化系

实验一 典型环节的MATLAB 仿真 一、实验目的 1.熟悉MATLAB 桌面和命令窗口,初步了解SIMULINK 功能模块的使用方法。 2.通过观察典型环节在单位阶跃信号作用下的动态特性,加深对各典型环节响应曲线的理解。 3.定性了解各参数变化对典型环节动态特性的影响。 二、实验原理 1.比例环节的传递函数为 K R K R R R Z Z s G 200,1002)(211 212==-=-=- = 其对应的模拟电路及SIMULINK 图形如图1-3所示。 三、实验内容 按下列各典型环节的传递函数,建立相应的SIMULINK 仿真模型,观察并记录其单位阶跃响应波形。 ① 比例环节1)(1=s G 和2)(1=s G ; ② 惯性环节11)(1+= s s G 和1 5.01 )(2+=s s G ③ 积分环节s s G 1)(1= ④ 微分环节s s G =)(1 ⑤ 比例+微分环节(PD )2)(1+=s s G 和1)(2+=s s G ⑥ 比例+积分环节(PI )s s G 11)(1+=和s s G 211)(2+= 四、实验结果及分析 图1-3 比例环节的模拟电路及SIMULINK 图形

① 仿真模型及波形图1)(1=s G 和2)(1=s G ② 仿真模型及波形图11)(1+= s s G 和1 5.01)(2+=s s G 11)(1+= s s G 1 5.01 )(2+=s s G ③ 积分环节s s G 1)(1= ④ 微分环节

操作系统实验-进程控制

实验一、进程控制实验 1.1 实验目的 加深对于进程并发执行概念的理解。实践并发进程的创建和控制方法。观察和体验进程的动态特性。进一步理解进程生命期期间创建、变换、撤销状态变换的过程。掌握进程控制的方法,了解父子进程间的控制和协作关系。练习Linux 系统中进程创建与控制有关的系统调用的编程和调试技术。 1.2 实验说明 1)与进程创建、执行有关的系统调用说明进程可以通过系统调用fork()创建子进程并和其子进程并发执行.子进程初始的执行映像是父进程的一个复本.子进程可以通过exec()系统调用族装入一个新的执行程序。父进程可以使用wait()或waitpid()系统调用等待子进程的结束并负责收集和清理子进程的退出状态。 fork()系统调用语法: #include pid_t fork(void); fork 成功创建子进程后将返回子进程的进程号,不成功会返回-1. exec 系统调用有一组6 个函数,其中示例实验中引用了execve 系统调用语法: #include int execve(const char *path, const char *argv[], const char * envp[]); path 要装入 的新的执行文件的绝对路径名字符串. argv[] 要传递给新执行程序的完整的命令参数列表(可以为空). envp[] 要传递给新执行程序的完整的环境变量参数列表(可以为空).

Exec 执行成功后将用一个新的程序代替原进程,但进程号不变,它绝不会再返回到调用进程了。如果exec 调用失败,它会返回-1。 wait() 系统调用语法: #include #include pid_t wait(int *status); pid_t waitpid(pid_t pid,int *status,int option); status 用 于保留子进程的退出状态 pid 可以为以下可能值: -1 等待所有PGID 等于PID 的绝对值的子进程 1 等待所有子进程 0 等待所有PGID 等于调用进程的子进程 >0 等待PID 等于pid 的子进程option 规 定了调用waitpid 进程的行为: WNOHANG 没有子进程时立即返回 WUNTRACED 没有报告状态的进程时返回 wait 和waitpid 执行成功将返回终止的子进程的进程号,不成功返回-1。 getpid()系统调用语法: #include #include pid_t getpid(void); pid_t getppid(void); getpid 返回当前进程的进程号,getppid 返回当前进程父进程的进程号 2)与进程控制有关的系统调用说明可以通过信号向一个进程发送消息以控制进程的 行为。信号是由中断或异常事件引发的,如:键盘中断、定时器中断、非法内存引

进程管理实验报告

实验2过程管理实验报告学生号姓名班级电气工程系过程、过程控制块等基本原理过程的含义:过程是程序运行过程中对数据集的处理,以及由独立单元对系统资源的分配和调度。在不同的数据集上运行程序,甚至在同一数据集上运行多个程序,是一个不同的过程。(2)程序状态:一般来说,一个程序必须有三种基本状态:就绪、执行和阻塞。然而,在许多系统中,过程的状态变化可以更好地描述,并且增加了两种状态:新状态和终端状态。1)就绪状态,当一个进程被分配了除处理器(CPU)以外的所有必要资源时,只要获得了处理器,进程就可以立即执行。此时,进程状态称为就绪状态。在系统中,多个进程可以同时处于就绪状态。通常,这些就绪进程被安排在一个或多个队列中,这些队列称为就绪队列。2)一旦处于就绪状态的进程得到处理器,它就可以运行了。进程的状态称为执行状态。在单处理器系统中,只有一个进程在执行。在多处理器系统中,可能有多个进程在执行中。3)阻塞状态由于某些事件(如请求输入和输出、额外空间等),执行进程被挂起。这称为阻塞状态,也称为等待状态。通常,处于阻塞状态的进程被调度为-?这个队列称为阻塞队列。4)新状态当一个新进程刚刚建立并且还没有放入就绪队列中时,它被称为新状态。5)终止状态是

什么时候-?进程已正常或异常终止,操作系统已将其从系统队列中删除,但尚未取消。这就是所谓的终结状态。(3)过程控制块是过程实体的重要组成部分,是操作系统中最重要的记录数据。控制块PCB记录操作系统描述过程和控制过程操作所需的所有信息。通过PCB,一个不能独立运行的程序可以成为一个可以独立运行的基本单元,并且可以同时执行一个进程。换句话说,在进程的整个生命周期中,操作系统通过进程PCB管理和控制并发进程。过程控制块是系统用于过程控制的数据结构。系统根据进程的PCB来检测进程是否存在。因此,进程控制块是进程存在的唯一标志。当系统创建一个进程时,它需要为它创建一个PCB;当进程结束时,系统回收其PCB,进程结束。过程控制块的内容过程控制块主要包括以下四个方面的信息。过程标识信息过程标识用于对过程进行标识,通常有外部标识和内部标识。外部标识符由流程的创建者命名。通常是一串字母和数字。当用户访问进程时使用。外部标识符很容易记住。内部标识符是为了方便系统而设置的。操作系统为每个进程分配一个唯一的整数作为内部标识符。通常是进程的序列号。描述性信息(process scheduling message)描述性信息是与流程调度相关的一些有关流程状态的信息,包括以下几个方面。流程状态:表

操作系统实验报告--实验一--进程管理

实验一进程管理 一、目的 进程调度是处理机管理的核心内容。本实验要求编写和调试一个简单的进程调度程序。通过本实验加深理解有关进程控制块、进程队列的概念,并体会和了解进程调度算法的具体实施办法。 二、实验内容及要求 1、设计进程控制块PCB的结构(PCB结构通常包括以下信息:进程名(进程ID)、进程优先数、轮转时间片、进程所占用的CPU时间、进程的状态、当前队列指针等。可根据实验的不同,PCB结构的内容可以作适当的增删)。为了便于处理,程序中的某进程运行时间以时间片为单位计算。各进程的轮转时间数以及进程需运行的时间片数的初始值均由用户给定。 2、系统资源(r1…r w),共有w类,每类数目为r1…r w。随机产生n进程P i(id,s(j,k),t),0<=i<=n,0<=j<=m,0<=k<=dt为总运行时间,在运行过程中,会随机申请新的资源。 3、每个进程可有三个状态(即就绪状态W、运行状态R、等待或阻塞状态B),并假设初始状态为就绪状态。建立进程就绪队列。 4、编制进程调度算法:时间片轮转调度算法 本程序用该算法对n个进程进行调度,进程每执行一次,CPU时间片数加1,进程还需要的时间片数减1。在调度算法中,采用固定时间片(即:每执行一次进程,该进程的执行时间片数为已执行了1个单位),这时,CPU时间片数加1,进程还需要的时间片数减1,并排列到就绪队列的尾上。 三、实验环境 操作系统环境:Windows系统。 编程语言:C#。 四、实验思路和设计 1、程序流程图

2、主要程序代码 //PCB结构体 struct pcb { public int id; //进程ID public int ra; //所需资源A的数量 public int rb; //所需资源B的数量 public int rc; //所需资源C的数量 public int ntime; //所需的时间片个数 public int rtime; //已经运行的时间片个数 public char state; //进程状态,W(等待)、R(运行)、B(阻塞) //public int next; } ArrayList hready = new ArrayList(); ArrayList hblock = new ArrayList(); Random random = new Random(); //ArrayList p = new ArrayList(); int m, n, r, a,a1, b,b1, c,c1, h = 0, i = 1, time1Inteval;//m为要模拟的进程个数,n为初始化进程个数 //r为可随机产生的进程数(r=m-n) //a,b,c分别为A,B,C三类资源的总量 //i为进城计数,i=1…n //h为运行的时间片次数,time1Inteval为时间片大小(毫秒) //对进程进行初始化,建立就绪数组、阻塞数组。 public void input()//对进程进行初始化,建立就绪队列、阻塞队列 { m = int.Parse(textBox4.Text); n = int.Parse(textBox5.Text); a = int.Parse(textBox6.Text); b = int.Parse(textBox7.Text); c = int.Parse(textBox8.Text); a1 = a; b1 = b; c1 = c; r = m - n; time1Inteval = int.Parse(textBox9.Text); timer1.Interval = time1Inteval; for (i = 1; i <= n; i++) { pcb jincheng = new pcb(); jincheng.id = i; jincheng.ra = (random.Next(a) + 1); jincheng.rb = (random.Next(b) + 1); jincheng.rc = (random.Next(c) + 1); jincheng.ntime = (random.Next(1, 5)); jincheng.rtime = 0;

进程管理实验报告

进程的控制 1 .实验目的 通过进程的创建、撤消和运行加深对进程概念和进程并发执行的理解,明确进程与程序之间的区别。 【答:进程概念和程序概念最大的不同之处在于: (1)进程是动态的,而程序是静态的。 (2)进程有一定的生命期,而程序是指令的集合,本身无“运动”的含义。没有建立进程的程序不能作为1个独立单位得到操作系统的认可。 (3)1个程序可以对应多个进程,但1个进程只能对应1个程序。进程和程序的关系犹如演出和剧本的关系。 (4)进程和程序的组成不同。从静态角度看,进程由程序、数据和进程控制块(PCB)三部分组成。而程序是一组有序的指令集合。】2 .实验内容 (1) 了解系统调用fork()、execvp()和wait()的功能和实现过程。 (2) 编写一段程序,使用系统调用fork()来创建两个子进程,并由父进程重复显示字符串“parent:”和自己的标识数,而子进程则重复显示字符串“child:”和自己的标识数。 (3) 编写一段程序,使用系统调用fork()来创建一个子进程。子进程通过系统调用execvp()更换自己的执行代码,新的代码显示“new

program.”。而父进程则调用wait()等待子进程结束,并在子进程结束后显示子进程的标识符,然后正常结束。 3 .实验步骤 (1)gedit创建进程1.c (2)使用gcc 1.c -o 1编译并./1运行程序1.c #include #include #include #include void mian(){ int id; if(fork()==0) {printf(“child id is %d\n”,getpid()); } else if(fork()==0) {printf(“child2 id %d\n”,getpid()); } else {id=wait(); printf(“parent id is %d\n”,getpid()); }

操作系统实验二(进程管理)

操作系统进程管理实验 实验题目: (1)进程的创建编写一段程序,使用系统调用fork( )创建两个子进程。当此程序运行时,在系统中有一个父进程和两个子进程活动。让每一个进程在屏幕上显示一个字符:父进程显示字符“a”;子进程分别显示字符“b”和字符“c”。试观察记录屏幕上的显示结果,并分析原因。 (2)进程的控制修改已编写的程序,将每个进程输出一个字符改为每个进程输出一句话,在观察程序执行时屏幕上出现的现象,并分析原因。 (3)编制一段程序,使其实现进程的软中断通信。要求:使用系统调用fork( )创建两个子进程,再用系统调用signal( )让父进程捕捉键盘上来的中断信号(即按Del键);当捕捉到中断信号后,父进程调用系统调用kill( )向两个子进程发出信号,子进程捕捉到信号后分别输出下列信息后终止:Child process 1 is killed by parent! Child process 2 is killed by parent! 父进程等待两个子进程终止后,输出如下的信息后终止:Parent process is killed! 在上面的程序中增加语句signal(SIGINT, SIG_IGN)和signal(SIGQUIT, SIG_IGN),观察执行结果,并分析原因。 (4)进程的管道通信编制一段程序,实现进程的管道通信。使用系统调用pipe( )建立一条管道线;两个进程P1和P2分别向管道各写一句话:Child 1 is sending a message! Child 2 is sending a message! 而父进程则从管道中读出来自于两个子进程的信息,显示在屏幕上。要求父进程先接收子进程P1发来的消息,然后再接收子进程P2发来的消息。 实验源程序及报告: (1)、进程的创建 #include int main(int argc, char *argv[]) { int pid1,pid2; /*fork first child process*/ if ( ( pid1=fork() ) < 0 ) { printf( "ProcessCreate Failed!"); exit(-1); }

自动控制实验报告1

东南大学自动控制实验室 实验报告 课程名称:自动控制原理 实验名称:闭环电压控制系统研究 院(系):仪器科学与工程专业:测控技术与仪器姓名:学号: 实验室:常州楼五楼实验组别:/ 同组人员:实验时间:2018/10/17 评定成绩:审阅教师: 实验三闭环电压控制系统研究

一、实验目的: (1)通过实例展示,认识自动控制系统的组成、功能。 (2)会正确实现闭环负反馈。 (3)通过开、闭环实验数据说明闭环控制效果。 二、实验原理: (1)利用各种实际物理装置(如电子装置、机械装置、化工装置等)在数学上的“相似性”,将各种实际物理装置从感兴趣的角度经过简化、并抽象成相同的数学形式。我们在设计控制系统时,不必研究每一种实际装置,而用几种“等价”的数学形式来表达、研究和设计。又由于人本身的自然属性,人对数学而言,不能直接感受它的自然物理属性,这给我们分析和设计带来了困难。所以,我们又用替代、模拟、仿真的形式把数学形式再变成“模拟实物”来研究。这样,就可以“秀才不出门,遍知天下事”。实际上,在后面的课程里,不同专业的学生将面对不同的实际物理对象,而“模拟实物”的实验方式可以做到举一反三,我们就是用下列“模拟实物”——电路系统,替代各种实际物理对象。 (2)自动控制的根本是闭环,尽管有的系统不能直接感受到它的闭环形式,如步进电机控制,专家系统等,从大局看,还是闭环。闭环控制可以带来想象不到的好处,本实验就是用开环和闭环在负载扰动下的实验数据,说明闭环控制效果。自动控制系统性能的优劣,其原因之一就是取决调节器的结构和算法的设计(本课程主要用串联调节、状态反馈),本实验为了简洁,采用单闭环、比例调节器K。通过实验证明:不同的K,对系性能产生不同的影响,以说明正确设计调节器算法的重要性。 (3)为了使实验有代表性,本实验采用三阶(高阶)系统。这样,当调节器K值过大时,控制系统会产生典型的现象——振荡。本实验也可以认为是一个真实的电压控制系统。 三、实验设备: THBDC-1实验平台 四、实验线路图: 五、实验步骤:

实验一-进程控制实验

实验一进程控制 一、实验目的: 加深对进程概念的理解,明确进程和程序的区别;掌握Linux操作系统的进程创建和终止操作,体会父进程和子进程的关系及进程状态的变化;进一步认识并发执行的实质,编写并发程序。 二、实验平台: 虚拟机:VMWare9以上 操作系统:Ubuntu12.04以上 编辑器:Gedit | Vim 编译器:Gcc 三、实验内容: (1)编写一段程序,使用系统调用fork()创建两个子进程,当此程序运行时,在系统中有一个父进程和两个子进程活动。让每一个进程在屏幕上显示“身份信息”:父进程显示“Parent process! PID=xxx1 PPID=xxx2”;子进程显示“Childx process! PID=xxx PPID=xxx”。多运行几次,观察记录屏幕上的显示结果,并分析原因。 说明: xxx1为进程号,用getpid()函数可获取进程号; xxx2为父进程号,用getppid()函数可获取父进程号; Childx中x为1和2,用来区别两个子进程; wait()函数用来避免父进程在子进程终止之前终止。 程序源码: #include #include #include #define NUM 2 int main(void) {

pid_t pid1,pid2; if((pid1=fork())<0){ printf("创建进程1失败"); }else{ if(pid1==0){ //子进程1执行 printf("Child1 process: "); printf("PID=%d PPID=%d \n",getpid(),getppid()); sleep(2); }else{ if((pid2=fork())<0){ printf("创建进程2失败"); }else{ if(pid2==0){ //子进程2执行 printf("Child2 process: "); printf("PID=%d PPID=%d \n",getpid(),getppid()); } else{ //父进程执行 wait(); wait(); printf("Parent process: "); printf("PID=%d PPID=%d \n",getpid(),getppid()); exit(0); } } } } }

浙工大过程控制实验报告

浙工大过程控制实验报告 202103120423徐天宇过程控制系统实验报告 实验一:系统认识及对象特性测试 一实验目的 1了解实验装置结构和组成及组态软件的组成使用。 2 熟悉智能仪表的使用及实验装置和软件的操作。 3熟悉单容液位过程的数学模型及阶跃响应曲线的实验方法。 4学会有实际测的得单容液位过程的阶跃响应曲线,用相关的方法分别确定它们的参数,辨识过程的数学模型。二实验内容 1 熟悉用MCGS组态的智能仪表过程控制系统。 2 用阶跃响应曲线测定单容液位过程的数学模型。三实验设备 1 AE2000B型过程控制实验装置。 2 计算机,万用表各一台。 3 RS232-485转换器1只,串口线1根,实验连接线若干。四实验原理 如图1-1所示,设水箱的进水量为Q1,出水量为Q2,水箱的液面高度为h,出水阀V2固定于某一开度值。根据物料动态平衡的关系,求得: 在零初始条件下,对上式求拉氏变换,得:

式中,T为水箱的时间常数(注意:阀V2的开度大小会影响到水箱的时间常数),T=R2*C,K=R2为单容对象的放大倍数, R1、R2分别为V1、V2阀的液阻,C 为水箱的容量系数。 阶跃响应曲线法是指通过调节过程的调节阀,使过程的控制输入产生一个阶跃变化,将被控量随时间变化的阶跃响应曲线记录下来,再根据测试记录的响应曲线求取输入输出之间的数学模型。本实验中输入为电动调节阀的开度给定值OP,通过改变电动调节阀的开度给定单容过程以阶跃变化的信号,输出为上水箱的液位高度h。电动调节阀的开度op通过组态软件界面有计算机传给智能仪表,有智能仪表输出范围为:0~100%。水箱液位高度有由传感变送器检测转换为4~20mA的标准信号,在经过智能仪表将该信号上传到计算机的组态中,由组态直接换算成高度值,在计算机窗口中显示。因此,单容液位被控对象的传递函数,是包含了由执行结构到检测装置的所有液位单回路物理关系模型有上述机理建模可知,单容液位过程是带有时滞性的一阶惯性环节,电动调节阀的开度op,近似看成与流量Q1成正比,当电动调节阀的开度op为一常量作为阶跃信号时,该单容液位过程的阶跃响应为 需要说明的是表达式(2-3)是初始量为零的情况,如果是在一个稳定的过程下进行的阶跃响应,即输入量是在原来的基础上叠加上op的变化,则输出表达式是对应原来输出值得基础上的增

自动控制原理实验报告2

自动控制原理课程实验 2010-2011学年第一学期 02020801班 张驰2008300566

? 课本实验内容 6-26 热轧厂的主要工序是将炽热的钢坯轧成具有预定厚度和尺度的钢板,所得到的最终产品之一是宽为3300mm 、厚为180mm 的标准板材。他有两台主要的辊轧台:1号台与2号台。辊轧台上装有直径为508mm 的大型辊轧台,由4470km 大功率电机驱动,并通过大型液压缸来调节轧制宽度和力度。 热轧机的典型工作流程是:钢坯首先在熔炉中加热,加热后的钢坯通过1号台,被辊轧机轧制成具有预期宽度的钢坯,然后通过2号台,由辊轧机轧制成具有与其厚度的钢板,最后再由热整平设备加以整平成型。 热轧机系统控制的关键技术是通过调整热轧机的间隙来控制钢板的厚度。热轧机控制系统框图如下: 扰动)(s N )(s R (1)已知)54(/)(20++=s s s s s G ,而)(s G c 为具有两个相同实零点的PID 控制器。要求:选择PID 控制器的零点和增益,使闭环系统有两对对等的特征根; (2)考察(1)中得到的闭环系统,给出不考虑前置滤波器)(s G P 与配置适当)(s G P 时,系统的单位阶跃响应; (3)当)(s R =0,)(s N =1/s 时,计算系统对单位阶跃扰动的响应。 ? 求解过程 解:(1)已知 )54(/)(20++=s s s s s G )(s G P )(s G C )(0s G

选择 s z s K s G c /)()(2+= 当取K=4,Z=1.25时,有 s s s s s G c 4/25.610/)25.1(4)(2++=+= 系统开环传递函数 )54(/)25.1(4)()(2220+++=s s s s s G s G c 闭环传递函数:)25.61094/()5625.15.2(4))()(1/()()()(2 34200++++++=+=s s s s s s s G s G s G s G s c c φ (2) 当不考虑前置滤波器时,单位阶跃输入作用下的系统输出 )25.61094(/)5625.15.2(4)()()(2342++++++==s s s s s s s s R s s C φ 系统单位阶跃响应如图1中(1)中实线所示。 当考虑前置滤波器时,选 2)25.1/(5625.1)(+=s s G p 则系统在单位阶跃输入作用下的系统输出 )25.61094(/25.6)()()()(234++++==s s s s s s R s s G s C p φ 系统单位阶跃曲线如图1中(1)虚线所示。 (3)当)(s R =0,)(s N =1/s 时,扰动作用下的闭环传递函数 )25.61094/())()(1/()()(23400++++-=+-=s s s s s s G s G s G s c n φ 系统输出 )25.61094/(1)()()(2 34++++-==s s s s s N s s C n n φ 单位阶跃响应曲线如图1中(2)所示。 MATLAB 程序代码: MA TLAB 程序:exe626.m K=4;z=1.25; G0=tf(1,conv([1,0],[1,4,5])); Gc=tf(K*conv([1,z],[1,z]),[1,0]); Gp=tf(1.5625,conv([1,z],[1,z])); G1=feedback(Gc*G0,1); G2=series(Gp,G1); G3=-feedback(G0,Gc); t=0:0.01:10; [x,y]=step(G1,t);[x1,y1]=step(G2,t); figure(1);plot(t,x,'-',t,x1,':');grid

实验1进程的控制与描述资料

实验1 进程的控制与描述 1.1 Windows任务管理器的进程管理 1.1.1 背景知识 Windows 的任务管理器提供了用户计算机上正在运行的程序和进程的相关信息,也显示了最常用的度量进程性能的单位。使用任务管理器.可以打开监视计算机性能的关键指示器,快速查看正在运行的程序的状态,或者终止已停止响应的程序。也可以使用多个参数评估正在运行的进程的活动,以及查看CPU和内存使用情况的图形和数据。其中: (1)“应用程序”选项卡显示正在运行程序的状态,用户能够结束、切换或者启动程序。(2)“进程”选项卡显示正在运行的进程信息。例如,可以显示关于CPU和内存使用情况、页面错误、句柄计数以及许多其他参数的信息。 (3)“性能”选项卡显示计算机动态性能,包括CPU和内存使用情况的图表,正在运行的句柄、线程和进程的总数、物理、核心和认可的内存总数(KB)等。 1.1.2 实验目的 通过在windows任务管理器中对程序进程进行响应的管理操作系统进程管理的概念,学习观察操作系统运行的动态性能。 1.1.3工具/准备工作 在开始本实验之前,请回顾教科书的相关内容。 需要准备一台运行windows 7操作系统的计算机。 1.1.4 实验内容与步骤 启动并进入Windows环境,按Ctrl+A1t+DeL键,或者右键单击任务栏,快捷菜单中单击“任务管理器”,打开“任务管理器”窗口。 在本次实验中,你使用的操作系统版本是: 在当前机器中,由你打开、正在运行的应用程序有:

Windows“任务管理器”的窗口由个选项卡组成,分别是: 当前“进程”选项卡显示的栏目分别是(可移动窗口下方的游标/箭头,或使窗口最大化进行观察): (1)使用任务管理器终止进程 步骤1:单击“进程”选项卡,一共显示个进程。请试着区分一下,其中:系统(system)进程有个,填入表2-1中。 表2-1 实验记录

过程控制实验报告

《过程控制实验》 实验报告

第一章、过程控制实验装置的认识 一、过程控制实验的基本内容及概述 本次过程控制实验主要是对实验室的水箱水位进行控制。水箱液位控制系统是一个简单控制系统,所谓简单液位控制系统通常是指由一个被控对象、一个检测变送单元(检测元件及变送器)、以个控制器和一个执行器(控制阀)所组成的单闭环负反馈控制系统,也称为单回路控制系统。 简单控制系统有着共同的特征,它们均有四个基本环节组成,即被控对象、测量变送装置、控制器和执行器。 图1-1 水箱液位控制系统的原理框图 这是单回路水箱液位控制系统,单回路调节系统一般指在一个调节对象上用一个调节器来保持一个参数的恒定,而调节器只接受一个测量信号,其输出也只控制一个执行机构。本系统所要保持的恒定参数是液位的给定高度,即控制的任务是控制水箱液位等于给定值所要求的高度。 二、主要设备 1)水路装置的认识 过程控制实验用的水路装置图如下

图1-2 水路图 由水路装置图我们看到,装置主要有水箱,交流电动泵,热炉,管道,电动阀,电磁阀,流量计,液位传感器,温度传感器组成,可以构成一个完整的过程控制实验平台。从上图我们可以看出,装置主要分为两大部分,第一水路,管道,热炉,水箱等等物理对象,第二是传感器,执行机构等等的控制部分的装置。 实验装置具体介绍如下:

b)电气连接图 由电气装置的图我们可以看到,所有的电器连接都在这里,主要是一些传感器信号,电动驱动信号,用于电动装置的驱动。 见附件 c)操作面板图: 从操作面板上我们可以看到主要是由四个表,由P909构成,用于测量控制压力、流量、液位、温度的测量以及控制,PV代表反馈测量,外给定可以用于串级控制,OUT用于输出信号,以上接口均使用4-20mA标准 见附件 第二、三章、实验系统的认知(包括力控软件,P909,实验装置) a)力控软件的安装 首先使用光盘里的Setup.exe安装力控软件的主题部分,然后将IO Servers文件夹拷到力控软件的安装目录下,安装IO Servers驱动 然后打开力控软件,寻找到力控软件的目录,点击开发模式,然后找到COM设置的部分,如图

实验二 进程管理

实验二进程管理 实验目的 通过进程的创建、撤销和运行加深对进程概念和进程并发执行的理解,明确进程与程序的区别。 实验内容 1、了解系统调用fork()、exec()、exit()和waitpid()的功能和实现过程。 2、编写一段程序,使用系统调用fork()创建两个子进程。当此程序运行时,在系统中有一个父进程和两个子进程活动。让每一个进程在屏幕上显示一个字符:父进程显示字符“a”;子进程分别显示字符“b”和“c”。试观察记录屏幕上的显示结果,并分析原因。 3、编写一段程序,使用系统调用fork()来创建一个子进程,子进程通过系统调用exec()更换自己的执行代码,显示新的代码“new program.”后,调用exit()结束。而父进程则调用waitpid()等待子进程结束,并在子进程结束后显示子进程的标识符,然后正常结束。

实验指导 一、所涉及的系统调用 1、getpid 在2.4.4版内核中,getpid是第20号系统调用,其在Linux函数库中的原型是: getpid的作用很简单,就是返回当前进程的进程ID,请大家看以下的例子: 这个程序的定义里并没有包含头文件sys/types.h,这是因为我们在程序中没有用到pid_t类型,pid_t类型即为进程ID的类型。事实上,在i386架构上(就是我们一般

PC计算机的架构),pid_t类型是和int类型完全兼容的,我们可以用处理整形数的方法去处理pid_t类型的数据,比如,用"%d"把它打印出来。 编译并运行程序getpid_test.c: 再运行一遍: 正如我们所见,尽管是同一个应用程序,每一次运行的时候,所分配的进程标识符都不相同。 2、fork 在2.4.4版内核中,fork是第2号系统调用,其在Linux函数库中的原型是:

北邮-大三-操作系统-进程管理实验报告

实验一进程管理 1.实验目的: (1)加深对进程概念的理解,明确进程和程序的区别; (2)进一步认识并发执行的实质; (3)分析进程争用资源的现象,学习解决进程互斥的方法; (4)了解Linux系统中进程通信的基本原理。 2.实验预备内容 (1)阅读Linux的源码文件,加深对进程管理概念的理解; (2)阅读Linux的fork()源码文件,分析进程的创建过程。 3.实验内容 (1)进程的创建: 编写一段程序,使用系统调用fork() 创建两个子进程。当此程序运行时,在系统中有一个父进程和两个子进程活动。让每一个进程在屏幕上显示一个字符:父进程显示字符“a”,子进程分别显示字符“b”和“c”。试观察记录屏幕上的显示结果,并分析原因。 源代码如下: #include #include<> #include<> #include <> #include <> int main(int argc,char* argv[]) { pid_t pid1,pid2; pid1 = fork(); if(pid1<0){ fprintf(stderr,"childprocess1 failed"); exit(-1); } else if(pid1 == 0){

printf("b\n"); } else{ pid2 = fork(); if(pid2<0){ fprintf(stderr,"childprocess1 failed"); exit(-1); } else if(pid2 == 0){ printf("c\n"); } else{ printf("a\n"); sleep(2); exit(0); } } return 0; } 结果如下: 分析原因: pid=fork(); 操作系统创建一个新的进程(子进程),并且在进程表中相应为它建立一个新的表项。新进程和原有进程的可执行程序是同一个程序;上下文和数据,绝大部分就是原进程(父进程)的拷贝,但它们是两个相互独立的进程!

实验项目二进程管理

实验项目二进程管理 一、实验目的 1.理解进程的概念,掌握父、子进程创建的方法。 2.认识和了解并发执行的实质,掌握进程的并发及同步操作。 二、实验内容 1.编写一C语言程序,实现在程序运行时通过系统调用fork( ) 创建两个子进程,使父、子三进程并发执行,父亲进程执行 时屏幕显示“I am father ,”儿子进程执行时屏幕显示“I am son ”,女儿进程执行时屏幕显示“ I am daughter。” 2.多次连续反复运行这个程序,观察屏幕显示结果的顺序,直 至出现不一样的情况为止。记下这种情况,试简单分析其原 因。 3.修改程序,在父、子进程中分别使用wait() 、exit()等系统调用 “实现”其同步推进,并获取子进程的ID 号及结束状态值。 多次反复运行改进后的程序,观察并记录运行结果。 三、源程序及运行结果 源程序 1: #include #include #include #include int main() { pid_t pid; char *a; int num; printf("starting:\n");

pid = fork(); if (pid == -1) { printf("failed"); exit(1); } else if (pid == 0) { a = "I am son"; } else { a = "I am father"; pid = fork(); if (pid == -1) { printf("failed"); exit(1); } else if (pid == 0) { a = "I am daughter"; } } for (num=3;num>0;num--) { puts(a); sleep(1); } exit(0); } 运行结果:

自动控制原理实验报告73809

-150-100 -50 50 实验一 典型环节的模拟研究及阶跃响应分析 1、比例环节 可知比例环节的传递函数为一个常数: 当Kp 分别为0.5,1,2时,输入幅值为1.84的正向阶跃信号,理论上依次输出幅值为0.92,1.84,3.68的反向阶跃信号。实验中,输出信号依次为幅值为0.94,1.88,3.70的反向阶跃信号, 相对误差分别为1.8%,2.2%,0.2%. 在误差允许范围内可认为实际输出满足理论值。 2、 积分环节 积分环节传递函数为: (1)T=0.1(0.033)时,C=1μf (0.33μf ),利用MATLAB ,模拟阶跃信号输入下的输出信号如图: T=0.1 T=0.033 与实验测得波形比较可知,实际与理论值较为吻合,理论上T=0.033时的波形斜率近似为T=0.1时的三倍,实际上为8/2.6=3.08,在误差允许范围内可认为满足理论条件。 3、 惯性环节 i f i o R R U U -=TS 1 CS R 1Z Z U U i i f i 0-=-=-=15 20

惯性环节传递函数为: K = R f /R 1,T = R f C, (1) 保持K = R f /R 1 = 1不变,观测T = 0.1秒,0.01秒(既R 1 = 100K,C = 1μf , 0.1μf )时的输出波形。利用matlab 仿真得到理论波形如下: T=0.1时 t s (5%)理论值为300ms,实际测得t s =400ms 相对误差为:(400-300)/300=33.3%,读数误差较大。 K 理论值为1,实验值2.12/2.28, 相对误差为(2.28-2.12)/2.28=7%与理论值 较为接近。 T=0.01时 t s (5%)理论值为30ms,实际测得t s =40ms 相对误差为:(40-30)/30=33.3% 由于ts 较小,所以读数时误差较大。 K 理论值为1,实验值2.12/2.28, 相对误差为(2.28-2.12)/2.28=7%与理论值较为接近 (2) 保持T = R f C = 0.1s 不变,分别观测K = 1,2时的输出波形。 K=1时波形即为(1)中T0.1时波形 K=2时,利用matlab 仿真得到如下结果: t s (5%)理论值为300ms,实际测得t s =400ms 相对误差为:(400-300)/300=33.3% 读数误差较大 K 理论值为2,实验值4.30/2.28, 1 TS K )s (R )s (C +-=

相关文档
最新文档