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()
 

 

posted @ 2019-06-19 09:21  钟桂耀  阅读(279)  评论(0编辑  收藏  举报