什么是守护进程
守护进程,简单来说就是让进程脱离当前会话,成为孤儿进程且在后台不间断运行
服务器服务进程应该为守护进程
如何成为守护进程?
系统daemon接口:
#include <unistd.h>int daemon(int nochdir, int noclose);
调用daemon的进程本身不能是pregress leader
进程的前后台切换
任何时候,只能一个前台,接收bash 的I/O
进程放到后台
启动进程时,命令末尾加&
对于已经启动的进程 Ctrl + z 暂停进程, 之后使用bg %任务编号
(不知名任务编号默认最近一个, 任务编号可jobs查看)
后台进程切到前台
fg
fd %1 (任务编号可以jobs查看)
进程组/任务
进程组是为了完成一个任务的一组任务
[ps ajs 运行结果与结果参数说明图片]:
PGID:Progress Group ID
SID:Session ID
TTY: TeleTYpewriter
TPGID:Terminal Progress Group ID
TTY(Teletypewriter)指进程所连接的终端设备。
-
如果进程与终端关联,显示终端名称(如
pts/0
、tty1
)。 -
如果进程与终端无关,显示
?
。
会话
我们连接到一个网站时,云服务器会给每个用户创建一个session&bash,这叫做会话。同一个会话拥有相同session(SID)
用户退出时,系统释放会话,可能会影响后台进程组(如提供服务进程)。要使服务进程在用户退出时正常运行,就要将其设置为守护进程
守护进程属于独立会话(和用户不同session/SID),这样server进程不会随用户退出而收到影响(这里的影响不一定使程序直接退出,但是I/O等功能可能异常)
守护进程要脱离终端
其实就是一个孤儿进程
手动实现daemon
手写daemon在很多实际情况中,是有必要的
code
#pragma once#include <iostream>
#include <cstdlib>
#include <signal.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>#define ROOT "/"
#define devnull "/dev/null"void Daemon(bool ischdir, bool isclose)
{// 1. 守护进程一般要屏蔽到特定的异常信号signal(SIGCHLD, SIG_IGN);signal(SIGPIPE, SIG_IGN);// 2. 成为非组长if (fork() > 0)exit(0);// 3. 建立新会话setsid();// 4. 每一个进程都有自己的CWD,是否将当前进程的CWD更改成为 / 根目录if (ischdir)chdir(ROOT);// 5. 已经变成守护进程啦,不需要和用户的输入输出,错误进行关联了if (isclose){::close(0);::close(1);::close(2);}else{int fd = ::open(devnull, O_WRONLY);if (fd > 0){// 各种重定向dup2(fd, 0);dup2(fd, 1);dup2(fd, 2);close(fd);}}
}
tips:
[setid]
使用setid的进程不能是progress leader,解决方法:fork后kill父,使用子来setid
/dev/null 黑洞文件
被读时丢弃所有得到数据
被写时返回空
int fd = open(/dev/null,O_WRONLY)
dup2(fd,0)
dup2(fd,1)
dup2(fd,2)
如果传递图片等二进制文件,应把buffer 由string改为vector<char>,防止二进制数据中出现0导致string直接中断