• 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()
     
  • 相关阅读:
    改造MFC程序,使原来不支持winsocket的工程支持winsocket
    算术移位和逻辑移位实现分析
    MFC 编辑框中字体大小改变,行高不能改变,只能显示一半的问题,已解决。
    在MFC中,使用控制台Console输出调试信息
    在MFC中使用GDI+的一般方法,以VC6.0编译器为例
    WinForm 实现主程序(exe)与其关联类库(*.dll)分开存放
    Deserializing/Serializing SOAP Messages in C#
    List分页
    ConvertJavaMiliSecondToDateTime
    中文数字大小写转阿拉伯数字
  • 原文地址:https://www.cnblogs.com/zhongguiyao/p/11049447.html
Copyright © 2020-2023  润新知