一、进程

    并发的本质: cpu切换进程+保存状态

    一个程序执行了多次,就启动了多个进程

    进程与进程之间的内存空间是隔离开的

二、在一个进程中开启子进程    

新进程的创建都是由一个已经存在的进程执行了一个用于创建进程的系统调用而创建的:

  1. 在UNIX中该系统调用是:fork,fork会创建一个与父进程一模一样的副本,二者有相同的存储映像、同样的环境字符串和同样的打开文件(在shell解释器进程中,执行一            个命令就会创建一个子进程)

  2. 在windows中该系统调用是:CreateProcess,CreateProcess既处理进程的创建,也负责把正确的程序装入新进程。

 

  关于创建的子进程,UNIX和windows

  1.相同的是:进程创建后,父进程和子进程有各自不同的地址空间(多道技术要求物理层面实现进程之间内存的隔离),任何一个进程的在其地址空间中的修改都不会影响到另外一个进程。

  2.不同的是:在UNIX中,子进程的初始地址空间是父进程的一个副本,提示:子进程和父进程是可以有只读的共享内存区的。但是对于windows系统来说,从一开始父进程与子进程的地址空间就是不同的。

    在一个进程中开启一个子系统:

      windons系统中:开启子进程要先将父进程的数据全部拷贝过来,并加上自己的特性

        Linux 系统中: 开启子进程只将父进程的数据全部拷贝过来,并不会加上自己的特性

   开启子进程的两种方式:        

  1. #第一种
      
    from multiprocessing import Process
  2. import time
  3.  
  4. def task(name):
  5. print('%s is running' %name)
  6. time.sleep(3)
  7. print('%s is done' %name)
  8.  
  9. if __name__ == '__main__':

  10. # 在windows系统之上,开启子进程的操作一定要放到这下面
  11. # Process(target=task,kwargs={'name':'egon'})

  12. p=Process(target=task,args=('egon',))

  13. p.start() # 向操作系统发送请求,操作系统会申请内存空间,然后把父进程的数据拷贝给子进程,作为子进程的初始状态
  14. print('======主')
  15.  
  16. #第二种
  17.  
  18. from multiprocessing import Process
  19. import time
  20.  
  21. class MyProcess(Process):
  22. def __init__(self,name):
  23. super(MyProcess,self).__init__()
  24. self.name=name
  25.  
  26. def run(self):
  27. print('%s is running' %self.name)
  28. time.sleep(3)
  29. print('%s is done' %self.name)
  30.  
  31. if __name__ == '__main__':
  32. p=MyProcess('egon')
  33. p.start() #开启子进程 start 调用的是run()方法
  34. print('主')

三、 Process类的介绍

   创建进程的类:   

  1. Process([group [, target [, name [, args [, kwargs]]]]]),由该类实例化得到的对象,表示一个子进程中的任务(尚未启动)
  2.  
  3. 强调:
  4. 1. 需要使用关键字的方式来指定参数
  5. 2. args指定的为传给target函数的位置参数,是一个元组形式,必须有逗号

  

  参数介绍:

  1. group参数未使用,值始终为None
  2.  
  3. target表示调用对象,即子进程要执行的任务
  4.  
  5. args表示调用对象的位置参数元组,args=(1,2,'egon',)
  6.  
  7. kwargs表示调用对象的字典,kwargs={'name':'egon','age':18}
  8.  
  9. name为子进程的名称

方法介绍:

  1. p.start():启动进程,并调用该子进程中的p.run()
  2. p.run():进程启动时运行的方法,正是它去调用target指定的函数,我们自定义类的类中一定要实现该方法
  3.  
  4. p.terminate():强制终止进程p,不会进行任何清理操作,如果p创建了子进程,该子进程就成了僵尸进程,使用该方法需要特别小心这种情况。如果p还保存了一个锁那么也将不会被释放,进而导致死锁
  5. p.is_alive():如果p仍然运行,返回True
  6.  
  7. p.join([timeout]):主线程等待p终止(强调:是主线程处于等的状态,而p是处于运行的状态)。timeout是可选的超时时间,需要强调的是,p.join只能joinstart开启的进程,而不能joinrun开启的进程

