43.forking
forking
什么是forking
1、fork(分岔)在Linux系统中使用非常广泛
2、当某一命令执行时,父进程(当前进程)fork出一个子进程
3、父进程将自身资源拷贝一份,命令在子进程中运行时,就具有和父进程完全一样的运行环境
进程的生命周期
1、父进程fork出子进程并挂起
2、子进程运行完毕后,释放大部分资源并通知父进程,这个时候,子进程被称作僵尸进程
3、父进程获知子进程结束,子进程所有资源释放
僵尸进程
1、僵尸进程没有任何可执行代码,也不能被调度
2、如果系统中存在过多的僵尸进程,将因为没有可用的进程号而导致系统不能产生新的进程
3、对于系统管理员来说,可以试图杀死其父进程或重启系统来消除僵尸进程
forking编程基本思路
1、需要使用os模块
2、os.fork()函数实现forking功能
3、python中,绝大多数的函数只返回一次,os.forking将返回两次
4、对fork()的调用,针对父进程返回子进程的PID;对于子进程,返回PID0
5、因为所有的父子进程拥有相同的资源,所以在编写程序时要避免资源冲突
pid = os.fork() #实现forking
if pid: #在父进程中关闭子进程连接
close_child_conn #接着处理其他的连接请求
handle_more_conn
else: #子进程关闭父进程连接,响应当
close_parent_conn #前的用户连接
process_this_conn
使用轮询解决zombie问题
1、父进程通过os.wait()来得到子进程是否终止的信息
2、在子进程终止和父进程调用wait()之间的这段时间,子进程被称为zombie(僵尸)进程
3、如果子进程还没有终止,父进程先退出了,那么子进程会持续工作。系统自动将子进程的父进程设置为init进程,init将来负责清理僵尸进程
4、python可以使用waitpid()来处理子进程
5、waitpid()接受两个参数,第一个参数设置为-1,表示与wait()函数相同;第二个参数如果设置为0表示挂起父进程,直到子程序退出,设置为1表示不挂起父进程
6、waitpid()的返回值:如果子进程尚未结束则返回0,否则返回子进程的PID
#!/usr/bin/env python #coding:utf-8 import os, time def reap(): result = os.waitpid(-1,os.WNOHANG) ##WNOHANG即值为1 print 'Reaped child process %d' % result[0] pid = os.fork() if pid: print 'In parent. Sleeping 15s...' time.sleep(15) reap() time.sleep(5) print 'parent done' else: print 'In child. Sleeping 5s...' time.sleep(5) print 'Child terminating.'
执行结果:
[root@zabbixserver data]# python ser.py In parent. Sleeping 15s... In child. Sleeping 5s... Child terminating. Reaped child process 12378 parent done
forking服务器
1、在网络服务器中,forking被广泛使用
2、如果服务器需要同时响应多个客户端,那么forking是解决问题最常用的方法之一
3、父进程负责接受客户端的连接请求
4、子进程负责处理客户端的请求
forking 基础应用
写 bfork.py 脚本,主要要求如下:
1、在父进程中打印In parent然后睡眠 10 秒
2、在子进程中编写循环,循环 5 次,输出当系统时间,每次循环结束后睡眠 1秒
3、父子进程结束后,分别打印parent exit和child exit
方案
os.fork()函数实现 forking 功能。 对 fork()的调用, 针对父进程返回子进程的 PID;对于子进程,返回 PID 0。
通过条件判断,返回值非零则为父进程,否则为子进程
实现此案例需要按照如下步骤进行。
步骤一:编写脚本
#!/usr/bin/env python
#coding:utf-8
import os
import time
pid = os.fork()
if pid: #父进程中执行的代码
print 'In parent'
time.sleep(10)
print 'parent exit'
else: #子进程中执行的代码
for i in range(5):
print time.ctime()
print 'child exit'
步骤二:验证执行结果
[root@host-192-168-3-6 test]# python bfork.py
In parent
Wed Aug 2 10:06:38 2017
Wed Aug 2 10:06:38 2017
Wed Aug 2 10:06:38 2017
Wed Aug 2 10:06:38 2017
Wed Aug 2 10:06:38 2017
child exit
parent exit
[root@host-192-168-3-6 test]#
利用 forking 创建 TCP 时间戳服务器
编写 forktsServer.py 服务器脚本,主要要求如下:
1、服务器监听在 0.0.0.0 的 21567 端口上
2、收到客户端数据后,将其加上时间戳后回送给客户端
3、如果客户端发过来的字符全是空白字符,则终止与客户端的连接
4、服务器能够同时处理多个客户端的请求
5、程序通过 forking 来实现
方案
如果服务器需要同时响应多个客户端,那么 forking 是解决问题最常用的方法之一。
父进程负责接受客户端的连接请求,子进程负责处理客户端的请求。
因为执行 fork 调用后,将会把父进程所有的资源都拷贝一份。所以要注意父进程需要断开与客户端的连接,继续等待新的请求;子进程需要关闭 socket,避免造成端口冲突
实现此案例需要按照如下步骤进行。
步骤一:编写脚本
#!/usr/bin/env python
#coding:utf-8
import socket
import time
import os
import sys
HOST = ''
PORT = 21567
BUFSIZ = 1024
ADDR = (HOST, PORT)
def reap(): #定义清除僵尸进程函数
while True:
try:
result = os.waitpid(-1, os.WNOHANG)
if not result[0]:
break
except:
break
print 'Reaped child process %d' % result[0]
tcpSerSock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
tcpSerSock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
tcpSerSock.bind(ADDR)
tcpSerSock.listen(5)
print 'Parent at %d listening for connections' % os.getpid()
while True:
print 'waiting for connection...'
tcpCliSock, addr = tcpSerSock.accept()
print '...connected from:', addr
reap()
pid = os.fork()
if pid: #父进程关闭客户端连接,继续监听新的请求
tcpCliSock.close()
continue
else: #子进程关闭 socket,避免冲突
tcpSerSock.close()
while True:
data = tcpCliSock.recv(BUFSIZ)
if not data.strip():
break
tcpCliSock.send('[%s] %s' % (time.ctime(), data))
tcpCliSock.close()
sys.exit(0)
tcpSerSock.close()
步骤二:验证执行结果
[root@py01 bin]# ./forktsServer.py #启动服务器
[root@py01 bin]# telnet 127.0.0.1 21567 #可以打开多个终端,同时连接
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
hello world!
[Mon Jul 6 19:25:42 2015] hello world!
#此处输入回车,退出连接
Connection closed by foreign host
例子:
myfork1.py内容:
#!/usr/bin/env python
#coding:utf8
import os
print "starting..."
os.fork()
print "hello world"
myfork2.py内容:
#!/usr/bin/env python
#coding:utf8
import os
print "starting..."
pid = os.fork()
if pid:
print "hello from parent."
else:
print "hello from child"
print "hello from both.."
fork_say.py内容:
#!/usr/bin/env python
#coding:utf8
import os
import sys
for i in range(6):
print "hello"
for i in range(6):
pid = os.fork()
if pid == '0':
print 'hello'
sys.exit() #测试时可以把这个注释掉看看有什么不同
fork_ping.py内容:
#!/usr/bin/env python
#coding:utf8
import sys
import os
ip_list = ("192.168.3.%s" % i for i in range(1,255))
for ip in ip_list:
pid = os.fork()
if pid == 0:
result = os.system("ping -c2 %s &> /dev/null" % ip)
if result == 0:
print "%s:up" % ip
else:
print "%s:down" % ip
sys.exit()
zb.py内容:
#!/usr/bin/env python
#coding:utf8
import os
import time
pid = os.fork()
if pid:
print "in parent.sleeping..."
time.sleep(20)
print "parent done"
else:
print "in child .sleeping..."
time.sleep(20)
print 'child done'
zb1.py内容:
#!/usr/bin/env python
#coding:utf8
import os
import time
pid = os.fork()
if pid:
print "in parent.sleeping..."
print os.waitpid(-1, 0)
time.sleep(5)
print "parent done"
else:
print "in child .sleeping..."
time.sleep(10)
print 'child done'
zb2.py内容:
#!/usr/bin/env python
#coding:utf8
import os
import time
pid = os.fork()
if pid:
print "in parent.sleeping..."
print os.waitpid(-1, os.WNOHANG)
time.sleep(5)
print "parent done"
else:
print "in child .sleeping..."
time.sleep(10)
print 'child done'
forktcpserv1.py内容:
#!/usr/bin/env python
#coding:utf8
import os
import time
import socket
import sys
host = ''
port = 21345
addr = (host,port)
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(addr)
s.listen(1)
while True:
cli_sock,cli_addr = s.accept()
pid = os.fork()
if pid:
cli_sock.close()
else:
s.close()
while True:
data = cli_sock.recv(4096)
if not data.strip():
cli_sock.close()
sys.exit()
cli_sock.send("[%s] %s" % (time.ctime(),data))
s.close()
测试:
[root@host-192-168-3-6 ~]# telnet 127.0.0.1 21345
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
hello
[Mon Jun 19 14:49:55 2017] hello
world
[Mon Jun 19 14:49:57 2017] world
forktcpserv2.py内容:
#!/usr/bin/env python
#coding:utf8
import os
import time
import socket
import sys
host = ''
port = 21345
addr = (host,port)
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(addr)
s.listen(1)
while True:
try:
while True:
result = os.waitpid(-1, os.WNOHANG)
if result[0] == 0:
break
except OSError:
pass
cli_sock,cli_addr = s.accept()
pid = os.fork()
if pid:
cli_sock.close()
else:
s.close()
while True:
data = cli_sock.recv(4096)
if not data.strip():
cli_sock.close()
sys.exit()
cli_sock.send("[%s] %s" % (time.ctime(),data))
s.close()