在前面一篇“用Python给Linux编写守护进程”的文章中,讲到了如何利用python给linux编写一个守护函数。文章讲到了守护进程不依赖用户终端,所以输出的信息用户是察觉不到的,但是有时候我们又需要记录一些信息。例如web服务中,我们可以需要记录访问日志,错误日志等等;在著名的web服务器nginx中,我们可以通过access_log和error_log分别指定访问日志和错误日志的存放位置。
首先要讲到linux系统的标准输入(stdin)标准输出(stdout)和标准错误(stderr): 在Unix和类Unix系统中,如同某些编程语言接口一样,标准流是当一个电脑程序运行时,在它和它的环境间(典型为终端),事先连接的输入和输出频道。这三个I/O链接称作“标准输入”、“标准输出”和“标准错误输出”。(1)标准输入,标准输出和标准错误的文件描述符分别为0,1,2。在正常进程中,这三个标准流通常都是指向用户使用的终端号。即进程通过终端获取输入,也通过终端输入正常信息和错误信息。
下面是三个文件描述符的指向信息:
[root@home fd]# ls -la total 0 dr-x------ 2 root root 0 Aug 3 14:52 . dr-xr-xr-x 7 root root 0 Aug 3 14:52 .. lrwx------ 1 root root 64 Aug 3 14:52 0 -> /dev/pts/0 lrwx------ 1 root root 64 Aug 3 14:52 1 -> /dev/pts/0 lrwx------ 1 root root 64 Aug 3 14:52 2 -> /dev/pts/0
从上述描述中,我们可以知道,只要我们把相应的文件描述符重新绑定给记录文件而不是终端,那么进程输出的信息就自然被记录到文件当中了。Linux中有两个比较常用的函数dup和dup2用来复制文件描述符,用法如下:
int dup(int filedes)
int dup2(int oldfd,int newfd)
先介绍dup,当dup函数执行时,它会把原来的文件状态复制并返回新的文件描述符,而且这个文件描述符将会是系统中最小的文件描述符。利用dup函数的这个特点,可以做到重定向系统的标准输入输出:
#!/usr/bin/env python import os,sys error_log="/var/error.log" access_log="/var/access.log" ehandle=open(error_log,"a") ahandle=open(access_log,"a") os.close(sys.stdout.fileno()) os.close(sys.stderr.fileno()) fdout=os.dup(ahandle.fileno()) fderr=os.dup(ehandle.fileno()) ehandle.close() ahandle.close() if fdout==1: print "Stdin Redirection success!" else: print "error stdin redirection" if fderr==2: print "Stderr Redirection success!" else: print "error stderr redirection"
监控结果,发现进程问的标准输出和标准错误都已经被重定向,print函数的输出的内容已经出现在了access_log当中。
在来说一下dup2函数,从上面的dup函数,我们了解到,系统会自动指定一个没有使用的最小文件描述符给进程。而dup2函数可以让用户自己指定文件描述符,它的作用是复制文件描述符,将newfd描述符所对应的文件表 改成 oldfd所对应的文件表项。之后,newfd与oldfd指向同一个文件表。这样就将newfd重定向到oldfd:
#!/usr/bin/env python import os,sys error_log="/var/error.log" access_log="/var/access.log" ehandle=open(error_log,"a") ahandle=open(access_log,"a") os.dup2(ehandle.fileno(),sys.stderr.fileno()) os.dup2(ahandle.fileno(),sys.stdout.fileno()) ehandle.close() ahandle.close()
上述两个函数均可实现linux下标准流的重定向,dup函数理解起来更加简单,dup2操作起来更加简单。理解和掌握这两个函数,对于理解linux系统编程是非常有帮助的。