多进程--有道笔记整理

转载 2015年11月19日 17:44:32


一,进程概念,为什么是多进程而不是多线程

进程是由操作系统创建的工作单元。值得注意的是进程和程序未必是等同的。一个程序可能由多个任务组成,而每个任务可以和一个或多个进程相关联。程序是由程序员创建的,而进程是由操作系统创建的。一个工作单元要想被称作进程,它必须要有操作系统指派给他的地址空间,必须拥有进程ID,必须拥有状态和进程表中的表项。进程和线程之间最大的区别是进程有着自己的地址空间,而线程共享创建它们的进程的地址空间。

在将C++任务映射为操作系统能够理解的执行单元时,结果证明线程更易于编程,其主要原因是线程共享相同的地址空间,使得线程间的通信和同步都要易于进程。创建或终止线程所要做的工作都要少于创建进程的相关工作,而且速度也要快于进程。

那为什么还有使用进程呢?

首先,进程有自己的地址空间,可以有效的提供安全性和隔离性,阻止流氓进程的干扰。

其次,线程所能使用的打开的文件数目受限于一个进程能拥有的打开的文件数目。这使得使用进程时能够对更多的资源进行访问。

第三,对于多用户的程序,每个用户程序还是应该更独立一些,如果一个用户进程失败,其他用户还是可以继续工作的,但如果一个用户线程失败,则有可能影响所有用户的操作。

但使用多进程时进程间通信启动时间是主要的代价。

二, 对进程的详细讨论

当执行进程时,操作系统将它指定到一个处理器上。进程在一个时间片(quantum)内执行他的指令。进程是可抢占的。分为操作系统进程和用户进程。

操作系统进程:是执行的系统代码,也被称作内核进程。执行的是系统管理的任务。

用户系统进程:是执行的用户代码。


进程控制块 PCB

进程拥有一些标识来描述他们运行的特性。内核维护数据,并提供可以允许用户访问的接口。这些信息被保存在进程控制块中(Process Control Block)。

PCB中的信息:

-    进程当前的状态和优先级

-    进程标识,父进程的标识,子进程的标识

-    指向以分配资源的指针

-    指向进程内存位置的指针

-    指向父进程和子进程的指针

-    进程所使用的处理 器

-    控制和状态寄存器

-    栈指针

在进程的地址空间内分为3个逻辑段: 代码段,数据段,栈段。

dia_5_2

进程的地址空间是虚拟的。虚拟存储使得在执行的进程中引用的地址同内存中的实际可用的地址是无关的。这使得可寻址的存储空间大小远远大于实际的内存大小。进程的虚拟地址空间的段是连续的。每个段及物理地址空间被分成页。每个页有唯一的帧号(page frame number)。虚拟页帧号(virtual page frame number) 用作进程页表中的索引。虚拟地址空间是连续的,但可以以任何顺序映射到物理页面。虽然进程的虚拟地址空间都受到保护,但进程的代码段是可以在不同进程间共享的。


dia_5_3


三,进程的创建,优先级调度,删除和进程的资源

3.1  进程的创建

要想运行任何一个程序,操作系统必须首先创建一个进程。当创建新的进程之后,在主进程表中会加入一个新的条目,创建并初始化一个新的PCB。PCB中会包含进程id,父进程id,进程的入口等。

创建进程有着不同的方法 fork()  system() posix_spawn(),这里将主要讨论posix_spawn()方法。

一个简单的posix_spawn() 的示例:


  1. #include <spawn.h>  
  2. #include <iostream.h>  
  3. #include <pthread.h>  
  4.   
  5.   
  6. int main()  
  7. {  
  8.     pid_t  nPid;  
  9.     posix_spawnattr_t X;  
  10.     posix_spawn_file_actions_t Y;  
  11.     char* argv[] = {"/bin/ps""-lf", NULL};  
  12.     char* envp[] = {"PROCESS=2"};  
  13.   
  14.     posix_spawnattr_init(&X);  
  15.     posix_spawn_file_actions_init(&Y);  
  16.   
  17.     posix_spawn(&nPid, "/bin/ps", &Y, &X, argv, envp);  
  18.   
  19.     cout << "spawned PID" << nPid << endl;  
  20.     return 0;  
  21. }  

