会话和守护进程

1 概念

进程组,也称之为作业。 BSD 于 1980 年前后向 Unix 中增加的一个新特性。代表一个或多个进程的集合。每个进程都属于一个进程组。在 waitpid 函数和 kill 函数的参数中都曾使用到。操作系统设计的进程组的概念,是为了简化对多个进程的管理。

当父进程,创建子进程的时候,默认子进程与父进程属于同一进程组。

进程组 ID==第一个进程 ID(组长进程)。所以,组长进程标识:其进程组 ID==其进程 ID 。

可以使用 kill -SIGKILL -进程组 ID(负的)来将整个进程组内的进程全部杀死 。

组长进程可以创建一个进程组,创建该进程组中的进程,然后终止。只要进程组中有一个进程存在,进程组就存在,与组长进程是否终止无关。
进程组生存期:进程组创建到最后一个进程离开(终止或转移到另一个进程组) 。
一个进程可以为自己或子进程设置进程组 ID 。

1.1 会话

会话:多个进程组的集合。

创建一个会话需要注意以下 6 点注意事项:

1. 调用进程不能是进程组组长,该进程变成新会话首进程(session header)
2. 该进程成为一个新进程组的组长进程。
3. 需有 root 权限 (ubuntu 不需要)
4. 新会话丢弃原有的控制终端,该会话没有控制终端
5. 该调用进程是组长进程,则出错返回
6. 建立新会话时,先调用 fork, 父进程终止,子进程调用 setsid()

getsid函数

获取进程所属的会话 ID

pid_t getsid(pid_t pid); 
// 成功:返回调用进程的会话 ID;失败: -1,设置 errno
// pid 为 0 表示察看当前进程 session ID

ps ajx 命令查看系统中的进程。
参数 a 表示不仅列当前用户的进程,也列出所有其他用户的进程;
参数 x 表示不仅列有控制终端的进程,也列出所有无控制终端的进程;
参数 j 表示列出与作业控制相关的信息。

setsid函数

创建一个会话,并以自己的 ID 设置进程组 ID,同时也是新会话的 ID。

pid_t setsid(void); 
// 成功:返回调用进程的会话 ID;失败: -1,设置 errno

 三合一:gid、sid、pid

测试

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <signal.h>
#include <sys/wait.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 = %d\n", getpid());        // 进程id
        printf("Group ID of child GID = %d\n", getpgid(0));  // 进程组id
        printf("session ID of child SID = %d\n", getsid(0)); // 会话id
        sleep(10);

        setsid(); // 子进程不是组长进程,可以成为新会话首进程,且成为组长进程,该进程组长id即为会话
        printf("Change:\n");
        printf("child process PID = %d\n", getpid());        // 进程id
        printf("Group ID of child GID = %d\n", getpgid(0));  // 进程组id
        printf("session ID of child SID = %d\n", getsid(0)); // 会话id
        sleep(20);
        exit(0);
    }

    return 0;
}

执行

2 守护进程

2.1 什么是守护进程

1 在linux系统中,我们会发现在系统启动的时候有很多的进程就已经开始跑了,也称为服务,这也是我们所说的守护进程。
2 守护进程(daemon)是生存期长的一种进程,没有控制终端。
3 它们常常在系统引导装入时启动,仅在系统关闭时才终止。
4 UNIX系统有很多守护进程,守护进程程序的名称通常以字母“d”结尾:例如,syslogd 就是指管理系统日志的守护进程

5 通过ps进程查看器 ps -efj 的输出实例,内核守护进程的名字出现在方括号中,大致输出如下:

守护进程:

daemon进程。通常运行于操作系统后台,脱离控制终端。一般不与用户直接交互。周期性的等待某个事件发生或周期性执行某一动作。
不受用户登录注销影响。通常采用以d结尾的命名方式。
创建守护进程,最关键的一步是调用setsid函数创建一个新的Session,并成为Session Leader。

2.2 守护进程创建步骤

1. fork子进程,让父进程终止。
2. 子进程调用 setsid() 创建新会话
3. 通常根据需要,改变工作目录位置 chdir(), 防止目录被卸载。(U盘)
4. 通常根据需要,重设umask文件权限掩码,影响新文件的创建权限。 022 -- 755 0345 ---
432 r---wx-w- 422
5. 通常根据需要,关闭/重定向(0/1/2) dev/null 文件描述符
6. 守护进程 业务逻辑。while()

2.3 守护进程代码实现(重点)

主要是理解一些概念。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <fcntl.h>
#include <sys/stat.h>
int main(int argc, char *argv[])
{
    pid_t pid; // 进程
    int res;   // 返回值
    int fd;    // 文件描述符
    pid = fork();
    if (pid > 0)
    { // 父进程
        exit(1);
    }
    else
    { // 子进程
        pid = setsid();
        if (pid == -1)
        {
            perror("setsid error\n");
            exit(1);
        }
        res = chdir("/home/winter/linuxStudy"); // 改变工作目录位置
        if (res == -1)
        {
            perror("chdir error\n");
            exit(1);
        }
        umask(0022);                    // 改变文件访问权限掩码
        close(STDIN_FILENO);            // 关闭文件描述符0
        fd = open("/dev/null", O_RDWR); // 读写权限打开 fd就是0
        if (fd == -1)
        {
            perror("open error\n");
            exit(1);
        }
        dup2(fd, STDOUT_FILENO); // 重定向,将STDOUT_FILENO指向fd
        dup2(fd, STDERR_FILENO);

        // 模拟守护进程业务
        while (1)
            ;
    }

    return 0;
}

执行

这个daemon进程就不会受到用户登录注销影响。要想终止,就必须用kill命令。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值