Linux修仙传
会话和守护进程
1. 进程组和会话概念
1.1 进程组
进程组概念:
进程组,也称之为作业。BSD于1980年前后向Unix中增加的一个新特性。代表一个或多个进程的集合。每个进程都属于一个进程组。在waitpid和kill函数的参数中都曾使用过。操作系统设计的进程组的概念,是为了简化对多个进程的管理
当父进程,创建子进程的时候,默认子进程与父进程属于同一个进程组。进程组ID = 第一个进程ID(组长进程)。所以,组长进程标识,其进程组ID = 其进程ID
kill 命令杀死整个进程组:
kill -9 -进程组ID //杀死整个进程组内的进程全部杀死
进程组的生存期:
组长进程可以创建一个进程组,创建该进程组中的进程,然后终止。只要进程组中有一个进程存在,进程组就存在,与组长进程是否终止无关。
进程组的生存期,进程组创建到最后一个进程离开(终止或转移到另一个进程组)
一个进程可以为自己或子进程设置进程组
1.2 会话
会话:
就是进程组的集合
创建会话:
创建一个会话需要注意以下6点:
- 调用进程不能是进程组组长,该进程变成新会话的首进程
- 该进程成为一个新进程组的组长进程
- 需要root权限 (Ubuntu不需要)
- 新会话丢弃原有的控制终端,该会话没有控制终端
- 如果该进程调用的是组长进程,则出错返回
- 建立新会话时,先调用fork,父进程终止,子进程调用setsid()
ps ajx 查看当前进程会话ID
ps ajx 查看系统中的进程
a:列出所有用户的进程
j:列出与作业控制相关的信息
x:显示 有控制终端的进程 和 没有控制终端的进程
注意:组长进程不能成为新会话的首进程,新会话的首进程必定回成为组长进程
getsid() 获取进程所属的会话ID
#include<sys/types.h>
#include<unistd.h>
pid_t getsid(pid_t pid);
返回值:
成功:返回调用进程的会话ID
失败:-1,设置errno
pid = 0:表示查看当前进程会话ID
setsid() 创建会话
#include<sys/types.h>
#include<unistd.h>
pid_t setsid(void);
返回值:
成功:返回调用进程的会话ID
失败:-1,设置errno
demo 创建会话
- fork 创建子进程,父进程退出
- 打印子进程信息
- setsid 创建会话
- 打印子进程信息
/* session.c */
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
int main(int argc, char * argv [ ])
{
pid_t pid;
if ((pid = fork()) < 0)
{
perror("fork error\n");
exit(1);
}
else if(pid == 0)
{
printf("child process PID is %d\n", getpid());
printf("Group ID of child is %d\n",getpgid(0));
printf("Session ID of child is %d\n", getsid(0));
sleep(10);
setsid(); // 子进程非组长进程,故其成为新会话的首进程(组长进程)
printf("changed:\n");
printf("child process PID is %d\n", getpid());
printf("Group ID of child is %d\n",getpgid(0));
printf("Session ID of child is %d\n", getsid(0));
sleep(20);
exit(0);
}
return 0;
}
2. 守护进程
2.1 守护进程的概念
Daemon(精灵)进程,是Linux中的后台服务进程,通常独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。一般采用以d结尾的名字。
Linux后台的一些系统服务进程,没有控制终端,不能直接和用户交互。不受用户登录、注销的影响,一直在运行着,他们都是守护进程。如:预读入缓输出机制的实现;ftp服务器;nfs服务器等。
创建守护进程最关键的一步是调用setsid函数创建一个新的Session,并成为Session Leader。
2.2 创建守护进程模型
- 创建子进程,父进程退出
所有工作在子进程中进行形式上脱离了控制终端 - 在子进程中创建新会话
setsid() 函数
使子进程完全独立出来,脱离控制 - 根据需要,改变当前目录位置(防止目录被卸载)
chdir() 函数
防止占用可卸载的文件系统
也可以换成其他路径 - 根据需要,重设文件权限掩码
umask 函数
防止继承的文件创建屏蔽字拒绝某些权限
增加守护进程的灵活性 - 根据需要,关闭/重定向 文件描述符 // 123 或者重定向到 /dev/null
继承的打开文件不会用到,浪费系统资源,无法卸载 - 开始执行守护进程核心工作守护进程退出处理程序模型
修改文件路径
man 2 chdir //change working directory
#include<unistd.h>
int chdir(const char *path);
重设文件掩码
man 2 umask // set file mode creation mask
#include<sys/types.h>
#include<sys/stat.h>
mode_t umask(mode_t mask); // 一直成功,返回原来mask
2.3 demo 守护进程
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<errno.h>
#include<signal.h>
#include<fcntl.h>
#include<sys/stat.h>
void sys_error(const char * str)
{
perror(str);
exit(1);
}
int main(int argc, char * argv [ ])
{
pid_t pid;
int ret,fd;
pid = fork();
if(pid > 0)
{
exit(0); // 父进程终止
}
pid = setsid(); //创建新会话
if (pid == -1)
sys_error("setsid error\n");
ret = chdir("/home/libai"); // 改变工作目录位置
if(ret == -1)
sys_error("chdir error\n");
umask(0022); // 改变文件访问权限掩码
close(STDIN_FILENO); // 关闭文件描述符0
fd = open("/dev/null" , O_RDWR); // fd-->/dev/null
if(fd == -1)
sys_error("open error\n");
dup2(fd, STDOUT_FILENO); // 重定向 stdout 和 stderr
dup2(fd, STDERR_FILENO);
while(1); // 模拟守护进程逻辑
return 0;
}