首页 > 系统相关 >Linux多进程21-守护进程

Linux多进程21-守护进程

时间:2023-05-17 18:59:05浏览次数:34  
标签:21 pid 会话 终端 Linux 进程 include 守护

终端

  • 在 UNIX 系统中,用户通过终端登录系统后得到一个 shell 进程,这个终端成为 shell 进程的控制终端(Controlling Terminal),进程中,控制终端是保存在 PCB 中的信息,而 fork() 会复制 PCB 中的信息,因此由 shell 进程启动的其它进程的控制终端也是这个终端。
  • 默认情况下(没有重定向),每个进程的标准输入、标准输出和标准错误输出都指向控制终端,进程从标准输入读也就是读用户的键盘输入,进程往标准输出或标准错误输出写也就是输出到显示器上。
  • 在控制终端输入一些特殊的控制键可以给前台进程发信号,例如 Ctrl + C 会产生 SIGINT 信号,Ctrl + \ 会产生 SIGQUIT 信号。

记住两个指令

$tty
/dev/pts/0
$echo $$
1620

进程组

  • 进程组和会话在进程之间形成了一种两级层次关系:进程组是一组相关进程的集合,会话是一组相关进程组的集合。进程组和会话是为支持 shell 作业控制而定义的抽象概念,用户通过 shell 能够交互式地在前台或后台运行命令。
  • 进程组由一个或多个共享同一进程组标识符(PGID)的进程组成。一个进程组拥有一个进程组首进程,该进程是创建该组的进程,其进程 ID 为该进程组的 ID,新进程会继承其父进程所属的进程组 ID。
  • 进程组拥有一个生命周期,其开始时间为首进程创建组的时刻,结束时间为最后一个成员进程退出组的时刻。一个进程可能会因为终止而退出进程组,也可能会因为加入了另外一个进程组而退出进程组。进程组首进程无需是最后一个离开进程组的成员。

会话

  • 会话是一组进程组的集合。会话首进程是创建该新会话的进程,其进程 ID 会成为会话 ID。新进程会继承其父进程的会话 ID。
  • 一个会话中的所有进程共享单个控制终端。控制终端会在会话首进程首次打开一个终端设备时被建立。一个终端最多可能会成为一个会话的控制终端。
  • 在任一时刻,会话中的其中一个进程组会成为终端的前台进程组,其他进程组会成为后台进程组。只有前台进程组中的进程才能从控制终端中读取输入。当用户在控制终端中输入终端字符生成信号后,该信号会被发送到前台进程组中的所有成员。
  • 当控制终端的连接建立起来之后,会话首进程会成为该终端的控制进程。

进程组/会话/控制终端之间的关系

find / 2 > /dev/null | wc -l &
sort < longlist | uniq -c 同一时间只能有一个前台进程组

image

相关函数

◼ pid_t getpgrp(void); 获取进程的组
◼ pid_t getpgid(pid_t pid); 获取指定进程的进程组
◼ int setpgid(pid_t pid, pid_t pgid); 设置进程组ID
◼ pid_t getsid(pid_t pid); 获取指定进程的会话ID
◼ pid_t setsid(void); 设置会话的ID

守护进程

  • 守护进程(Daemon Process),也就是通常说的 Daemon 进程(精灵进程),是Linux 中的后台服务进程。它是一个生存期较长的进程,通常独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。一般采用以 d 结尾的名字。
  • 守护进程具备下列特征:
    • 生命周期很长,守护进程会在系统启动的时候被创建并一直运行直至系统被关闭。
    • 它在后台运行并且不拥有控制终端。没有控制终端确保了内核永远不会为守护进程自动生成任何控制信号以及终端相关的信号(如 SIGINT、SIGQUIT)。
    • Linux 的大多数服务器就是用守护进程实现的。比如,Internet 服务器 inetd,Web 服务器 httpd 等。

守护进程的创建步骤

◼ 执行一个 fork(),之后父进程退出,子进程继续执行。
◼ 子进程调用 setsid() 开启一个新会话。
◼ 清除进程的 umask 以确保当守护进程创建文件和目录时拥有所需的权限。
◼ 修改进程的当前工作目录,通常会改为根目录(/)。
◼ 关闭守护进程从其父进程继承而来的所有打开着的文件描述符。
◼ 在关闭了文件描述符0、1、2之后,守护进程通常会打开/dev/null 并使用dup2()使所有这些描述符指向这个设备。
◼ 核心业务逻辑

实例

daemon.c

/*
守护进程
每隔2S获取系统时间, 将这个时间写入磁盘文件中
*/

#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/time.h>
#include <signal.h>
#include <time.h>
#include <stdlib.h>
#include <string.h>
void work(int num)
{
    //捕捉到信号后获取系统时间, 写入磁盘文件
    time_t tm = time(NULL);

    //转换格式
    struct tm *loc = localtime(&tm);
    // char buf[1024];
    // sprintf(buf, "%d-%d-%d %d:%d:%d\n", loc->tm_year, loc->tm_mon, loc->tm_mday,
    // loc->tm_hour, loc->tm_min, loc->tm_sec);
    // printf("%s\n", buf);
    char *str = asctime(loc);
    int fd = open("time.txt", O_RDWR | O_CREAT | O_APPEND, 0664);
    write(fd, str, strlen(str));
    close(fd);
}