posix_spawn() 函数的说明:

  1. int posix_spawn(pid_t *restrict pid,   
  2.                 const char *restrict path,  
  3.                 const posix_spawn_file_actions_t *file_actions,  
  4.                 const posix_spawnattr_t *restrict attrp,  
  5.                 char *const argv[restrict], char *const envp[restrict]);  
  6.   
  7.   
  8. int posix_spawnp(pid_t *restrict pid,   
  9.                  const char *restrict file,  
  10.                  const posix_spawn_file_actions_t *file_actions,  
  11.                  const posix_spawnattr_t *restrict attrp,  
  12.                  char *const argv[restrict], char * const envp[restrict]);   

file_atction: 包含了新进程将要对文件描述符执行的动作的信息的数据结构。

  1. struct posix_spawn_file_actions_t  
  2. {  
  3.     int __allocated;  
  4.     int __used;  
  5.     struct __spawn_action *actions;  
  6.     int __pad[16];  
  7. };  

attrp 参数: 包含关于新进程的调度策略,进程组,信号,标志等信息。
  1. struct posix_spawnattr_t  
  2. {  
  3.     short int __flags;  //用于指示在新进程中将要修改哪个属性  
  4.     pid_t     __pgrp;   //新进程即将加入的进程组ID  
  5.     sigset_t  __sd;     //新进程被迫默认使用的默认信号处理的信号集合  
  6.     sigset_t  __ss;     //新进程将要使用的信号掩码  
  7.     struct sched_param  __sp;  //将要赋给新进程的调度参数  
  8.     int  __policy;      //新进程将要使用的调度策略  
  9.     int  __pad[16];  
  10. };  


3.2  进程的调度

进程在运行期间,会有不同的状态

运行(Running)

就绪(runnable,  ready)

僵死(zombied)

等待(waiting, block)

停止(stopped)

进程状态的改变取决于操作系统所创造的环境。


process_status


当一个就绪队列包含多个进程时,调度器就会决定首先将那个进程指派给处理器。每个进程都会被赋予一个优先级,有相同优先级的进程被放在一个队列中。进程的优先级可能是静态的,也可能是动态的。调度器根据不同的调度策略来处理进程。在POSIX API中主要包含2中调度策略,FIFO(先进先出),RR(轮询)


FIFO 先进先出:进程是根据到达队列的时间被指派给处理器的。当正在运行的进程时间片耗尽时他会被放到队列的头部,当一个休眠进程变为可运行时,它将被放在队列的尾部。


FIFO_Digam


RR 轮询:所有的进程被同等对待,当进程时间片被耗尽后进程被放到队列的后端。


RR_diagram




3.3  进程的删除  

进程终止后其PCB将会被清除,而且它所使用的资源和地址空间将全部被释放。
父进程负责子进程的终止和释放,父进程应处于等待状态,直到它所有的子进程已经终止。当父进程提取子进程的退出码后,子进程将正常的退出系统。进程在父进程接受信号之前一直处于僵死状态。如果父进程一直不接受信号(父进程终止或退出),那么子进程将一直处于僵死状态。


进程终止函数 exit()  abort()  kill()


函数exit():
进程可以自身调用该函数来终止自己, exit()会导致调用的进程正常的终止。与进程相关的被打开的文件描述符都会被关闭。


函数abort():
会导致调用该函数的进程异常终止。


函数kill()
可导致其他的进程终止。


四,同步进程和异步进程

异步进程间是相互独立的。进程A运行到结束不需要考虑其他进程。
同步进程定义为交错进程,进程A运行一段时间后将会挂起自身,等待进程B运行结束。


fork()  fork-exec()  posix_spawn等函数创建的进程都是异步进程,父进程在成功创建子进程后并不挂起,两个进程将会各自独立的运行。
system()  创建的进程是同步进程,它会创建一个shell来执行命令或可执行文件。父进程将会被挂起,直到子进程结束,且system()调用返回。
wait() 函数:异步进程可以通过执行它将自身挂起,等到子进程结束。当子进程结束后,等待中的父进程收集子进程的退出状态,防止出现僵死进程。

五,谓词

