浅析Linux下的后台进程和守护进程

后台进程

使用命令建立后台进程
程序运行命令+& 可使程序进入后台运行。
后台进程无法接受终端的标准输入,但标准输出依然可输出至终端。
在这里插入图片描述
由于不接受终端输入所以无法用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.csdn.net/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 李超

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值