运行的这个进程,它的pid和gpid(进程组ID)一样,它是自成一组的。
这就是一个进程组。
进程组和任务有什么关系?
将任务指派给进程组。任务都是由进程组去完成的。
可以发现,这三个进程的会话id1351都是一样的,多个任务(进程组),在同一个sesion内启动的sid是一样的。
当后台任务在执行的时候,我将用户退出,后台任务就会受到退出的影响。
如果不想受到用户登录和注销的影响---守护进程话
当前用户在登录的时候,让其中的某个进程组自成一个会话,这个会话不需要和键盘显示器什么的关联。这种自称进程组会话的进程,叫守护进程。
此时再把用户退出,这个进程不会受到任何影响。
setsid 是一个Linux系统调用,用于创建一个新的会话(session)。
这个新的会话通常是用于创建守护进程(daemon)的。
#include <unistd.h>pid_t setsid(void);
(进程组的组长不能独立成一个会话)
第1步:fork子进程,父进程退出子进程继承了父进程的进程组id,但具有一个新的进程id,这样就保证了子进程不是一个进程组的组长id,这对于下面要做的setsid函数的调用是必要的前提条件
第2步:子进程调用 setsid函数创建新会话调用这个函数以后该进程成为新会话的首进程,是会话的会长成为一个新进程组的组长进程,是进程组组长不受控制终端的影响
第3步:改变当前工作目录chdir如:a.0ut在u盘上,启动这个程序,这个程序的当前的工作目录就是这个u盘,如果u盘拔掉后进程的当前工作目录将消失,a.out将不能正常工作。
第4步:重设文件掩码 mode & umask子进程会继承父进程的抢码 中,增加子进程程序操作的灵活性umask(0000);
第5步:关闭文件描述符守护进程术受控制终端的影响所以可以关闭,以释放资源
close(stdin_fileno);
close(stdout_fileno);
close(stderr_fileno);
第6步:执行核心工作守护进程的核心代码逻辑
345部不是必须的
守护进程一般以d结尾命名
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
#include <time.h>
#include <sys/time.h>
#include <fcntl.h>void myfunc(int signo)
{int fd = open("./session.txt", O_RDWR | O_CREAT | O_APPEND, 0755);if (fd < 0){return;}// 打开文件成功后获取当前时间time_t t;time(&t);char *p = ctime(&t);write(fd, p, strlen(p));close(fd);return;
}int main()
{// 1.创建子进程pid_t id = fork();if (id != 0)exit(0);// 2.子进程调用setsid函数创建会话setsid();// 3.改变工作目录(可选)// chdir();// 4.文件掩码(可选)// umask(0000);// 5.关闭标准输入,标准输出,标准错误文件描述符// close(STDIN_FILENO);// close(STDOUT_FILENO);// close(STDERR_FILENO);// 也可以将这三个文件重定向到黑洞文件 /dev/null 中。这个文件通常用于丢弃数据int fd = open("/dev/null", O_CREAT | O_RDWR | O_APPEND, 0775);if (fd < 0){exit(-1);}dup2(fd, 0);dup2(fd, 1);dup2(fd, 2);close(fd);struct sigaction act;act.sa_flags = 0;act.sa_handler = myfunc;sigemptyset(&act.sa_mask);sigaction(SIGALRM, &act, nullptr);struct itimerval tm;tm.it_interval.tv_sec = 2;tm.it_interval.tv_usec = 0;tm.it_value.tv_sec = 3;tm.it_value.tv_usec = 0;setitimer(ITIMER_REAL, &tm, NULL);while (1){sleep(1);}return 0;
}
程序运行之后,直接去后台了。PPID为1,PID PGID SID都一样,自成进程组,自成会话。
下面我将云服务器关了。一会登录查看日志文件。
用户的退出或者登录该任务没有任何影响。
Linux提供了守护进程的系统调用接口
daemon 函数用于将当前进程转变为守护进程(daemon)。
#include <unistd.h>
int daemon(int nochdir, int noclose);
nochdir:如果 nochdir 不为0,表示不改变当前工作目录为根目录(/);如果为0,则会将当前工作目录更改为根目录。
noclose:如果 noclose 不为0,表示不关闭标准输入、标准输出和标准错误文件描述符;如果为0,则会关闭这些文件描述符。
daemon 函数的返回值为0表示成功,-1表示失败。