一、进程组与会话
1. 进程组
进程组,也叫做作业。BSD于1980年前后向Unix中增加的一个新特性,代表一个或者多个进程的集合,每个进程都属于一个进程组。操作系统设计进程组的概念主要就是为了简化对多个进程的管理。
当父进程创建子进程的时候,默认父进程和子进程同属于同一个进程组,进程组的ID等于第一个进程的ID,也就是组长进程的ID。我们在使用kill杀死进程的时候,可以通过 kill -SIGKILL -进程组ID 来杀死整个进程组内的全部进程。组长进程可以创建一个进程组,只要进程组中有一个进程存在,那么这个进程组就存在,并且即使组长进程终止也不会影响进程组。进程组的生命周期时从进程组创建到进程组最后一个进程终止或转移到另一个进程组。而进程可以为自己或者子进程设置进程组ID。
2. 会话
创建会话的注意事项:
- 调用进程不能是进程组组长(父进程不能创建会话),该进程变成新会话首进程session header;
- 该进程成为一个新的进程组的组长进程,也就是说如果子进程创建了一个会话,那么子进程就脱离父进程的进程组,成为一个新进程的进程组组长;
- 新会话丢弃原有的控制终端,该会话没有控制终端;
- 该调用进程是组长进程,则出错返回,也就是说组长不能当会长;
- 建立新会话时,先调用fork,父进程会终止,子进程调用setsid,也就是说,只有父进程终止了,子进程才能创建会话;
3. 总结
进程组:多个进程在同一个组,第一个进程默认是进程组组长。
会话:进程组的上一级,多个进程组对应一个会话。
创建会话的时候,不能使用进程组组长创建,必须使用组员创建。
创建会话主要包括创建子进程、结束父进程、子进程做会长三步。
4. setsid()和getsid()函数
4.1 setsid()函数
- 包含头文件及函数原型
#include <unistd.h>
pid_t setsid(void);
- 函数描述 setsid() creates a new session if the calling process is not a process group leader. The calling process is the leader of the new session, the process group leader of the new process group, and has no controlling tty. The process group ID and session ID of the calling process are set to the PID of the calling process. The calling process will be the only process in this new process group and in this new session.
- 函数参数 void
- 函数返回值 On success, the (new) session ID of the calling process is returned. On error, (pid_t) -1 is returned, and errno is set to indicate the error.
4.2 getsid()函数
- 包含头文件及函数原型
#include <unistd.h>
pid_t getsid(pid_t pid);
- 函数描述 getsid(0) returns the session ID of the calling process. getsid(p) returns the session ID of the process with process ID p. (The session ID of a process is the process group ID of the session leader.)
- 函数参数 pid
- 函数返回值 On success, a session ID is returned. On error, (pid_t) -1 will be returned, and errno is set appropriately.
二、守护进程
1. 什么是守护进程
守护进程,也叫做精灵进程Daemon,是Linux中的后台服务进程,通常独立于控制终端并且周期性的执行某种任务或等待处理某些发生的事件,一般使用d结尾的名字。
Linux后台的一些系统服务进程,没有控制终端,不能直接和用户交互,不受用户登录、注销的影响,一直在运行着,这些就是守护进程,比如,预读入缓输出机制的实现,ftp服务器,nfs服务器等等。而我们运行的普通进程在终端关闭的时候就会随之终止,它实际上是这么实现的,当关闭终端的时候,会给所有进程发送一个1号信号SIGHUP,进程收到这个信号就会进行相应动作。创建一个守护进程最关键的一步是调用setsid函数创建一个新的session,并成为session leader。
通过 ps aux 可以查看进程是否有依靠的终端,?表示不依赖终端,红色标出的表示依赖这个终端。
2. 如何创建守护进程模型
- ① 创建子进程,父进程退出 所有工作在子进程中进行,形式上脱离了控制终端。
- ② 在子进程中创建会话 使用setsid()函数创建会话,使子进程完全独立出来,脱离控制(脱离终端)。
- ③ 改变当前目录为根目录 使用chdir()函数改变目录为根目录,防止占用可卸载的文件系统,也可以换成其他路径(最好是家目录)。这一步使非必须的,目的是使进程和某个目录脱离联系,不然的话可能会影响到出于其他原因而对该目录的删除等需求。
- ④ 重设文件权限掩码 使用umask()函数重设文件掩码,即可以防止继承的文件创建屏蔽字来拒绝某些权限,也可以增加守护进程的灵活性。一般设置为0002或0022,实际上每个进程都有自己的掩码,比如我们在shell下输入命令umask就可以看到shell进程的掩码,这个掩码决定了在shell进程下创建文件或目录时。文件所拥有的默认权限。
- ⑤ 关闭文件描述符 继承的打开文件不会用到,浪费系统资源,无法卸载。这一步也是非必要的,主要是考虑资源的合理利用。
- ⑥ 开始执行守护进程的核心工作
- ⑦ 守护进程退出处理程序模型(非必要)
3. 守护进程编程实战
案例:创建一个守护进程,每经过30秒在$HOME/log/目录下创建一个文件,并命名为 file.time
代码语言:javascript复制/************************************************************
>File Name : daemon_test.c
>Author : Mindtechnist
>Company : Mindtechnist
>Create Time: 2022年05月24日 星期二 22时10分06秒
************************************************************/
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/time.h>
#include <time.h>
#include <string.h>
#include <stdlib.h>
#define FILE_NAME "%s/log/date.%ld"
void mkfile(int signo)
{
char* homedir = getenv("HOME");
char filename[256] = {0};
sprintf(filename, FILE_NAME, homedir, time(NULL)/*当前时间戳*/);
int fd = open(filename, O_RDWR | O_CREAT, 0644);
if(fd < 0)
{
perror("open err");
_exit(1); /*守护进程也会退出*/
}
close(fd);
}
int main(int argc, char* argv[])
{
/*创建子进程,父进程退出*/
pid_t pid = fork();
if(pid > 0)
{
_exit(1);
}
/*成为会长*/
setsid();
/*设置掩码*/
umask(0);
/*切换目录*/
chdir(getenv("HOME"));
/*关闭文件描述符*/
close(0);
close(1);
close(2);
/*执行核心逻辑*/
struct itimerval mit = {{30, 0}, {30, 0}};
setitimer(ITIMER_REAL, &mit, NULL);
struct sigaction mact;
mact.sa_flags = 0;
sigemptyset(&mact.sa_mask);
mact.sa_handler = mkfile;
sigaction(SIGALRM, &mact, NULL);
while(1)
{
sleep(1);
}
/*退出*/
return 0;
}
4. nohup与&后台执行
- nohup:表示忽略1号信号SIGHUP,这个信号是杀死进程的,当终端或shell退出时,发送给所有进程。 SIGHUP 1 Term Hangup detected on controlling terminalor death of controlling process
- &:表示后台运行程序。
一般我们后台执行程序时是这样用的
代码语言:javascript复制nohup ./a.out > file.log &
这样运行的程序就是一个守护进程,也可以达到上面那样创建守护进程的效果。