Linux学习总结(十五)进程组,会话,守护进程

进程组

  进程组,也称之为作业,BSD于1980年前后向unix中增加的一个新特性,代表一个或多个进程的集合,每个进程都属于一个进程组,在waitpid函数和kill函数的参数中都曾使用导,操作系统设计的进程组的概念,是为了简化对多个进程的管理。
  当父进程创建子进程的时候,默认子进程与父进程属于同一个进程组,进程组ID=第一个进程ID(组长进程),所以,组长进程ID等于其进程组ID
  可以使用kill -SIGKILL -进程组ID(负的)来将整个进程组内的进程全部杀死。
  组长进程可以创建一个进程组,创建该进程组中的进程,然后终止,只要进程组中有一个进程存在,进程组就存在,与组长进程是否终止无关。
  进程组生命周期:进程组创建到最后一个进程离开(终止或转移到另一个进程组)
  一个进程可以为自己或子进程设置进程组ID
  使用如下命令查看进程组ID相关信息:

ps ajx

在这里插入图片描述

进程组相关函数

获取当前进程的进程组ID
 pid_t getpgrp(void); 总是返回调用者的进程组ID

获取指定进程的进程组ID
 pid_t getpgid(pid_t pid); 成功:0;失败:-1,设置errno
如果pid = 0,那么该函数作用和getpgrp一样。

改变进程默认所属进程组,通常可用来加入一个现有的进程组或创建一个新进程组。
int setpgid(pid_t pid,pid_t pgid);成功0,失败-1
将参1对应的进程,加入参2对应的进程组中。
注意:
1.如果改变子进程为新的组,应fork后,exec前
2.权限问题,非root进程只能改变自己创建的子进程,或者有权限操作的进程。
练习:修改子进程的进程组ID

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(void)
{
    
    
    pid_t pid;

    if ((pid = fork()) < 0) {
    
    
        perror("fork");
        exit(1);
    } else if (pid == 0) {
    
    
        printf("child PID == %d\n",getpid());
        printf("child Group ID == %d\n",getpgid(0)); // 返回组id
        //printf("child Group ID == %d\n",getpgrp()); // 返回组id
        sleep(7);
        printf("----Group ID of child is changed to %d\n",getpgid(0));
        exit(0);

    } else if (pid > 0) {
    
    
        sleep(1);
        setpgid(pid,pid);           //让子进程自立门户,成为进程组组长,以它的pid为进程组id

        sleep(13);
        printf("\n");
        printf("parent PID == %d\n", getpid());
        printf("parent's parent process PID == %d\n", getppid());
        printf("parent Group ID == %d\n", getpgid(0));

        sleep(5);
        setpgid(getpid(),getppid()); // 改变父进程的组id为父进程的父进程
        printf("\n----Group ID of parent is changed to %d\n",getpgid(0));

        while(1);
    }

    return 0;
}

会话

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

getsid函数

获取进程所属的会话ID
pid_t getsid(pid_t pid);成功返回调用进程的会话ID,失败返回-1
pid为0标识查看当前进程会话ID
ps ajx:

  • a:不仅列出当前用户的进程,也列出所有其他用户的进程
  • j:列出与作业控制相关的信息
  • x:不仅列出有控制终端的进程,也列出没有控制终端的进程

组长进程不能成为新会话首进程,新会话首进程必定会成为组长进程

setsid函数

创建一个会话,并以自己的ID设置进程组ID,同时也是新会话ID
pid_t setsid(void);成功返回调用进程的会话ID,失败返回-1
调用了setsid函数的进程,既是新的会长也是新的组长
练习:fork一个子进程,并使其创建一个新会话

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(void)
{
    
    
    pid_t pid;

    if ((pid = fork())<0) {
    
    
        perror("fork");
        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();       //子进程非组长进程,故其成为新会话首进程,且成为组长进程。该进程组id即为会话进程

        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;
}

守护进程

  daemon(精灵)进程,是Linux中的后台服务进程,通常独立于控制中断并且周期性的执行某种任务或等待处理某些发生的事件,一般采用以d结尾的名字。
  Linux后台的一些系统服务进程,没有控制中断,不能直接和用户交互,不接受用户登录和注销的影响,一直在运行着,他们都是守护进程,如:预读入缓输出机制的实现;ftp服务器;nfs服务器等。
  创建守护进程,最关键的一步是调用setsid函数创建一个新的session并成为sission leader.

创建守护进程

1.创建子进程,父进程退出
所有哦工作在子进程中进行形式上脱离了控制终端
2.在子进程中创建新会话
sesid()函数创建新会话,使子进程完全脱离出来,脱离控制
3.改变当前目录为根目录
chdir()函数,防止占用可卸载的文件系统,也可以换成其他路径
4.重设文件权限掩码
umask()函数
防止继承的文件创建屏蔽字拒绝某些权限,增加守护进程灵活性
5.关闭文件描述符
继承的打开文件不会用到,浪费系统资源,无法卸载
6.开始执行守护经常核心工作
7.守护进程退出处理程序模型

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>

void daemonize(void)
{
    
    
    pid_t pid;
    /*
     * * 成为一个新会话的首进程,失去控制终端
     * */
    if ((pid = fork()) < 0) {
    
    
        perror("fork");
        exit(1);
    } else if (pid != 0) /* parent */
        exit(0);
    setsid();
    /*
     * * 改变当前工作目录到/目录下.
     * */
    if (chdir("/") < 0) {
    
    
        perror("chdir");
        exit(1);
    }
    /* 设置umask为0 */
    umask(0);
    /*
     * * 重定向0,1,2文件描述符到 /dev/null,因为已经失去控制终端,再操作0,1,2没有意义.
     * */
    close(0);
    open("/dev/null", O_RDWR);
    dup2(0, 1);
    dup2(0, 2);
}

int main(void)
{
    
    
    daemonize();
    while(1); /* 在此循环中可以实现守护进程的核心工作 */
}

猜你喜欢

转载自blog.csdn.net/bureau123/article/details/112652932