守护进程原理及Python实现

守护进程原理及Python实现

守护进程,不依赖于终端,在后台运行的程序,通常称为daemon(ˈdiːmənˈdeɪmən)。

一些常见的Linux软件通常都是已守护进程的方式运行,比如:

nginx

redis

memcached

 

守护进程的原理:

通过fork() 复刻出子进程,并通过setsid()创建新会话,成为会话首领;同时结束原来的父进程,使得复刻出来的子进程脱离终端而运行。

 

守护进程Python代码实现:

 

 

核心函数说明:

os.fork(): 对进程进行复刻;值得特别注意的是fork之后,原来的进程并没有终止,而是继续存在,被成为父进程;也就是说,在fork成功后,一共会存在2个进程,1个是原来的进程,称为父进程,1个是新创建的进程,称为子进程。父进程和子进程都会从fork的位置开始继续向下执行,不同的是父进程中,得到的fork返回值为子进程的进程号,而子进程中得到的是0。通过这个返回值,就能判断哪个是父进程,哪个是子进程。以上这点值得特别注意,这与我们以往理解的程序执行逻辑完全不同。

os.setsid():创建新的会话,并成为会话首领。

os.chdir():修改当前工作目录路径,防止目录被移除导致守护进程异常。

os.umask():设置文件创建模式屏蔽字,使得创建文件不受系统默认权限的影响。

 

常见问题:

1.第1次fork子进程已经脱离终端,为什么还要第2次fork,第2次fork是否必须?

第2次fork并不是必须的,实际上,很多流行的开源软件的守护进程并没有进行第2次fork。第2次fork的目的在于防止第1次fork出来的进程再次获得终端,第2次fork后,产生的孙子进程不再是会话首领,也就没有再次获得终端的能力。

来看看Redis是如何实现守护进程的:redis/server.c at cb51bb4320d2240001e8fc4a522d59fb28259703 · antirez/redis · GitHub

 

2.进程已经脱离终端,如何让它停止或者重启?

每一个进程都有一个进程id,即pid,通常程序启动后,会把pid写入到/var/run/目录下的某个文件里,通过发送信号量给pid,即可操作相关进程。示例代码中的“进程注册信号处理方式”就是用来响应信号量的,守护进程可以针对不同的信号,做出不同的反应。