谓词是一个返回bool值的函数的对象。谓词不仅仅是函数,它有着对象的语义。谓词为动作序列提供了声明式的解释。
使用谓词可以是用户脱离并行化的过程式算法。



#include <spawn.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>


extern char **environ;
int main(int argc, char * argv[])
{
posix_spawnattr_t attr;
posix_spawn_file_actions_t fact;
pid_t pid;
char cmd[]="ls";
char opt[]="-l";
char *args[3];
args[0]=cmd;
args[1]=opt;
args[2]=NULL;
posix_spawnattr_init(&attr);
posix_spawn_file_actions_init(&fact);
posix_spawn(&pid,"/bin/ls",&fact,&attr,args,environ);
perror("posix_spawn");
printf("pid=%d,child pid = %d\n",getpid(),pid);
int stat=0;
waitpid(pid,&stat,0);
printf("stat is %d\n",stat);
return 0;
}

一,进程概念,为什么是多进程而不是多线程

进程是由操作系统创建的工作单元。值得注意的是进程和程序未必是等同的。一个程序可能由多个任务组成,而每个任务可以和一个或多个进程相关联。程序是由程序员创建的,而进程是由操作系统创建的。一个工作单元要想被称作进程,它必须要有操作系统指派给他的地址空间,必须拥有进程ID,必须拥有状态和进程表中的表项。进程和线程之间最大的区别是进程有着自己的地址空间,而线程共享创建它们的进程的地址空间。

在将C++任务映射为操作系统能够理解的执行单元时,结果证明线程更易于编程,其主要原因是线程共享相同的地址空间,使得线程间的通信和同步都要易于进程。创建或终止线程所要做的工作都要少于创建进程的相关工作,而且速度也要快于进程。

那为什么还有使用进程呢?

首先,进程有自己的地址空间,可以有效的提供安全性和隔离性,阻止流氓进程的干扰。

其次,线程所能使用的打开的文件数目受限于一个进程能拥有的打开的文件数目。这使得使用进程时能够对更多的资源进行访问。

第三,对于多用户的程序,每个用户程序还是应该更独立一些,如果一个用户进程失败,其他用户还是可以继续工作的,但如果一个用户线程失败,则有可能影响所有用户的操作。

但使用多进程时进程间通信启动时间是主要的代价。

二, 对进程的详细讨论

当执行进程时,操作系统将它指定到一个处理器上。进程在一个时间片(quantum)内执行他的指令。进程是可抢占的。分为操作系统进程和用户进程。

操作系统进程:是执行的系统代码,也被称作内核进程。执行的是系统管理的任务。

用户系统进程:是执行的用户代码。


进程控制块 PCB

进程拥有一些标识来描述他们运行的特性。内核维护数据,并提供可以允许用户访问的接口。这些信息被保存在进程控制块中(Process Control Block)。

PCB中的信息:

-    进程当前的状态和优先级

-    进程标识,父进程的标识,子进程的标识

-    指向以分配资源的指针

-    指向进程内存位置的指针

-    指向父进程和子进程的指针

-    进程所使用的处理 器

-    控制和状态寄存器

-    栈指针

在进程的地址空间内分为3个逻辑段: 代码段,数据段,栈段。

dia_5_2

进程的地址空间是虚拟的。虚拟存储使得在执行的进程中引用的地址同内存中的实际可用的地址是无关的。这使得可寻址的存储空间大小远远大于实际的内存大小。进程的虚拟地址空间的段是连续的。每个段及物理地址空间被分成页。每个页有唯一的帧号(page frame number)。虚拟页帧号(virtual page frame number) 用作进程页表中的索引。虚拟地址空间是连续的,但可以以任何顺序映射到物理页面。虽然进程的虚拟地址空间都受到保护,但进程的代码段是可以在不同进程间共享的。


dia_5_3


三,进程的创建,优先级调度,删除和进程的资源

3.1  进程的创建

要想运行任何一个程序,操作系统必须首先创建一个进程。当创建新的进程之后,在主进程表中会加入一个新的条目,创建并初始化一个新的PCB。PCB中会包含进程id,父进程id,进程的入口等。

创建进程有着不同的方法 fork()  system() posix_spawn(),这里将主要讨论posix_spawn()方法。

