【av68676164(p18-p20)】进程控制
4.2.1 进程控制的概念
进程控制的概念
- 在进程生存全期间,对其全部行为的控制
- 存在四个典型的控制行为
- 创建进程
- 阻塞进程
- 撤销进程
- 唤醒进程
 
进程创建
功能:创建一个具有制定标识(ID)的进程
参数:进程标识、优先级、进程起始地址、CPU初始状态、资源清单等。
创建进程的过程
- 创建一个空白PCB
- 获得并赋予进程标识符ID
- 为进程分配空间
- 初始化PCB
- 默认值
 
- 插入相应的进程队列
- 新进程插入就绪队列
 
进程创建的伪代码
// CPU的状态,内存,优先级
Create(Si, Mi, Pi) {
  // 分配新的PCB
  p = Get_New_PCB();
  // 分配进程的PID
  pid = Get_New_PID();
  // 设置进程的PID
  p->ID = pid;
  // CPU的状态
  p->CPU_State = Si;
  // 内存
  p->Memory = Mi;
  // 优先级
  p->Priority = Pi;
  // 进程状态
  p->Status.Type = "Ready";
  // 进程队列RL:Ready List
  p->Status.List = RL;
  ...;
  // 将进程p插入到就绪队列
  Insert(RL, p);
  // 调度程序
  Scheduler();
}
进程撤销
功能
- 撤销一个指定的进程
- 收回进程所占有的资源,撤销该进程的PCB
进程撤销的时机/事件
- 正常结束
- 异常结束
- 外界干预
进程撤销的实现
- 在PCB队列中检索出该PCB
- 获取该进程的状态
- 若该进程处在运行态,立即终止该进程
- 【递归】检查是否有子进程,先撤销子进程
 
- 释放进程占有的资源
- 将进程从PCB队列移除
进程阻塞
功能:停止进程的执行,变为阻塞
阻塞的时机/事件
- 请求系统服务
- 由于某种原因,OS不能立即满足进程的要求
 
- 启动某种操作
- 进程启动某操作,阻塞等待该操作完成
 
- 新数据尚未到达
- A进程要获得B进程的中间结果,A进程等待
 
- 无新工作可做
- 进程完成任务后,自我阻塞,等待新任务到达
 
参数
- 阻塞原因
- 不同原因构建有不同的阻塞队列
进程阻塞的实现
- 停止运行
- 将PCB“运行态“改”阻塞态“
- 插入相应原因的阻塞队列
- 转调度程序
进程唤醒
功能:唤醒处于阻塞队列的某一个进程
引起唤醒的时机/事件
- 系统服务由不满足到满足
- I/O完成
- 新数据到达
- 进程提出新请求(服务)
参数:被唤醒进程的标识
原语
原语是由若干指令构成具有特定功能的函数
具有原子性,其操作不可分割
进程创建的原语:(同上伪代码),所以进程创建是一个原语
进程控制原语
- 创建原语
- 撤销原语
- 阻塞原语
- 唤醒原语
4.2.2 Windows进程控制
Windows启动EXE程序创建进程的方法

思考:Windows如何通过编程启动exe程序创建进程?
#include <iostream>
#include <Windows.h>
using namespace std;
const char* NOTEPAD_PATH = "C:/Windows/System32/notepad.exe";
int main() {
    system(NOTEPAD_PATH);
    WinExec(NOTEPAD_PATH, SW_SHOWNORMAL);
    ShellExecute(NULL, (LPCWSTR)L"open", (LPCWSTR)L"notepad.exe", NULL, NULL, SW_SHOWMAXIMIZED);
    PROCESS_INFORMATION pi;
    STARTUPINFO si;
    BOOL ret = CreateProcess(NULL, (LPWSTR)L"notepad.exe", NULL, NULL, FALSE, NULL, NULL, NULL, &si, &pi);
    return 0;
}
以下三个函数都是对CreateProcess的封装
_DCRTIMP int __cdecl system(
    _In_opt_z_ char const* _Command
);
WinExec(
    _In_ LPCSTR lpCmdLine,
    _In_ UINT uCmdShow
    );
