4.2.1 进程控制的概念

进程控制的概念

  • 在进程生存全期间,对其全部行为的控制
  • 存在四个典型的控制行为
    • 创建进程
    • 阻塞进程
    • 撤销进程
    • 唤醒进程

进程创建

功能:创建一个具有制定标识(ID)的进程

参数:进程标识、优先级、进程起始地址、CPU初始状态、资源清单等。

创建进程的过程

  • 创建一个空白PCB
  • 获得并赋予进程标识符ID
  • 为进程分配空间
  • 初始化PCB
    • 默认值
  • 插入相应的进程队列
    • 新进程插入就绪队列

进程创建的伪代码

  1. // CPU的状态,内存,优先级
  2. Create(Si, Mi, Pi) {
  3. // 分配新的PCB
  4. p = Get_New_PCB();
  5. // 分配进程的PID
  6. pid = Get_New_PID();
  7. // 设置进程的PID
  8. p->ID = pid;
  9. // CPU的状态
  10. p->CPU_State = Si;
  11. // 内存
  12. p->Memory = Mi;
  13. // 优先级
  14. p->Priority = Pi;
  15. // 进程状态
  16. p->Status.Type = "Ready";
  17. // 进程队列RL:Ready List
  18. p->Status.List = RL;
  19. ...;
  20. // 将进程p插入到就绪队列
  21. Insert(RL, p);
  22. // 调度程序
  23. Scheduler();
  24. }

进程撤销

功能

  • 撤销一个指定的进程
  • 收回进程所占有的资源,撤销该进程的PCB

进程撤销的时机/事件

  • 正常结束
  • 异常结束
  • 外界干预

进程撤销的实现

  • 在PCB队列中检索出该PCB
  • 获取该进程的状态
  • 若该进程处在运行态,立即终止该进程
    • 【递归】检查是否有子进程,先撤销子进程
  • 释放进程占有的资源
  • 将进程从PCB队列移除

进程阻塞

功能:停止进程的执行,变为阻塞

阻塞的时机/事件

  • 请求系统服务

    • 由于某种原因,OS不能立即满足进程的要求
  • 启动某种操作
    • 进程启动某操作,阻塞等待该操作完成
  • 新数据尚未到达
    • A进程要获得B进程的中间结果,A进程等待
  • 无新工作可做
    • 进程完成任务后,自我阻塞,等待新任务到达

参数

  • 阻塞原因
  • 不同原因构建有不同的阻塞队列

进程阻塞的实现

  • 停止运行
  • 将PCB“运行态“改”阻塞态“
  • 插入相应原因的阻塞队列
  • 转调度程序

进程唤醒

功能:唤醒处于阻塞队列的某一个进程

引起唤醒的时机/事件

  • 系统服务由不满足到满足
  • I/O完成
  • 新数据到达
  • 进程提出新请求(服务)

参数:被唤醒进程的标识

原语

原语是由若干指令构成具有特定功能的函数

具有原子性,其操作不可分割

进程创建的原语:(同上伪代码),所以进程创建是一个原语

进程控制原语

  • 创建原语
  • 撤销原语
  • 阻塞原语
  • 唤醒原语

4.2.2 Windows进程控制

Windows启动EXE程序创建进程的方法

思考:Windows如何通过编程启动exe程序创建进程?

  1. #include <iostream>
  2. #include <Windows.h>
  3. using namespace std;
  4. const char* NOTEPAD_PATH = "C:/Windows/System32/notepad.exe";
  5. int main() {
  6. system(NOTEPAD_PATH);
  7. WinExec(NOTEPAD_PATH, SW_SHOWNORMAL);
  8. ShellExecute(NULL, (LPCWSTR)L"open", (LPCWSTR)L"notepad.exe", NULL, NULL, SW_SHOWMAXIMIZED);
  9. PROCESS_INFORMATION pi;
  10. STARTUPINFO si;
  11. BOOL ret = CreateProcess(NULL, (LPWSTR)L"notepad.exe", NULL, NULL, FALSE, NULL, NULL, NULL, &si, &pi);
  12. return 0;
  13. }