属性介绍: 

  1. p.daemon:默认值为False,如果设为True,代表p为后台运行的守护进程,当p的父进程终止时,p也随之终止,并且设定为True后,p不能创建自己的新进程,必须在p.start()之前设置
  2.  
  3. p.name:进程的名称
  4.  
  5. p.pid:进程的pid
  6.  
  7. p.exitcode:进程在运行时为None、如果为–N,表示被信号N结束(了解即可)
  8.  
  9. p.authkey:进程的身份验证键,默认是由os.urandom()随机生成的32字符的字符串。这个键的用途是为涉及网络连接的底层进程间通信提供安全性,这类连接只有在具有相同的身份验证键时才能成功(了解即可)

四、僵尸进程和孤儿进程    

  1. 参考博客:http://www.cnblogs.com/Anker/p/3271773.html
  2.  
  3. 一:僵尸进程(有害)
  4.   僵尸进程:一个进程使用fork创建子进程,如果子进程退出,而父进程并没有调用waitwaitpid获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中。这种进程称之为僵死进程。详解如下
  5.  
  6. 我们知道在unix/linux中,正常情况下子进程是通过父进程创建的,子进程在创建新的进程。子进程的结束和父进程的运行是一个异步过程,即父进程永远无法预测子进程到底什么时候结束,如果子进程一结束就立刻回收其全部资源,那么在父进程内将无法获取子进程的状态信息。
  7.  
  8. 因此,UNⅨ提供了一种机制可以保证父进程可以在任意时刻获取子进程结束时的状态信息:
  9. 1、在每个进程退出的时候,内核释放该进程所有的资源,包括打开的文件,占用的内存等。但是仍然为其保留一定的信息(包括进程号the process ID,退出状态the termination status of the process,运行时间the amount of CPU time taken by the process等)
  10. 2、直到父进程通过wait / waitpid来取时才释放. 但这样就导致了问题,如果进程不调用wait / waitpid的话,那么保留的那段信息就不会释放,其进程号就会一直被占用,但是系统所能使用的进程号是有限的,如果大量的产生僵死进程,将因为没有可用的进程号而导致系统不能产生新的进程. 此即为僵尸进程的危害,应当避免。
  11.  
  12.   任何一个子进程(init除外)在exit()之后,并非马上就消失掉,而是留下一个称为僵尸进程(Zombie)的数据结构,等待父进程处理。这是每个子进程在结束时都要经过的阶段。如果子进程在exit()之后,父进程没有来得及处理,这时用ps命令就能看到子进程的状态是“Z”。如果父进程能及时 处理,可能用ps命令就来不及看到子进程的僵尸状态,但这并不等于子进程不经过僵尸状态。 如果父进程在子进程结束之前退出,则子进程将由init接管。init将会以父进程的身份对僵尸状态的子进程进行处理。
  13.  
  14. 二:孤儿进程(无害)
  15.  
  16.   孤儿进程:一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。孤儿进程将被init进程(进程号为1)所收养,并由init进程对它们完成状态收集工作。
  17.  
  18.   孤儿进程是没有父进程的进程,孤儿进程这个重任就落到了init进程身上,init进程就好像是一个民政局,专门负责处理孤儿进程的善后工作。每当出现一个孤儿进程的时候,内核就把孤 儿进程的父进程设置为init,而init进程会循环地wait()它的已经退出的子进程。这样,当一个孤儿进程凄凉地结束了其生命周期的时候,init进程就会代表党和政府出面处理它的一切善后工作。因此孤儿进程并不会有什么危害。
  19.  
  20. 我们来测试一下(创建完子进程后,主进程所在的这个脚本就退出了,当父进程先于子进程结束时,子进程会被init收养,成为孤儿进程,而非僵尸进程),文件内容
  21.  
  22. import os
  23. import sys
  24. import time
  25.  
  26. pid = os.getpid()
  27. ppid = os.getppid()
  28. print 'im father', 'pid', pid, 'ppid', ppid
  29. pid = os.fork()
  30. #执行pid=os.fork()则会生成一个子进程
  31. #返回值pid有两种值:
  32. # 如果返回的pid值为0,表示在子进程当中
  33. # 如果返回的pid值>0,表示在父进程当中
  34. if pid > 0:
  35. print 'father died..'
  36. sys.exit(0)
  37.  
  38. # 保证主线程退出完毕
  39. time.sleep(1)
  40. print 'im child', os.getpid(), os.getppid()
  41.  
  42. 执行文件,输出结果:
  43. im father pid 32515 ppid 32015
  44. father died..
  45. im child 32516 1
  46.  
  47. 看,子进程已经被pid1init进程接收了,所以僵尸进程在这种情况下是不存在的,存在只有孤儿进程而已,孤儿进程声明周期结束自然会被init来销毁。
  48.  
  49. 三:僵尸进程危害场景:
  50.  
  51.   例如有个进程,它定期的产 生一个子进程,这个子进程需要做的事情很少,做完它该做的事情之后就退出了,因此这个子进程的生命周期很短,但是,父进程只管生成新的子进程,至于子进程 退出之后的事情,则一概不闻不问,这样,系统运行上一段时间之后,系统中就会存在很多的僵死进程,倘若用ps命令查看的话,就会看到很多状态为Z的进程。 严格地来说,僵死进程并不是问题的根源,罪魁祸首是产生出大量僵死进程的那个父进程。因此,当我们寻求如何消灭系统中大量的僵死进程时,答案就是把产生大 量僵死进程的那个元凶枪毙掉(也就是通过kill发送SIGTERM或者SIGKILL信号啦)。枪毙了元凶进程之后,它产生的僵死进程就变成了孤儿进 程,这些孤儿进程会被init进程接管,init进程会wait()这些孤儿进程,释放它们占用的系统进程表中的资源,这样,这些已经僵死的孤儿进程 就能瞑目而去了。
  52.  
  53. 四:测试
  54. #1、产生僵尸进程的程序test.py内容如下
  55.  
  56. #coding:utf-8
  57. from multiprocessing import Process
  58. import time,os
  59.  
  60. def run():
  61. print('子',os.getpid())
  62.  
  63. if __name__ == '__main__':
  64. p=Process(target=run)
  65. p.start()
  66.  
  67. print('主',os.getpid())
  68. time.sleep(1000)
  69.  
  70. #2、在unix或linux系统上执行
  71. [root@vm172-31-0-19 ~]# python3 test.py &
  72. [1] 18652
  73. [root@vm172-31-0-19 ~]# 18652
  74. 18653
  75.  
  76. [root@vm172-31-0-19 ~]# ps aux |grep Z
  77. USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
  78. root 18653 0.0 0.0 0 0 pts/0 Z 20:02 0:00 [python3] <defunct> #出现僵尸进程
  79. root 18656 0.0 0.0 112648 952 pts/0 S+ 20:02 0:00 grep --color=auto Z
  80.  
  81. [root@vm172-31-0-19 ~]# top #执行top命令发现1zombie
  82. top - 20:03:42 up 31 min, 3 users, load average: 0.01, 0.06, 0.12
  83. Tasks: 93 total, 2 running, 90 sleeping, 0 stopped, 1 zombie
  84. %Cpu(s): 0.0 us, 0.3 sy, 0.0 ni, 99.7 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
  85. KiB Mem : 1016884 total, 97184 free, 70848 used, 848852 buff/cache
  86. KiB Swap: 0 total, 0 free, 0 used. 782540 avail Mem
  87.  
  88. PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
  89. root 20 0 29788 1256 988 S 0.3 0.1 0:01.50 elfin
  90.  
  91. #3、
  92. 等待父进程正常结束后会调用waitwaitpid去回收僵尸进程
  93. 但如果父进程是一个死循环,永远不会结束,那么该僵尸进程就会一直存在,僵尸进程过多,就是有害的
  94. 解决方法一:杀死父进程
  95. 解决方法二:对开启的子进程应该记得使用joinjoin会回收僵尸进程
  96. 参考python2源码注释
  97. class Process(object):
  98. def join(self, timeout=None):
  99. '''
  100. Wait until child process terminates
  101. '''
  102. assert self._parent_pid == os.getpid(), 'can only join a child process'
  103. assert self._popen is not None, 'can only join a started process'
  104. res = self._popen.wait(timeout)
  105. if res is not None:
  106. _current_process._children.discard(self)
  107.  
  108. join方法中调用了wait,告诉系统释放僵尸进程。discard为从自己的children中剔除
  109.  
  110. 解决方法三:http://blog.csdn.net/u010571844/article/details/50419798