SHSTDAPI_(HINSTANCE) ShellExecuteW(_In_opt_ HWND hwnd, _In_opt_ LPCWSTR lpOperation, _In_ LPCWSTR lpFile, _In_opt_ LPCWSTR lpParameters,
    _In_opt_ LPCWSTR lpDirectory, _In_ INT nShowCmd);
CreateProcess()创建进程
CreateProcessW(
    _In_opt_ LPCWSTR lpApplicationName,		// 可执行程序名
    _Inout_opt_ LPWSTR lpCommandLine,		// [可执行程序名]程序参数
    _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes,
    _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
    _In_ BOOL bInheritHandles,
    _In_ DWORD dwCreationFlags,				// 创建标志(优先级……)
    _In_opt_ LPVOID lpEnvironment,
    _In_opt_ LPCWSTR lpCurrentDirectory,
    _In_ LPSTARTUPINFOW lpStartupInfo,		// 启动信息(进程启动的状态,是否显示窗口……)
    _Out_ LPPROCESS_INFORMATION lpProcessInformation	//
    );
创建新进程
- 创建进程内核对象,创建虚拟地址空间
- 装载EXE和/或DLL的代码和数据到地址空间中
- 创建主线程和线程内核对象
- 启动主线程,进入主函数(main)
STARTUPINFO si = { sizeof(si) };
PROCESS_INFORMATION pi;
::CreateProcess((LPWSTR)L"C:/Windows/System32/notepad.exe", (LPWSTR)L"C:/readme.txt", NULL, NULL, FALSE, NULL, NULL, NULL, &si, &pi);
结束进程
- ExitProcess
WINBASEAPI DECLSPEC _NORETURN VOID WINAPI ExitProcess(UINT uExitCode);
- TerminateProcess
WINBASEAPI WINBOOL WINAPI TerminateProcess(HANDLE hProcess, UINT uExitCode);
4.2.3 Linux进程控制
创建进程
pid_t fork(void);
例子:
pid_t pid = fork();
#include <unistd.h>
#include <cstdio>
int main() {
    pid_t pid;
    int count = 0;
    pid = fork();
    printf("This is first time, pid = %d\n", pid);
    printf("This is second time, pid = %d\n", pid);
    count++;
    printf("count = %d\n", count);
    if (pid > 0) {
        printf("This is the parent process,the child has the pid:%d\n", pid);
    } else if (!pid) {
        printf("This is the child Process.\n");
    } else {
        printf("fork failed.\n");
    }
    printf("This is third time, pid = %d\n", pid);
    printf("This is fouth time, pid = %d\n", pid);
    return 0;
}
运行结果
This is first time, pid = 1174
This is second time, pid = 1174
count = 1
This is the parent process,the child has the pid:1174
This is third time, pid = 1174
This is fouth time, pid = 1174
This is first time, pid = 0
This is second time, pid = 0
count = 1
This is the child Process.
This is third time, pid = 0
This is fouth time, pid = 0
Process finished with exit code 0
- 新进程是当前进程的子进程
- 父进程和子进程
- 父进程:fork()的调用者
- 子进程:新建的进程
 
- 子进程是父进程的复制
- 子进程和父进程有相同代码、数据、堆栈
- 子进程的行为和父进程的行为一模一样
- 子进程和父进程唯一不同的是PID
 
- 子进程和父进程可以并发运行
思考:下面程序在屏幕上将输出什么内容
int main() {
  fork();	// 子进程是父进程的复制,父子进程都输出了
  printf("Hello World!\n");
  return 0;
}
输出结果:
父子进程在并发运行

思考:下面程序运行结果是什么
#include <unistd.h>
#include <cstdio>
int main() {
    pid_t pid;
    pid = fork();
    if (pid == 0) {
        printf("Hello World!\n");
    } else if (pid > 0) {
        printf("How are you!\n");
    }
    return 0;
}
输出结果:

父子进程谁先输出不确定
fork返回值pid**
Upon successful completion, fork() returns a value of 0 to the child process and returns the process ID of the child process to the parent process. Otherwise, a value of -1 is returned to the parent process, no child process is created, and the global variable errno is set to indicate the error.
- 在子进程中,pid=0
- 在父进程中,pid>0(子进程ID)
- 出错:pid=-1
修改:增加输出pid,运行结果如下
PID of current Process is : 1609
How are you!	// 父进程
PID of current Process is : 0
Hello World!	// 子进程
Process finished with exit code 0
fork函数的实现
在文件/kernel/fork.c中
int do_fork(unsigned long clone_flags,
             unsigned long stack_start,
             unsigned long stack_size,
             int __user *parent_tidptr,
             int __user *child_tidptr) {
  struct task_struct *p;
  // 分配无力页面存放task_struct结构和内核空间的堆栈
  p = alloc_task_struct();
  // 把当前进程的task_struct结构中所有内容拷贝到新进程中
  *p = *current;
  // 判断用户进程数量是否超过了最大限制,否则不许fork
  if ((&p->user->processes) >= p->rlim[RLIMIT_NPROC].rlim_cur) {
    goto bad_fork_free;
  }
  // 子进程状态设TASK_UNINTERRUPTIBLE
  p->state = TASK_UNINTERRUPTIBLE;
  // 拷贝进程的所有信息
  copy_files(clone_flags, p);
  copy_fs(clone_flags, p);
  copy_mm(clone_flags, p);
  // 进程创建后与父进程链接起来形成一个进程组
  list_add(&p->thread_group, ¤t->thread_group);
  // 唤醒进程,将其挂入可执行队列等待被调度
  wake_up_process(p);s
}
- 为子进程分配task_struct空间
- 初始化子进程task_struct
- 复制父进程的file,fs,sighand,mm等信息
- ……
进程执行与父进程不同的功能
exec函数簇(包括若干函数)
- 功能 - 装入一个指定的可执行程序运行
- 使子进程具有和父进程完全不同的新功能
 
- 步骤 - 根据文件名找到相应的可执行程序
- 将可执行程序的内容填入子进程的地址空间
- 进入新进程执行且不再返回
 
// unistd.h
int	 execl(const char * __path, const char * __arg0, ...) __WATCHOS_PROHIBITED __TVOS_PROHIBITED;
int	 execle(const char * __path, const char * __arg0, ...) __WATCHOS_PROHIBITED __TVOS_PROHIBITED;
int	 execlp(const char * __file, const char * __arg0, ...) __WATCHOS_PROHIBITED __TVOS_PROHIBITED;
int	 execv(const char * __path, char * const * __argv) __WATCHOS_PROHIBITED __TVOS_PROHIBITED;
int	 execve(const char * __file, char * const * __argv, char * const * __envp) __WATCHOS_PROHIBITED __TVOS_PROHIBITED;
int	 execvp(const char * __file, char * const * __argv) __WATCHOS_PROHIBITED __TVOS_PROHIBITED;
【av68676164(p18-p20)】进程控制的更多相关文章
- 【linux草鞋应用编程系列】_2_ 环境变量和进程控制
		一. 环境变量 应用程序在执行的时候,可能需要获取系统的环境变量,从而执行一些相应的操作. 在linux中有两种方法获取环境变量,分述如下. 1.通过main函数的参数获取环境变量 ... 
- 【Linux程序设计】之进程控制&守护进程
		这个系列的博客贴的都是我大二的时候学习Linux系统高级编程时的一些实验程序,都挺简单的. 实验题目:Linux环境下的进程控制 实验目的:熟悉并掌握Linux环境下进程的相关函数的应用:守护进程的概 ... 
- linux进程及进程控制
		Linux进程控制 程序是一组可执行的静态指令集,而进程(process)是一个执行中的程序实例.利用分时技术,在Linux操作系统上同时可以运行多个进程.分时技术的基本原理是把CPU的运行时间划 ... 
