一、进程的创建方式
1. os.fork
importos#创建子进程,调用一次fork会返回两次pid,如果当前是运行在父进程当中则为子进程的进程号,如果是在子进程当中则为0,发生错误抛出OSError异常
pid =os.fork()if pid ==0:print('Here is son process')print(os.getpid(), os.getppid()) #getppid: 获取父进程进程号
else:print('Here is main process')print(os.getpid()) #获取当前进程号
os.wait()#主进程等待子进程结束释放资源
--------------------------------------- 我是分割线 -------------------------------------os.kill(pid, signal): 发送一个信号给pid进程
os.wait(): 等待任何一个子进程的结束,返回一个tuple,包含子进程进程号、退出状态信息
os.waitpid(pid, options): 等待指定进程号的子进程结束,返回一个tuple同wait。-pid:
pid=0,等待并获取当前进程组中的任何子进程的值;
pid= -1,等待当前进程的任何子进程;
pid< -1,获取进程组id为pid的绝对值的任何一个进程。
当系统调用返回-1时,抛出一个OSError异常。-options:
os.WNOHANG-如果指定的子进程没有退出,则不阻塞waitpid()调用,立即返回0
os.WCONTINUED-当被暂停的子进程,又被信号恢复时,则立即返回子进程的pid。
os.WUNTRACED-当子进程被暂停时,则立即返回子进程的 pid
os._exit(): 参数为0时表示程序正常退出
fork操作仅可在类uinx系统使用, Windows并未实现此函数, 即Windows系统不可用。
需要在父进程当中对子进程进行资源回收, 否则易出现孤儿进程、僵尸进程。
若需产生较多的子进程时,进程管理非常不便。
是最接近底层创建的方式,效率很高。
2. multiprocessing
importmultiprocessingdefson_func(num):print('Here is son process')
...print('son process ending ...')if __name__ == '__main__':print('Here is main process')
num= 2
#注:target接收函数引用,不可带括号;args是以元组方式传参,仅有一个参数时需加逗号
son_process = multiprocessing.Process(target=son_func, args=(num,))
son_process.start()#需要start启动子进程才会运行
print('main ending ...')--------------------------------------- 我是分割线 -------------------------------------
from multiprocessing importProcessclassHandlerProcess(Process):defrun(self):print('Here is son process')
....def __del__(self):print('Son process will over')if __name__ == '__main__':print('main process start')
process=HandlerProcess()#start会自动调用 run 方法,所以自定义的进程类中必须实现 run 方法
process.start()print('main process ending ...')--------------------------------------- 我是分割线 -------------------------------------
from multiprocessing importPooldefprocess_pool(n):print('start 进程 %s'%n, os.getpid())
time.sleep(5)print('end 进程 %s'%n, os.getpid())if __name__ == '__main__':
pool= Pool(9) #设置进程池大小,若不指定默认创建的进程数为系统的内核数量
for i in range(10):
ret= pool.apply_async(process_pool, args=(i,)) #apply: 同步执行, apply_async: 异步执行
pool.close()#关闭进程池,不再接收新任务
pool.join() #主进程阻塞等待子进程结束,必须在join之后
--------------------------------------- 我是分割线 -------------------------------------Process对象的参数:classProcess(object):def __init__(self, group=None, target=None, name=None, args=(), kwargs={}):""":param group,扩展保留字段
:param target,目标代码,一般为创建进程所要执行的函数引用
:param name,进程名字
:param args,目标函数的位置参数
:param kwargs,目标函数的关键字参数"""
pass
Process对象可以创建进程,但Process对象不是进程,其删除与否与系统资源是否被回收没有直接的关系。
Process创建进程时,子进程会将主进程的Process对象完全复制一份,这样在主进程和子进程各有一个Process对象,但process.start()启动的是子进程,主进程中的Process对象作为一个静态对象存在,此处的del方法调用了两次。
主进程执行完毕后会默认等待子进程结束后回收资源,不需要手动回收资源;
join()函数用来控制子进程结束的顺序,主进程会阻塞等待子进程结束,其内部也有一个清除僵尸进程的函数,可以回收资源;
当子进程执行完毕后,会产生一个僵尸进程,其会被join函数回收,或者再有一条进程开启,start函数也会回收僵尸进程,所以不一定需要写join函数。
windows系统在子进程结束后会立即自动清除子进程的Process对象,而linux系统子进程的Process对象如果没有join函数和start函数的话会在主进程结束后统一清除。
二、孤儿进程和僵尸进程
1. 孤儿进程: 一个父进程执行完成或被终止后,它的子进程还在继续运行,这些正在运行的子进程即为孤儿进程,孤儿进程将会被 init 进程收养,并收集他们的状态信息,且init进程无法被杀死。
2. 僵尸进程: 一个进程创建子进程之后,一旦子进程先于父进程退出,而父进程并没有调用wait或者waitpid以此来收集子进程的状态信息,那么在系统当中仍然保存这个已经结束运行的子进程描述符。
子进程正常结束后,并不会立马退出,会留下一个 Zombie的数据结构等待父进程处理,若子进程exit后父进程还未来得及处理这个子进程,使用 ps aux | grep xxx 命令可以看到状态为 Z 的僵尸进程,僵尸进程无法被 kill 命令杀掉。
孤儿进程是执行状态,只是没有了爸爸,然后它会被init进程收养;僵尸进程是终止状态,但是它的爸爸由他自生自灭,所以它为了证明来过这个世界,在系统中保留了它的进程描述符。孤儿进程并没有什么危害,它的养爸爸会给它善后处理,但是僵尸进程由于在系统中存留下了进程描述符,这个行为就比孤儿进程坏多了,假设一个父进程只管一直创建新的子进程,对子进程是否退出一概不处理,运行一段时间后,系统中将会出现大量状态为Z的僵尸进程,僵尸进程本身不是问题的根源,而是创造它的父进程没有做到爸爸的责任,所以想要消灭系统中大量僵尸进程的措施是毙掉这些僵尸进程的爸爸~
具体解决:父进程中调用 signal.signal(signal.SIGCHLD, handler) 接收子进程的退出信号,handler为信号处理函数,在这个信号处理函数当中死循环调用 waitpid() ,用来获取所有已经终止的子进程状态,waitpid 的options参数必须指定WNOHANG选项,他告知waitpid在有尚未终止的子进程在运行时不要阻塞(我们不能在循环内调用wait,因为没有办法防止wait在尚有未终止的子进程在运行时阻塞,wait将会阻塞到现有的子进程中第一个终止为止)。
三、kill命令
1.命令格式: kill[参数][进程号]
2.命令功能:发送指定的信号到相应进程。不指定型号将发送SIGTERM(15)终止指定进程。如果任然无法终止该程序可用“-KILL” 参数,其发送的信号为SIGKILL(9) ,将强制结束进程,使用ps命令或者jobs 命令可以查看进程号。root用户将影响用户的进程,非root用户只能影响自己的进程。
3.命令参数:
-l 信号,若果不加信号的编号参数,则使用“-l”参数会列出全部的信号名称
-a 当处理当前进程时,不限制命令名和进程号的对应关系
-p 指定kill 命令只打印相关进程的进程号,而不发送任何信号
-s 指定发送信号
-u 指定用户
四、 信号
1. Linux查看所有信号(kill -l )
1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL
5) SIGTRAP 6) SIGABRT 7) SIGBUS 8) SIGFPE
9) SIGKILL 10) SIGUSR1 11) SIGSEGV 12) SIGUSR2
13) SIGPIPE 14) SIGALRM 15) SIGTERM 16) SIGSTKFLT
17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU
25) SIGXFSZ 26) SIGVTALRM 27) SIGPROF 28) SIGWINCH
29) SIGIO 30) SIGPWR 31) SIGSYS 34) SIGRTMIN
35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3 38) SIGRTMIN+4
39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8
43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12
47) SIGRTMIN+13 48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14
51) SIGRTMAX-13 52) SIGRTMAX-12 53) SIGRTMAX-11 54) SIGRTMAX-10
55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7 58) SIGRTMAX-6
59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2
63) SIGRTMAX-1 64) SIGRTMAX
SIGKILL信号是无条件终止进程,SIGSTOP(ctrl + z), SIGSTOP和SIGKILL不可被忽略,其他信号可由进程自行忽略
2. 信号预处理函数
python使用signal.signal()函数来预设(register)信号处理函数: singnal.signal(signalnum, handler)
signalnum为某个信号,handler为该信号的处理函数
importosimportsignalimportmultiprocessingdefexit_process(signal_num, fram):print('do someting ...') #可以在捕获到某个信号时,做一些额外的操作
os._exit(0) #退出当前进程
defhandler():print('son process running')
...defmain():print('main running')
...if __name__ == '__main__':
main()
son_process= multiprocessing.Process(target=handler)
signal.signal(signal.SIGINT, exit_process)