4月24日 python学习总结 多进程与子进程的更多相关文章

  1. SPSS 2019年10月24日 今日学习总结

    2019年10月24日今日课上内容1.SPSS掌握基于键值的一对多合并2.掌握重构数据3.掌握汇总功能 内容: 1.基于键值的一对多合并 合并文件 添加变量 合并方法:基于键值的一对多合并 变量 2. ...

  2. 4月2日 python学习总结

    昨天内容回顾: 1.迭代器 可迭代对象: 只要内置有__iter__方法的都是可迭代的对象 既有__iter__,又有__next__方法 调用__iter__方法==>得到内置的迭代器对象 调 ...

  3. 4月8日 python学习总结 模块与包

    一.包 #官网解释 Packages are a way of structuring Python's module namespace by using "dotted module n ...

  4. 4月12日 python学习总结 继承和派生

    一.继承 什么是继承:   继承是一种新建类的方式,在python中支持一个子类继承多个父类   新建类称为子类或派生类   父类可以称之为基类或者超类   子类会遗传父类的属性 2.  为什么继承 ...

  5. 4月11日 python学习总结 对象与类

    1.类的定义 #类的定义 class 类名: 属性='xxx' def __init__(self): self.name='enon' self.age=18 def other_func: pas ...

  6. 6月15日 python学习总结 Django模板语言相关内容

    Django模板语言相关内容   Django模板系统 官方文档 常用语法 只需要记两种特殊符号: {{  }}和 {% %} 变量相关的用{{}},逻辑相关的用{%%}. 变量 {{ 变量名 }} ...

  7. 5月31日 python学习总结 JavaScript概述

    JavaScript概述 ECMAScript和JavaScript的关系 1996年11月,JavaScript的创造者--Netscape公司,决定将JavaScript提交给国际标准化组织ECM ...

  8. 6月11日 python学习总结 框架理论

    Web框架本质及第一个Django实例   Web框架本质 我们可以这样理解:所有的Web应用本质上就是一个socket服务端,而用户的浏览器就是一个socket客户端. 这样我们就可以自己实现Web ...

  9. 4月4日 python学习总结 os pickle logging

    1.序列化和反序列化 我们把对象(变量)从内存中变成可存储或传输的过程称之为序列化,在Python中叫pickling. 反过来,把变量内容从序列化的对象重新读到内存里称之为反序列化,即unpickl ...

