终端、进程组、会话、守护进程(包括创建守护进程)详解

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/IT_10/article/details/90167613

终端

输出设备和输入设备的总称为终端。Unix中的terminal是伪终端,输入和输出都流经这个terminal。Unix系统中,用户通过终端登录系统后得到一个shell进程,这个终端成为shell进程的控制终端。默认情况下(没有重定向),每个进程的标准输入,标准输出和标准错误输出都指向控制终端。

网络终端

向XShell这样的连接到远程Unix系统的软件成为网络终端,网络终端工作流程如下图。tty命令可以查看当前所处的网络终端号。
在这里插入图片描述

进程组

进程组也称之为作业,代表一个或多个进程的集合,为了方便管理多个进程。
进程组生存期:进程组创建到最后一个进程离开(终止或转移到另一个进程组)。
一个进程可以为自己或子进程设置进程组ID。
getpgrp函数可以获取当前进程组ID
getpgid函数可以获取指定进程的进程组ID
setpgid函数可以改变自己或子进程默认所属的进程组

会话

进程组的集合成为会话
创建一个会话必须注意一下五点:
(1)用来创建会话的进程不能是进程组组长(该调用进程是组长进程,则出错返回),并且调用的进程变为新会话首领
(2)用来创建会话的进程成为一个新进程组的组长进程
(3)需要root权限(ubuntu不需要)
(4)新会话丢弃原有的控制终端,该会话没有控制终端
(5)建立新会话时,先调用fork函数,父进程终止,子进程调用setsid
getsid函数用来获取进程所属的会话ID
setsid函数用来创建一个会话,并以自己的ID设置进程组ID,同时也是新会话的ID,即调用了setsid函数的进程,即是新的会长,也是新的组长
创建会话实例


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

int main(void){
    pid_t pid;

    if(pid = fork() < 0){
        perror("fork error:");
        exit(1);
    }else if(pid == 0){
        printf("child process PID is %d\n",getpid());
        printf("group ID of the child is %d\n",getpgid(0));
        printf("session ID of the child is %d\n",getsid(0));

        sleep(5);
        setsid();//子进程非组长进程,故其可以创建会话

        printf("changed\n");

        printf("child process PID is %d\n",getpid());
        printf("group ID of the child is %d\n",getpgid(0));
        printf("session ID of the child is %d\n",getsid(0));

        exit(0);
    }else{
        sleep(10);//防止子进程变成孤儿进程
    }
    return 0;
}

在这里插入图片描述

守护进程

Daemon(精灵)进程,是Linux中的后台服务进程,通常独立于控制终端并且周期地执行某任务或等待处理某些发生的事件。一般采用以d结尾的名字,如httpd、vsftpd。
Linux中的一些系统服务进程,没有控制终端、不能和用户交互、不受用户登录注销的影响,一直运行着,是守护进程。
创建守护进程最关键的一步是调用setsid函数创建一个新的会话,并成为session leader。
创建守护进程步骤:
(1)创建子进程,父进程退出
(2)在子进程中创建新会话,调用setsid函数,为了使子进程脱离控制终端而独立
(3)改变当前目录为根目录,chdir函数,为了防止占用可卸载的文件系统,也可以换成其他路径
(4)重设文件权限掩码(默认为0022),umask函数,为了防止继承的文件创建屏蔽字拒绝某些权限,增加进程灵活性
(5)关闭文件描述符,0~2这三个文件描述符随着进程创建而打开,分别指向标准输入,标准输出,标准错误输出,由于该会话需要脱离终端,因此不需要这三个文件描述符。实际过程中不是直接close,因为在使用文件描述符的时候从最小可用的单元找,如果关闭0号,则0号被认为可用,但是0号一般不被其他程序使用,因此一般会把这三个文件描述符重定向到/dev/null黑洞中。
(6)开始执行守护进程核心工作
(7)守护进程退出处理程序模块
创建一个守护进程向指定文件中写入数据实例

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <signal.h>
#include <sys/resource.h>

void printfError(char *s){
    perror(s);
    exit(0);
}
int main(void){
    pid_t pid, sid;
    int ret;
	int i;
    pid = fork(); //第一步
    if(pid < 0){
        printfError("fork error");
    }
    if(pid > 0){
        return 0;
    }

    sid = setsid(); //第二步
    if(sid < 0){
printfError("setsid error");
    }

    ret = chdir("/"); //第三步
    if(ret < 0){
        printfError("chdir error");
    }

    umask(0002); //第四步

	signal(SIGHUP, SIG_IGN);    // 信号处理
    signal(SIGCHLD, SIG_IGN);

    close(STDIN_FILENO);  //第五步
    open("/dev/null",O_RDWR);
    dup2(0,STDOUT_FILENO);
    dup2(0,STDERR_FILENO);

    ret = open("/opt/workspace/linux/log",O_RDWR|O_TRUNC); //第六步
    if(ret == -1){
        printfError("open error");
    }

    pid = fork(); 
     if(pid > 0){
        for (i = 0; i < 60; ++i) {
            write(ret, "Hello World\n", 12);
            sleep(1);
        }
    }
    close(ret);
    return 0;
}

编译运行,使用命令ps -ajx查看进程信息
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/IT_10/article/details/90167613