网络编程——进程

进程

 

        进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。在早期面向进程设计的计算机结构中,进程是程序的基本执行实体;在当代面向线程设计的计算机结构中,进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体。

 

程序和进程的区别就在于:

        程序是指令的集合,它是进程运行的静态描述文本;进程是程序的一次执行活动,属于动态概念。

       在多道编程中,我们允许多个程序同时加载到内存中,在操作系统的调度下,可以实现并发地执行。就是这样的设计,大大提高了CPU的利用率。进程的出现让每个用户感觉到自己独享CPU,因此,进程就是为了在CPU上实现多道编程而提出的。

 

进程的创建

 

1. 系统初始化(查看进程linux中用ps命令,windows中用任务管理器,前台进程负责与用户交互,后台运行的进程与用户无关,运行在后台并且只在需要时才唤醒的进程,称为守护进程,如电子邮件、web页面、新闻、打印)

2. 一个进程在运行过程中开启了子进程(如nginx开启多进程,os.fork,subprocess.Popen等)

3. 用户的交互式请求,而创建一个新进程(如用户双击暴风影音)

4. 一个批处理作业的初始化(只在大型机的批处理系统中应用)

  

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

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

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

 

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

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

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

 

 

进程的终止

 

1. 正常退出(自愿,如用户点击交互式页面的叉号,或程序执行完毕调用发起系统调用正常退出,在linux中用exit,在windows中用ExitProcess)

2. 出错退出(自愿,python a.py中a.py不存在)

3. 严重错误(非自愿,执行非法指令,如引用不存在的内存,1/0等,可以捕捉异常,try...except...)

4. 被其他进程杀死(非自愿,如kill -9)

 

 

进程的层次结构

 

无论UNIX还是windows,进程只有一个父进程,不同的是:

  1. 在UNIX中所有的进程,都是以init进程为根,组成树形结构。父子进程共同组成一个进程组,这样,当从键盘发出一个信号时,该信号被送给当前与键盘相关的进程组中的所有成员。

  2. 在windows中,没有进程层次的概念,所有的进程都是地位相同的,唯一类似于进程层次的暗示,是在创建进程时,父进程得到一个特别的令牌(称为句柄),该句柄可以用来控制子进程,但是父进程有权把该句柄传给其他子进程,这样就没有层次了。

 

 

进程并发的实现

 

进程并发的实现在于,硬件中断一个正在运行的进程,把此时进程运行的所有状态保存下来,为此,操作系统维护一张表格,即进程表(process table),每个进程占用一个进程表项(这些表项也称为进程控制块)

 

 

进程的pid 父进程ppid

 

获取进程号Pid的四种方法:

1、启动任务管理器查找

2、中断cmd-->tasklist(tasklist|findstr 程序名)

3、os模块方法 主进程os.getpid() 父进程os.getppid()

4、类中方法p.pid

 

 

开启进程的两种方式

 

创建进程的第一种方式:函数的形式

创建一个进程对象,将进程的代码放进进程对象中.

启动进程对象,会给操作系统发送一个请求,操作系统得到请求命令,会在内存开辟一个空间,操作系统会将主进程所有的代码数据copy一份到子进程,这个过程会有时间消耗。主进程开始 和子进程开始,谁先到内存被cpu执行,谁先运行.

 

创建进程的第二种方式:对象的形式(用的比较少)

利用创建好的框架去构建进程,

        好处:很多方法或者功能都给你提供好了.

        坏处: 只能按照他的规定去写.

 

# 方法一
from multiprocessing import Process
import time

def task(name):
    print('%s is runing' % (name))
    time.sleep(3)
    print('%s is done' % (name))

if __name__ == '__main__':
    p = Process(target=task, args=('Mask',))  # 传入的必须是元祖
    # p = Process(target=task, kwargs={'name':'Mask'})  # 两种传参方式
    p.start()
    print('===主进程===')
# 方法二
from multiprocessing import Process
import time

class MyProcess(Process):
    def __init__(self,name):
        self.name = name
        super().__init__()
        
    def run(self):  # 必须定义一个run方法
        print('%s is runing' % (self.name))
        time.sleep(3)
        print('%s is done' % (self.name))
        
if __name__ == '__main__':
    p = MyProcess('Mask')
    p.start()  # 自动启动调起run方法
    print('===主进程===')

 

进程的空间隔离验证

 

# 在同一个进程下
x = 1000
def task():
   global x
   x = 2
   
print(x)  # 结果为1000
task()
print(x)  # 结果为2
from multiprocessing import Process
import time
x = 1000

def task():
   time.sleep(3)
   global x
   x = 2
   print('子进程中的X值为:', x)

if __name__ == '__main__':
   p = Process(target=task,)
   p.start()  # 发送一个请求
   time.sleep(5)
   print('主进程中的X值为:',x)

由上可知:

1、在一个进程中,内存空间只有一份,代码由上至下逻辑运行

2、当系统开辟了子进程后:

主进程和子进程分别拥有独立的内存空间,是隔离的

子进程是在主进程的基础上copy了一份,初始数据值相同,但是随着两个进程代码逻辑的不同,执行的结果不同。

 

 

 

join方法

 

from multiprocessing import Process
import time
x = 1000

def task(n):
	print('%s is begin' % n)
	time.sleep(3)
	print('%s is over' % n)

def main():
	print('主进程执行.....')

