在之前的树莓派网关项目中遇到了这样一个问题,由于要把网关写的Server持续运行,尤其是要加电自动开启。发现ssh登录开启服务程序之后,当把pty退出时Server端自动断开了,这里想到的APUE中第九章的内容,回顾了下关于会话首进程,进程组,控制终端的概念,所以我们需要把自己写的Server端变为父进程为init(1)的守护进程。
--->首先想到的办法是使用nohup命令,这里遇到一个坑:
当我们用nohup去处理shell脚本时是没有问题的,但是在尝试执行一个Python脚本时:
nohup python tcp_server.py > ser_log.out 2>&1 &
结果很出了问题:竟然查不到重定向的ser_log.out的输出!后来发现:python的输出有缓冲,导致ser_log.out并不能够马上看到输出。
我们应该加 -u参数,使得python不启用缓冲,如下:
nohup python -u tcp_server.py > ser_log.out 2>&1 &
#!/usr/bin/env python # coding:utf-8 import os,sys,time def daemon_init(stdin='/dev/null',stdout='/dev/null',stderr='/dev/null'): sys.stdin = open(stdin,'r') sys.stdout = open(stdout,'a+') sys.stderr = open(stderr,'a+') try: pid = os.fork() if pid > 0: #parrent os._exit(0) except OSError,e: sys.stderr.write("first fork failed!!"+e.strerror) os._exit(1) # 子进程, 由于父进程已经退出,所以子进程变为孤儿进程,由init收养 '''setsid使子进程成为新的会话首进程,和进程组的组长,与原来的进程组、控制终端和登录会话脱离。''' os.setsid() '''防止在类似于临时挂载的文件系统下运行,例如/mnt文件夹下,这样守护进程一旦运行,临时挂载的文件系统就无法卸载了,这里我们推荐把当前工作目录切换到根目录下''' os.chdir("/") '''设置用户创建文件的默认权限,设置的是权限“补码”,这里将文件权限掩码设为0,使得用户创建的文件具有最大的权限。否则,默认权限是从父进程继承得来的''' os.umask(0) try: pid = os.fork() #第二次进行fork,为了防止会话首进程意外获得控制终端 if pid > 0: os._exit(0) #父进程退出 except OSError,e: sys.stderr.write("second fork failed!!"+e.strerror) os._exit(1) # 孙进程 # for i in range(3,64): # 关闭所有可能打开的不需要的文件,UNP中这样处理,但是发现在python中实现不需要。 # os.close(i) sys.stdout.write("Daemon has been created! with pid: %d " % os.getpid()) sys.stdout.flush() #由于这里我们使用的是标准IO,回顾APUE第五章,这里应该是行缓冲或全缓冲,因此要调用flush,从内存中刷入日志文件。 def main(): print '========main function start!============' #在调用daemon_init函数前是可以使用print到标准输出的,调用之后就要用把提示信息通过stdout发送到日志系统中了 daemon_init('/dev/null','/tmp/daemon.log','/tmp/daemon.err') # 调用之后,你的程序已经成为了一个守护进程,可以执行自己的程序入口了 time.sleep(10) #daemon化自己的程序之后,sleep 10秒,模拟阻塞 if __name__ == '__main__': main()
这样,通过调用daemon_init方法,就可以把自己的程序变为守护进程了,实现了nohup的功能。这样做就更加灵活了,之后的事情就要具体问题具体分析了,比如针对自己实际的应用程序来决定是否要设置umask,是否要关闭不必要的文件描述符,是否要改变当前工作目录等等。