一个简单的posix_spawn() 的示例:


  1. #include <spawn.h>  
  2. #include <iostream.h>  
  3. #include <pthread.h>  
  4.   
  5.   
  6. int main()  
  7. {  
  8.     pid_t  nPid;  
  9.     posix_spawnattr_t X;  
  10.     posix_spawn_file_actions_t Y;  
  11.     char* argv[] = {"/bin/ps""-lf", NULL};  
  12.     char* envp[] = {"PROCESS=2"};  
  13.   
  14.     posix_spawnattr_init(&X);  
  15.     posix_spawn_file_actions_init(&Y);  
  16.   
  17.     posix_spawn(&nPid, "/bin/ps", &Y, &X, argv, envp);  
  18.   
  19.     cout << "spawned PID" << nPid << endl;  
  20.     return 0;  
  21. }  

posix_spawn() 函数的说明:

  1. int posix_spawn(pid_t *restrict pid,   
  2.                 const char *restrict path,  
  3.                 const posix_spawn_file_actions_t *file_actions,  
  4.                 const posix_spawnattr_t *restrict attrp,  
  5.                 char *const argv[restrict], char *const envp[restrict]);  
  6.   
  7.   
  8. int posix_spawnp(pid_t *restrict pid,   
  9.                  const char *restrict file,  
  10.                  const posix_spawn_file_actions_t *file_actions,  
  11.                  const posix_spawnattr_t *restrict attrp,  
  12.                  char *const argv[restrict], char * const envp[restrict]);   

file_atction: 包含了新进程将要对文件描述符执行的动作的信息的数据结构。

  1. struct posix_spawn_file_actions_t  
  2. {  
  3.     int __allocated;  
  4.     int __used;  
  5.     struct __spawn_action *actions;  
  6.     int __pad[16];  
  7. };  

attrp 参数: 包含关于新进程的调度策略,进程组,信号,标志等信息。
  1. struct posix_spawnattr_t  
  2. {  
  3.     short int __flags;  //用于指示在新进程中将要修改哪个属性  
  4.     pid_t     __pgrp;   //新进程即将加入的进程组ID  
  5.     sigset_t  __sd;     //新进程被迫默认使用的默认信号处理的信号集合  
  6.     sigset_t  __ss;     //新进程将要使用的信号掩码  
  7.     struct sched_param  __sp;  //将要赋给新进程的调度参数  
  8.     int  __policy;      //新进程将要使用的调度策略  
  9.     int  __pad[16];  
  10. };  


3.2  进程的调度

进程在运行期间,会有不同的状态

运行(Running)

就绪(runnable,  ready)

僵死(zombied)

等待(waiting, block)

停止(stopped)

进程状态的改变取决于操作系统所创造的环境。


process_status


当一个就绪队列包含多个进程时,调度器就会决定首先将那个进程指派给处理器。每个进程都会被赋予一个优先级,有相同优先级的进程被放在一个队列中。进程的优先级可能是静态的,也可能是动态的。调度器根据不同的调度策略来处理进程。在POSIX API中主要包含2中调度策略,FIFO(先进先出),RR(轮询)


FIFO 先进先出:进程是根据到达队列的时间被指派给处理器的。当正在运行的进程时间片耗尽时他会被放到队列的头部,当一个休眠进程变为可运行时,它将被放在队列的尾部。


FIFO_Digam


RR 轮询:所有的进程被同等对待,当进程时间片被耗尽后进程被放到队列的后端。


RR_diagram




3.3  进程的删除  

进程终止后其PCB将会被清除,而且它所使用的资源和地址空间将全部被释放。
父进程负责子进程的终止和释放,父进程应处于等待状态,直到它所有的子进程已经终止。当父进程提取子进程的退出码后,子进程将正常的退出系统。进程在父进程接受信号之前一直处于僵死状态。如果父进程一直不接受信号(父进程终止或退出),那么子进程将一直处于僵死状态。


进程终止函数 exit()  abort()  kill()


函数exit():
进程可以自身调用该函数来终止自己, exit()会导致调用的进程正常的终止。与进程相关的被打开的文件描述符都会被关闭。


函数abort():
会导致调用该函数的进程异常终止。


函数kill()
可导致其他的进程终止。