if __name__ == '__main__':
	p = Process(target=task,args=(1,))
	p.start()  # 发送一个请求
 
# 要求主进程在子进程后执行
# 方法一、主程序开始执行的时间晚于子进程
 	time.sleep(5)  # 实际生产不现实,不可用
# 方法二、
	p.join()  # 通知操作系统p子进程结束之后,再马上执行主进程
	main()

 

进程的对象的其他属性 (了解)

from multiprocessing import Process
import time
import os

def task(n):
    time.sleep(3)
    print('%s is running' % n, os.getpid(), os.getppid())

if __name__ == '__main__':
    p1 = Process(target=task, args=(1,), name = '任务1')
    # print(p1.name) # 给子进程起名字
    # for i in range(3):
    #     p = Process(target=task, args=(1,))
    #     print(p.name)  # 给子进程起名字
    p1.start()
    # p1.terminate()  # 杀死进程
    # time.sleep(2)  # 睡一会,他就将我的子进程杀死了。
    # print(p1.is_alive())  # False  进程是否存在
    print(p1.pid)
    # print('主')
    print(os.getpid())

 

僵尸进程与孤儿进程(了解)

在linux 才会出现:僵尸进程与孤儿进程参考博客:http://www.cnblogs.com/Anker/p/3271773.html
在windos中是否存在僵尸进程:https://blog.csdn.net/u010150046/article/details/76461316 
一:僵尸进程(有害)   
    僵尸进程:一个进程使用fork创建子进程,如果子进程退出,而父进程并没有调用wait或waitpid获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中。这种进程称之为僵死进程。
详解如下:我们知道在unix/linux中,正常情况下子进程是通过父进程创建的,子进程在创建新的进程。子进程的结束和父进程的运行是一个异步过程,即父进程永远无法预测子进程到底什么时候结束,如果子进程一结束就立刻回收其全部资源,那么在父进程内将无法获取子进程的状态信息。 因此,UNⅨ提供了一种机制可以保证父进程可以在任意时刻获取子进程结束时的状态信息: 
1、在每个进程退出的时候,内核释放该进程所有的资源,包括打开的文件,占用的内存等。但是仍然为其保留一定的信息(包括进程号the process ID,退出状态the termination status of the process,运行时间the amount of CPU time taken by the process等) 
2、直到父进程通过wait / waitpid来取时才释放. 但这样就导致了问题,如果进程不调用wait / waitpid的话,那么保留的那段信息就不会释放,其进程号就会一直被占用,但是系统所能使用的进程号是有限的,如果大量的产生僵死进程,将因为没有可用的进程号而导致系统不能产生新的进程. 此即为僵尸进程的危害,应当避免。
    任何一个子进程(init除外)在exit()之后,并非马上就消失掉,而是留下一个称为僵尸进程(Zombie)的数据结构,等待父进程处理。这是每个子进程在结束时都要经过的阶段。如果子进程在exit()之后,父进程没有来得及处理,这时用ps命令就能看到子进程的状态是“Z”。如果父进程能及时处理,可能用ps命令就来不及看到子进程的僵尸状态,但这并不等于子进程不经过僵尸状态。如果父进程在子进程结束之前退出,则子进程将由init接管。init将会以父进程的身份对僵尸状态的子进程进行处理。 
二:孤儿进程(无害)
    孤儿进程:一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。孤儿进程将被init进程(进程号为1)所收养,并由init进程对它们完成状态收集工作。
    孤儿进程是没有父进程的进程,孤儿进程这个重任就落到了init进程身上,init进程就好像是一个民政局,专门负责处理孤儿进程的善后工作。每当出现一个孤儿进程的时候,内核就把孤儿进程的父进程设置为init,而init进程会循环地wait()它的已经退出的子进程。这样,当一个孤儿进程凄凉地结束了其生命周期的时候,init进程就会代表党和政府出面处理它的一切善后工作。因此孤儿进程并不会有什么危害。

 

守护进程 一个参数而已.

 

子进程守护父进程,随着父进程的结束,子进程也结束(不会等子进程执行完)

主进程创建守护进程

  其一:守护进程会在主进程代码执行结束后就终止

  其二:守护进程内无法再开启子进程,否则抛出异常:AssertionError: daemonic processes are not allowed to have children

注意:进程之间是互相独立的,主进程代码运行结束,守护进程随即终止

from multiprocessing import Process
import time
import random

class MyProcess(Process):
    def __init__(self,name):
        self.name=name
        super().__init__()

    def run(self):
        print('%s is running' % self.name)
        time.sleep(random.randrange(1,3))
        print('%s is run end' % self.name)

p = MyProcess('Mask')
p.daemon = True  #一定要在p.start()前设置,设置p为守护进程,禁止p创建子进程,
                 # 并且父进程代码执行结束,p即终止运行
p.start()
print('===主进程===')

 

#主进程代码运行完毕,守护进程就会结束
from multiprocessing import Process
from threading import Thread
import time
def foo():
    print(123)
    time.sleep(1)
    print("end123")

def bar():
    print(456)
    time.sleep(3)
    print("end456")

p1 = Process(target = foo)
p2 = Process(target = bar)

p1.daemon = True
p1.start()
p2.start()
print("===主进程===") # 打印该行则主进程代码结束,则守护进程p1应该被终止,可能会有p1任务执行的打印信息123,
# 因为主进程打印“===主进程===”时,p1也执行了,但是随即被终止

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值