- (六) 一起学 Unix 环境高级编程 (APUE) 之 进程控制
		. . . . . 目录 (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO (三) 一起学 Unix 环境高级编 ... 
- UNIX环境高级编程笔记之进程控制
		本章重点介绍了进程控制的几个函数:fork.exec族._exit.wait和waitpid等,主要需要掌握的是父进程和子进程之间的运行机制,怎么处理进程的正常和异常终止.以及怎么让进程执行不同的程序 ... 
- Linux进程控制(二)
		1. 进程的创建 Linux下有四类创建子进程的函数:system(),fork(),exec*(),popen() 1.1. system函数 原型: #include <stdlib.h&g ... 
- 进程控制之exec函数
		用fork函数创建子进程后,子进程往往要调用一种exec函数以执行另一个程序.当进程调用一种exec函数时,该进程执行的程序完全替换为新程序,而新程序则从其main函数开始执行.因为调用exec并不创 ... 
- Linux C 程序 进程控制(17)
		进程控制 1.进程概述现代操作系统的特点在于程序的并行执行.Linux是一个多用户多任务的操作系统.ps .pstree 查看进程进程除了进程id外还有一些其他标识信息,可以通过相应的函数获得.// ... 
- linux 命令及进程控制
		main.c main.o/main.obj main/main.exe 编译 连接 程序运行; 两步: gcc/g++ -c mai ... 
随机推荐
- day2:Number,tuple,str,list,set,dict
			# ### Number ( int float bool complex) 1.int 整型 (正整数 0 负整数)intvar = 1print(intvar) # type 获取值的类型res ... 
- Python Ethical Hacking - WEB PENETRATION TESTING(1)
			WHAT IS A WEBSITE Computer with OS and some servers. Apache, MySQL ...etc. Cotains web application. ... 
- xenomai内核解析之信号signal(二)---xenomai信号处理机制
			xenomai信号 上篇文章讲了linux的信号在内核的发送与处理流程,现在加入了cobalt核,Cobalt内核为xenomai线程提供了信号机制.下面一一解析xenomai内核的信号处理机制. 1 ... 
- 10种常见OOM分析——手把手教你写bug
			点赞+收藏 就学会系列,文章收录在 GitHub JavaKeeper ,N线互联网开发必备技能兵器谱,笔记自取 在<Java虚拟机规范>的规定里,除了程序计数器外,虚拟机内存的其他几个运 ... 
- java 将整型数组转化为字符串
			java arrays 和arrayList 的区别 package com.vc; import java.util.Arrays; public class Demo05 { public sta ... 
- Dcoker  docker: Error starting userland proxy: Bind for 0.0.0.0:80: unexpected error (Failure EADDRINUSE).
			https://stackoverflow.com/questions/46533482/error-starting-userland-proxy-bind-for-0-0-0-080-unexpe ... 
- 并发系列(二)——FutureTask类源码简析
			背景 本文基于JDK 11,主要介绍FutureTask类中的run().get()和cancel() 方法,没有过多解析相应interface中的注释,但阅读源码时建议先阅读注释,明白方法的主要的功 ... 
- 常见的HTTP返回状态值
			200 (成功) 服务器已成功处理了请求. 通常,这表示服务器提供了请求的网页. 301 (永久移动) 请求的网页已永久移动到新位置. 服务器返回此响应(对 GET 或 HEAD 请求的响应)时,会自 ... 
- PHP设计模式之----观察者模式
			一.概述 1.观察者模式(Observer),当一个对象的状态发生改变时,依赖他的对象会全部收到通知,并自动更新. 2.场景:一个事件发生后,要执行一连串更新操作.传统的编程方式,就是在事件的代码之后 ... 
- 如何使用Excel管理项目?
			1.什么是复杂问题? 复杂问题需要很多道工序,涉及到与多个人进行沟通,人的注意力没法持续关注,导致很容易忘掉很多重要步骤.像这种问题就要用到项目管理工具,在重要的节点上,来检查自己是否遗漏了重要的环节 ... 
