后台进程
使用命令建立后台进程
程序运行命令+& 可使程序进入后台运行。
后台进程无法接受终端的标准输入,但标准输出依然可输出至终端。
由于不接受终端输入所以无法用Ctrl+C终止进程,可使用kill命令
使用daemon API建立后台进程
程序调用该函数后将进入后台运行
#include <unistd.h>
int daemon(int nochdir,int noclose);
nochdir非零时,将工作目录转移至根目录
noclose非零时,将标准输入输出和错误重定向至/dev/null
如何使后台进程在终端关闭后继续运行
若程序未对SIGHUP信号做处理,在终端关闭时即使运行在后台的进程也会被终结(关于信号可见浅析Linux下常见信号(Signal))
可使用nohup命令让进程忽略SIGHUP信号,使得在终端关闭后进程仍可运行
此时进程的标准输出将被舍弃
nohup ./sigtest &
守护进程
守护进程的概念是后台进程的延伸,即使终端关闭后,后台进程与终端同属于一个进程组和会话当中,并且和终端还有着许多联系,要想彻底独立出来,需要额外步骤。而这种独立出来的,脱离终端的进程可称之为守护进程。
关于进程,进程组,会话之间的关系,参考:https://blog.youkuaiyun.com/yh1548503342/article/details/41891047
使用fork()建立守护进程
1.fork一个子进程,令父进程退出,此时子进程被init接管,成为孤儿进程
2.清除mask,子进程会继承shell的umask,如果不清除的话,会导致子进程创建文件时屏蔽某些权限
3.关闭从父进程继承来的不必要的文件描述符。(可选)
4.调用setsid()建立新的进程会话(最好在这里再次fork。这样使得daemon进程不再是会话首进程,那么永远没有机会获得控制终端。如果这里不fork的话,会话首进程依然可能打开控制终端。)
5.将当前工作目录切换到需要的目录,父进程继承过来的当前目录可能mount在一个文件系统上,如果不切换到根目录,那么这个文件系统不允许unmount。
6.将标准输出,输入,错误重定向至需要的地方
示例
/**
* 注释1:因为我们从shell创建的daemon子进程,所以daemon子进程会继承shell的umask,如果不清除的话,会导致daemon进程创建文件时屏蔽某些权限。
* 注释2:fork后让父进程退出,子进程获得新的pid,肯定不为进程组组长,这是setsid前提。
* 注释3:调用setsid来创建新的进程会话。这使得daemon进程成为会话首进程,脱离和terminal的关联。
* 注释4:最好在这里再次fork。这样使得daemon进程不再是会话首进程,那么永远没有机会获得控制终端。如果这里不fork的话,会话首进程依然可能打开控制终端。
* 注释5:将当前工作目录切换到根目录。父进程继承过来的当前目录可能mount在一个文件系统上,如果不切换到根目录,那么这个文件系统不允许unmount。
* 注释6:在子进程中关闭从父进程中继承过来的那些不需要的文件描述符。可以通过_SC_OPEN_MAX来判断最高文件描述符(不是很必须).
* 注释7:打开/dev/null复制到0,1,2,因为dameon进程已经和terminal脱离了,所以需要重新定向标准输入,标准输出和标准错误(不是很必须).
*/
void daemonize(const char *cmd){
int i, fd0, fd1, fd2;
pid_t pid;
struct rlimit rl;
struct sigaction sa;
/* * Clear file creation mask. */
umask(0);//注释1
/* * Get maximum number of file descriptors. */
if (getrlimit(RLIMIT_NOFILE, &rl) < 0)
err_quit("%s: can't get file limit", cmd);
/* * Become a session leader to lose controlling TTY. */
if ((pid = fork()) < 0) {//注释2
printf("%s: can't fork", cmd);
exit(-1);
}
else if (pid != 0) /* parent */
exit(0);
setsid();//注释3
/* * Ensure future opens won't allocate controlling TTYs. */
sa.sa_handler = SIG_IGN;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
if (sigaction(SIGHUP, &sa, NULL) < 0)
err_quit("%s: can't ignore SIGHUP", cmd);
if ((pid = fork()) < 0)//注释4
err_quit("%s: can't fork", cmd);
else if (pid != 0) //parent
exit(0);
/* * Change the current working directory to the root so * we won't prevent file systems from being unmounted. */
if (chdir("/") < 0) {//注释5
printf("%s: can't change directory to /", cmd);
exit(-1);
}
/* * Close all open file descriptors. */
if (rl.rlim_max == RLIM_INFINITY)
rl.rlim_max = 1024;
for (i = 0; i < rl.rlim_max; i++)
close(i);//注释6
/* * Attach file descriptors 0, 1, and 2 to /dev/null. */
fd0 = open("/dev/null", O_RDWR);//注释7
fd1 = dup(0);//注释7
fd2 = dup(0);//注释7
dup2(fd0, STDIN_FILENO);
dup2(fd0, STDOUT_FILENO);
dup2(fd0, STDERR_FILENO);
/* * Initialize the log file. */
openlog(cmd, LOG_CONS, LOG_DAEMON);
if (fd0 != 0 || fd1 != 1 || fd2 != 2) {
syslog(LOG_ERR, "unexpected file descriptors %d %d %d",fd0, fd1, fd2);
exit(1);
}
}
以上示例来自imooc 李超