以下三个函数都是对CreateProcess的封装

  1. _DCRTIMP int __cdecl system(
  2. _In_opt_z_ char const* _Command
  3. );
  4. WinExec(
  5. _In_ LPCSTR lpCmdLine,
  6. _In_ UINT uCmdShow
  7. );
  8. SHSTDAPI_(HINSTANCE) ShellExecuteW(_In_opt_ HWND hwnd, _In_opt_ LPCWSTR lpOperation, _In_ LPCWSTR lpFile, _In_opt_ LPCWSTR lpParameters,
  9. _In_opt_ LPCWSTR lpDirectory, _In_ INT nShowCmd);

CreateProcess()创建进程

  1. CreateProcessW(
  2. _In_opt_ LPCWSTR lpApplicationName, // 可执行程序名
  3. _Inout_opt_ LPWSTR lpCommandLine, // [可执行程序名]程序参数
  4. _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes,
  5. _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
  6. _In_ BOOL bInheritHandles,
  7. _In_ DWORD dwCreationFlags, // 创建标志(优先级……)
  8. _In_opt_ LPVOID lpEnvironment,
  9. _In_opt_ LPCWSTR lpCurrentDirectory,
  10. _In_ LPSTARTUPINFOW lpStartupInfo, // 启动信息(进程启动的状态,是否显示窗口……)
  11. _Out_ LPPROCESS_INFORMATION lpProcessInformation //
  12. );

创建新进程

  • 创建进程内核对象,创建虚拟地址空间
  • 装载EXE和/或DLL的代码和数据到地址空间中
  • 创建主线程和线程内核对象
  • 启动主线程,进入主函数(main)
  1. STARTUPINFO si = { sizeof(si) };
  2. PROCESS_INFORMATION pi;
  3. ::CreateProcess((LPWSTR)L"C:/Windows/System32/notepad.exe", (LPWSTR)L"C:/readme.txt", NULL, NULL, FALSE, NULL, NULL, NULL, &si, &pi);

结束进程

  • ExitProcess
  1. WINBASEAPI DECLSPEC _NORETURN VOID WINAPI ExitProcess(UINT uExitCode);
  • TerminateProcess
  1. WINBASEAPI WINBOOL WINAPI TerminateProcess(HANDLE hProcess, UINT uExitCode);

4.2.3 Linux进程控制

创建进程

  1. pid_t fork(void);

例子:

  1. pid_t pid = fork();
  1. #include <unistd.h>
  2. #include <cstdio>
  3. int main() {
  4. pid_t pid;
  5. int count = 0;
  6. pid = fork();
  7. printf("This is first time, pid = %d\n", pid);
  8. printf("This is second time, pid = %d\n", pid);
  9. count++;
  10. printf("count = %d\n", count);
  11. if (pid > 0) {
  12. printf("This is the parent process,the child has the pid:%d\n", pid);
  13. } else if (!pid) {
  14. printf("This is the child Process.\n");
  15. } else {
  16. printf("fork failed.\n");
  17. }
  18. printf("This is third time, pid = %d\n", pid);
  19. printf("This is fouth time, pid = %d\n", pid);
  20. return 0;
  21. }

运行结果

  1. This is first time, pid = 1174
  2. This is second time, pid = 1174
  3. count = 1
  4. This is the parent process,the child has the pid:1174
  5. This is third time, pid = 1174
  6. This is fouth time, pid = 1174
  7. This is first time, pid = 0
  8. This is second time, pid = 0
  9. count = 1
  10. This is the child Process.
  11. This is third time, pid = 0
  12. This is fouth time, pid = 0
  13. Process finished with exit code 0
  • 新进程是当前进程的子进程
  • 父进程和子进程
    • 父进程:fork()的调用者
    • 子进程:新建的进程
  • 子进程是父进程的复制
    • 子进程和父进程有相同代码、数据、堆栈
    • 子进程的行为和父进程的行为一模一样
    • 子进程和父进程唯一不同的是PID
  • 子进程和父进程可以并发运行

思考:下面程序在屏幕上将输出什么内容

  1. int main() {
  2. fork(); // 子进程是父进程的复制,父子进程都输出了
  3. printf("Hello World!\n");
  4. return 0;
  5. }

输出结果:

父子进程在并发运行

思考:下面程序运行结果是什么

  1. #include <unistd.h>
  2. #include <cstdio>
  3. int main() {
  4. pid_t pid;
  5. pid = fork();
  6. if (pid == 0) {
  7. printf("Hello World!\n");
  8. } else if (pid > 0) {
  9. printf("How are you!\n");
  10. }
  11. return 0;
  12. }

输出结果:

父子进程谁先输出不确定

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,运行结果如下

  1. PID of current Process is : 1609
  2. How are you! // 父进程
  3. PID of current Process is : 0
  4. Hello World! // 子进程
  5. Process finished with exit code 0

fork函数的实现

在文件/kernel/fork.c中

  1. int do_fork(unsigned long clone_flags,
  2. unsigned long stack_start,
  3. unsigned long stack_size,
  4. int __user *parent_tidptr,
  5. int __user *child_tidptr) {
  6. struct task_struct *p;
  7. // 分配无力页面存放task_struct结构和内核空间的堆栈
  8. p = alloc_task_struct();
  9. // 把当前进程的task_struct结构中所有内容拷贝到新进程中
  10. *p = *current;
  11. // 判断用户进程数量是否超过了最大限制,否则不许fork
  12. if ((&p->user->processes) >= p->rlim[RLIMIT_NPROC].rlim_cur) {
  13. goto bad_fork_free;
  14. }
  15. // 子进程状态设TASK_UNINTERRUPTIBLE
  16. p->state = TASK_UNINTERRUPTIBLE;
  17. // 拷贝进程的所有信息
  18. copy_files(clone_flags, p);
  19. copy_fs(clone_flags, p);
  20. copy_mm(clone_flags, p);
  21. // 进程创建后与父进程链接起来形成一个进程组
  22. list_add(&p->thread_group, &current->thread_group);
  23. // 唤醒进程,将其挂入可执行队列等待被调度
  24. wake_up_process(p);s
  25. }
  • 为子进程分配task_struct空间
  • 初始化子进程task_struct
  • 复制父进程的file,fs,sighand,mm等信息
  • ……

进程执行与父进程不同的功能

exec函数簇(包括若干函数)

  • 功能

    • 装入一个指定的可执行程序运行
    • 使子进程具有和父进程完全不同的新功能
  • 步骤

    1. 根据文件名找到相应的可执行程序
    2. 将可执行程序的内容填入子进程的地址空间
    3. 进入新进程执行且不再返回
  1. // unistd.h
  2. int execl(const char * __path, const char * __arg0, ...) __WATCHOS_PROHIBITED __TVOS_PROHIBITED;
  3. int execle(const char * __path, const char * __arg0, ...) __WATCHOS_PROHIBITED __TVOS_PROHIBITED;
  4. int execlp(const char * __file, const char * __arg0, ...) __WATCHOS_PROHIBITED __TVOS_PROHIBITED;
  5. int execv(const char * __path, char * const * __argv) __WATCHOS_PROHIBITED __TVOS_PROHIBITED;
  6. int execve(const char * __file, char * const * __argv, char * const * __envp) __WATCHOS_PROHIBITED __TVOS_PROHIBITED;
  7. int execvp(const char * __file, char * const * __argv) __WATCHOS_PROHIBITED __TVOS_PROHIBITED;