随机推荐

  1. PHP面试常考内容之Memcache和Redis(1)

    你好,是我琉忆.继上周(2019.2-11至2-15)发布的"PHP面试常考内容之面向对象"专题后,发布的第二个专题,感谢你的阅读.本周(2019.2-18至2-22)的文章内容点 ...

  2. 疑难杂症:运用 transform 导致文本模糊的现象探究

    在我们的页面中,经常会出现这样的问题,一块区域内的文本或者边框,在展示的时候,变得特别的模糊,如下(数据经过脱敏处理): 正常而言,应该是这样的: emmm,可能大图不是很明显,我们取一细节对比,就非 ...

  3. MyBatis中使用log4j进行调试入门实例

    导入log4j.jar 设置日志级别等相关内容 文件内容(仅控制台有效): ### 设置###log4j.rootLogger = debug,stdout,D,E### 输出sql信息到控制抬 ## ...

  4. MyBatis功能点二:从责任链设计模式的角度理解插件实现技术

    MyBatis允许对其四大组件(Executor,StatementHandler,ParameterHandler, ResultSetHandler)进行增强处理.在创建四大组件对象的时候 1.每 ...

  5. 使用burpsuite对APP数据包进行安全测试

    如之前的文章将手机抓包监听环境设置好后(之前学习burpsuite的时候写的,保存到草稿箱,忘记发了...),主要用到的功能如下: 1-1.数据包篡改 截获包后,可以对数据包中的内容在Raw标签框中直 ...

  6. 【C#设计模式】里氏替换原则

    今天,我们再来学习 SOLID 中的"L"对应的原则:里式替换原则. 里氏替换原则 里氏替换原则(Liskov Substitution Principle):派生类(子类)对象能 ...

  7. C#委托Action、Action<T>、Func<T>、Predicate<T>系统自带的委托

    C#委托Action.Action<T>.Func<T>.Predicate<T>   CLR环境中给我们内置了几个常用委托Action. Action<T& ...

  8. VIM对替换的数字进行计算

    VIM对替换的数字进行计算 运行下面的命令 %s/sub(\([0-9]*\))/\=submatch(1)+8/g 函数式 :s/替换字符串/\=函数式

  9. java创建线程的四种方法

    第一种:  通过继承Thread类创建线程 第二种: 通过实现Runnable接口创建线程 这两种早已烂记于心,这里就不作过多的介绍, 主要介绍其源码 Thread类 implements Runna ...

  10. [GYCTF2020]Ezsqli 无列名注入

    手工注入了几下,是数字注入,过滤了 or , union 输入1||1=1   回显Nu1L 再输入 1&&(ascii(substr(database(),1,1))>32)# ...