四,同步进程和异步进程

异步进程间是相互独立的。进程A运行到结束不需要考虑其他进程。
同步进程定义为交错进程,进程A运行一段时间后将会挂起自身,等待进程B运行结束。


fork()  fork-exec()  posix_spawn等函数创建的进程都是异步进程,父进程在成功创建子进程后并不挂起,两个进程将会各自独立的运行。
system()  创建的进程是同步进程,它会创建一个shell来执行命令或可执行文件。父进程将会被挂起,直到子进程结束,且system()调用返回。
wait() 函数:异步进程可以通过执行它将自身挂起,等到子进程结束。当子进程结束后,等待中的父进程收集子进程的退出状态,防止出现僵死进程。

五,谓词

谓词是一个返回bool值的函数的对象。谓词不仅仅是函数,它有着对象的语义。谓词为动作序列提供了声明式的解释。
使用谓词可以是用户脱离并行化的过程式算法。



#include <spawn.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>


extern char **environ;
int main(int argc, char * argv[])
{
posix_spawnattr_t attr;
posix_spawn_file_actions_t fact;
pid_t pid;
char cmd[]="ls";
char opt[]="-l";
char *args[3];
args[0]=cmd;
args[1]=opt;
args[2]=NULL;
posix_spawnattr_init(&attr);
posix_spawn_file_actions_init(&fact);
posix_spawn(&pid,"/bin/ls",&fact,&attr,args,environ);
perror("posix_spawn");
printf("pid=%d,child pid = %d\n",getpid(),pid);
int stat=0;
waitpid(pid,&stat,0);
printf("stat is %d\n",stat);
return 0;
}
举报

相关文章推荐

Android开发笔记之获取通讯录

关注官方微博关注官方微信添加到我的笔记下载有道云笔记--------------------------------------------------------------------------...

如何去除有道云笔记广告

1. 首先我们可以看到有道云笔记界面左下角有一个影响美观的广告2. 找到这个路径,用记事本打开build.xml文件3. 查找广告关键字4. 删掉这段代码5. 保存的时候如果提醒保存不了,可以先把这个...

精选:深入理解 Docker 内部原理及网络配置

网络绝对是任何系统的核心,对于容器而言也是如此。Docker 作为目前最火的轻量级容器技术,有很多令人称道的功能,如 Docker 的镜像管理。然而,Docker的网络一直以来都比较薄弱,所以我们有必要深入了解Docker的网络知识,以满足更高的网络需求。

有道云笔记启动时出现问题

有道云笔记启动时出现问题:网络错误,点击查看帮助

有名管道+多进程最基本的并发式的通信

一.之前的有名管道实现的服务器-客户端程序只能实现你一句我一句的基本的通信。要实现最最基本的并发式通信,需要引入多个进程 真正的并发式通信不再是一个服务器针对一个客户端这种一对一的模式,而是一对多...

从印象笔记转入有道云

1 起因 起因有两点吧,第一点就是最不能忍得,就是不能在离线使用,虽然这个我已经忍受了很久;第二点就是现在强制限制2天同时登录,我看了一下我现在一共有5台设备,每次打开印象笔记的时候都要提醒我只能两台...

有道云笔记图片存档

有道云笔记图片存档

有道云笔记客户端收起左侧目录边栏方法

介绍两种有道云笔记客户端收起左侧目录栏的方法

办公技巧——免费关闭有道云笔记广告

背景 我一直在使用有道云笔记做为笔记工具,真的很好用,推荐一下 有道云笔记。 有道云笔记有很多优点,容量大,不限流量,速度快,多版本,支持Markdown等等。自行补脑。 有道云笔记可以免费使用,容量...

JavaScript应用开发实践指南

说明:以前都是在有道云笔记上记录我每天学的东西,可是它不具有共享功能,现在好多公司都需要你又自己的技术博客,所以,把云笔记上的东西搬到csdn博客了,希望能帮到其他人。 这本书挺适合作为入门书籍研读...

有道云笔记下MarkDown使用

继有道云协作率先支持markdown语法之后,令人振奋的markdown编辑功能现已在web版有道云笔记优雅上线,移动端也已支持预览! 不要着急,PC客户端也将随后支持markdown的编辑预览...
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)