int main(int argc, char const *argv[])
{
    //创建子进程, 退出父进程
    pid_t pid = fork();
    if (pid > 0)
        exit(0);

    //将子进程重新创建一个会话
    setsid();

    //设置掩码
    umask(022);

    //更改工作目录
    chdir("/home/anqwjoe/learn");

    //关闭 重定向文件描述符
    int fd = open("/dev/null", O_RDWR);
    dup2(fd, STDIN_FILENO);
    dup2(fd, STDOUT_FILENO);
    dup2(fd, STDERR_FILENO);

    //业务逻辑

    //捕捉定时信号
    struct sigaction act;
    act.sa_flags = 0;
    act.sa_handler = work;
    sigemptyset(&act.sa_mask);
    sigaction(SIGALRM, &act, NULL);

    //设置定时器
    struct itimerval val;
    val.it_value.tv_sec = 2;
    val.it_value.tv_usec = 0;
    val.it_interval.tv_sec = 2;
    val.it_interval.tv_usec = 2;

    //创建定时器
    setitimer(ITIMER_REAL, &val, NULL);

    //不让进程结束
    while (1)
    {
        sleep(10);
    }

    return 0;
}

运行

Thu Dec  8 15:16:17 2022
Thu Dec  8 15:16:19 2022
Thu Dec  8 15:16:21 2022
Thu Dec  8 15:16:23 2022
...
...

标签:21,pid,会话,终端,Linux,进程,include,守护
From: https://www.cnblogs.com/anqwjoe/p/17409759.html

相关文章

  • Linux多进程02-进程状态转换
    进程状态三态模型:就绪态、运行态、阻塞态五态模型:新建态、就绪态、运行态、阻塞态、终止态新建态:进程刚被创建,未进入就绪队列就绪态:进程具备运行条件,等待系统分配处理器以便运行。可能会有多个,排成一个队列。运行态:进程占有处理器正在运行阻塞态:进程不具备运行条件,等待某......
  • Linux多进程04-父子进程虚拟地址空间
    调用fork函数后,相当于复制一份地址空间出来,父子进程代码段(.text)是相同的,但栈空间是没有关系的,它们依据各自栈空间的值执行各自的判断逻辑在内核区中,父进程和子进程的pid是不一样的父子进程的关系:fork()函数的返回值不同pcb(progresscontrolblock进程控制块)中......
  • Linux多进程06-进程退出、孤儿进程、僵尸进程
    进程退出#include<stdlib.h>voidexit(intstatus);#include<unistd.h>void_exit(intstatus);/*#include<stdlib.h>voidexit(intstatus);#include<unistd.h>void_exit(intstatus);status参数:进程退出时一个状态信息,父进程回收子进程资源时可以获得*......
  • Linux多进程05-exec函数族
    execl:执行文件函数#include<unistd.h>intexecl(constchar*pathname,constchar*arg,...); 执行参数path字符串所代表的文件路径参数:-path:需要指定的执行的文件的路径或者名称(推荐使用绝对路径)-arg:是执行可......
  • Linux多进程07-wait和waitpid
    进程回收在每个进程退出的时候,内核释放该进程所有的资源、包括打开的文件、占用的内存等。但是仍然为其保留一定的信息,这些信息主要主要指进程控制块PCB的信息(包括进程号、退出状态、运行时间等)。父进程可以通过调用wait或waitpid得到它的退出状态同时彻底清除掉这个进程。......
  • Linux多进程09-匿名管道实现ps aux
    /*实现psaux|grepxxx父子进程间通信子进程:psaux,子进程结束后将数据发送给父进程父进程:获取到数据,guolvpipe()execlp()子进程将标准输出stdout_fileno重定向到管道的写端dup2()*/#include<stdio.h>#include<stdlib.......
  • Linux多进程08-进程间通信与管道
    进程间通信进程是一个独立的资源分配单元,不同进程(这里所说的进程通常指的是用户进程)之间的资源是独立的,没有关联,不能在一个进程中直接访问另一个进程的资源。不同的进程需要进行信息的交互和状态传递(如:数据传输/通知事件/资源共享/进程控制),因此需要进程间通信(IPC:Int......
  • Linux多进程11-内存映射
    内存映射(Memory-mappedI/O)是将磁盘文件的数据映射到内存,用户通过修改内存就能修改磁盘文件。mmap#include<sys/mman.h>void*mmap(void*addr,size_tlength,intprot,intflags,intfd,off_toffset);功能:将一个文件或者设备的数据映射到内存中参数:......
  • Linux多进程10-有名管道实现简单版聊天功能
    chatA.c//有名管道实现简单版聊天功能#include<unistd.h>#include<stdio.h>#include<sys/types.h>#include<sys/stat.h>#include<stdlib.h>#include<fcntl.h>#include<string.h>intmain(intargc,charconst*argv[]){......
  • Linux多进程13-kill,raise,abort函数
    #include<sys/types.h>#include<signal.h>intkill(pid_tpid,intsig);-功能:给某个进程pid,发送某个信号sig-参数:-pid:>0:将信号发送给指定的进程=0:将信号发送给当前的进程组=-1:将信号发送给每一个......