运行的这个进程,它的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表示失败。