【av68676164(p18-p20)】进程控制的更多相关文章

  1. 【linux草鞋应用编程系列】_2_ 环境变量和进程控制

    一. 环境变量     应用程序在执行的时候,可能需要获取系统的环境变量,从而执行一些相应的操作.     在linux中有两种方法获取环境变量,分述如下.   1.通过main函数的参数获取环境变量 ...

  2. 【Linux程序设计】之进程控制&守护进程

    这个系列的博客贴的都是我大二的时候学习Linux系统高级编程时的一些实验程序,都挺简单的. 实验题目:Linux环境下的进程控制 实验目的:熟悉并掌握Linux环境下进程的相关函数的应用:守护进程的概 ...

  3. linux进程及进程控制

    Linux进程控制   程序是一组可执行的静态指令集,而进程(process)是一个执行中的程序实例.利用分时技术,在Linux操作系统上同时可以运行多个进程.分时技术的基本原理是把CPU的运行时间划 ...

  4. (六) 一起学 Unix 环境高级编程 (APUE) 之 进程控制

    . . . . . 目录 (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO (三) 一起学 Unix 环境高级编 ...

  5. UNIX环境高级编程笔记之进程控制

    本章重点介绍了进程控制的几个函数:fork.exec族._exit.wait和waitpid等,主要需要掌握的是父进程和子进程之间的运行机制,怎么处理进程的正常和异常终止.以及怎么让进程执行不同的程序 ...

  6. Linux进程控制(二)

    1. 进程的创建 Linux下有四类创建子进程的函数:system(),fork(),exec*(),popen() 1.1. system函数 原型: #include <stdlib.h&g ...

  7. 进程控制之exec函数

    用fork函数创建子进程后,子进程往往要调用一种exec函数以执行另一个程序.当进程调用一种exec函数时,该进程执行的程序完全替换为新程序,而新程序则从其main函数开始执行.因为调用exec并不创 ...

  8. Linux C 程序 进程控制(17)

    进程控制 1.进程概述现代操作系统的特点在于程序的并行执行.Linux是一个多用户多任务的操作系统.ps .pstree 查看进程进程除了进程id外还有一些其他标识信息,可以通过相应的函数获得.// ...

  9. linux 命令及进程控制

    main.c  main.o/main.obj  main/main.exe          编译                连接 程序运行;      两步: gcc/g++  -c  mai ...

随机推荐

  1. java 面向对象(七):类结构 方法(四)递归方法

    1.定义:递归方法:一个方法体内调用它自身.2.如何理解递归方法?> 方法递归包含了一种隐式的循环,它会重复执行某段代码,但这种重复执行无须循环控制.> 递归一定要向已知方向递归,否则这种 ...

  2. wiremock技术入门

    mock用于制作测试桩,是非常好用的自动化测试mock工具  一.下载 进入官网的下载地址: http://wiremock.org/docs/running-standalone/

  3. .net core微服务——gRPC(下)

    序 上一篇博客把grpc的概念说了个大概,介绍了proto的数据类型,基本语法,也写了个小demo,是不是没那么难? 今天要从理论到实际,写两个微服务,并利用grpc完成两者之间的通信.只是作为dem ...

  4. Mysql---搭建简单集群,实现主从复制,读写分离

    参考博客:https://blog.csdn.net/xlgen157387/article/details/51331244 A. 准备:多台服务器,且都可以互相随意访问root用户,都可以随意进行 ...

  5. react实战 : react 与 canvas

    有一个需求是这样的. 一个组件里若干个区块.区块数量不定. 区块里面是一个正六边形组件,而这个用 SVG 和 canvas 都可以.我选择 canvas. 所以就变成了在 react 中使用 canv ...

  6. 面试题千变万化,为什么总是会问MySQL?

    前言 当你简历上写了 熟悉mysql关系型数据库时,那肯定免不了面试官对于myql索引.事务,慢查询等等的考察 那么到底什么是索引,索引的数据类型有哪些,它们的优缺点以及如何去排查那些慢SQL语句等, ...

  7. Monster Audio 使用教程 (八) Vst3 使用侧链功能

    Monster Audio对 Vst3 插件支持侧链功能,例如,我们插入一个Waves C1 comp Stereo 效果器 然后在侧链处,就可以选择任意一个音轨的信号,作为侧链信号源. 注意,只有v ...

  8. [Jarvisos]Tell me something

    0x01 拿到题目首先检查一下是32位还是64位 64位程序,未开启canary栈保护 运行一下程序,检查一下程序的运行逻辑 提示->输入->输出 0x02 ida反编译一下,看一下代码逻 ...

  9. ~~并发编程(十一):GIL全局解释锁~~

    进击のpython ***** 并发编程--GIL全局解释锁 这小节就是有些"大神"批判python语言不完美之处的开始 这一节我们要了解一下Cpython的GIL解释器锁的工作机 ...

  10. 扫描PDF417崩溃的原因找到:手机摄像头分辨率低

    换孩子姥姥华为手机解决了. 能扫pdf417码了