守护进程「建议收藏」

守护进程「建议收藏」[toc]终端在UNIX系统中,用户通过终端登录系统后得到一个Shell进程,这个终端成为Shell进程的控制终端(ControllingTerminal),进程中,控制终端是保存在PC


Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺

终端

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

Alt + Ctrl + F1、F2、F3、F4、F5、F6–>字符终端
Alt + F7–>图形终端
SSH、Telnet–>网络终端, pts (pseudo terminal slave)指伪终端

终端启动流程

每个进程都可以通过一个特殊的设备文件/dev/tty访问它的控制终端. 事实上每个终端设备都对应一个不同的设备文件, /dev/tty提供了一个通用的接口, 一个进程要访问它的控制终端既可以通过/dev/tty也可以通过该终端设备所对应的设备文件来访问. ttyname函数可以由文件描述符查出对应的文件名, 该文件描述符必须指向一个终端设备而不能是任意文件

简单来说,一个Linux系统启动,大致经历如下的步骤: init --> fork --> exec --> getty -->用户输入帐号 --> login -->输入密码 --> exec --> bash

硬件驱动程序负责读写实际的硬件设备, 比如从键盘读入字符和把字符输出到显示器, 线路规程像一个过滤器, 对于某些特殊字符并不是让它直接通过, 而是做特殊处理, 比如在键盘上按下Ctrl-z, 对应的字符并不会被用户程序的read读到, 而是被线路规程截获, 解释成SIGTSTP信号发给前台进程, 通常会使该进程停止. 线路规程应该过滤哪些字符和做哪些特殊处理是可以配置的

ttyname与ttyname_r

char *ttyname(int fd);
由文件描述符查出对应的文件名

int ttyname_r(int fd, char *buf, size_t buflen);
与上述相同

#include <unistd.h>
#include <stdio.h>
int main(void)
{
    char p[64];
    ttyname_r(0, p, sizeof(p));
    printf("%s\n", p);
    printf("fd 0: %s\n", ttyname(0));
    printf("fd 1: %s\n", ttyname(1));
    printf("fd 2: %s\n", ttyname(2));
    return 0;
}	

/*
/dev/pts/1
fd 0: /dev/pts/1
fd 1: /dev/pts/1
fd 2: /dev/pts/1
*/

进程组

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

当父进程, 创建子进程的时候, 默认子进程与父进程属于同一进程组. 进程组ID第一个进程ID(组长进程). 所以, 组长进程标识: 其进程组ID其进程ID

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

组长进程可以创建一个进程组, 创建该进程组中的进程, 然后终止. 只要进程组中有一个进程存在, 进程组就存在, 与组长进程是否终止无关

进程组生存期: 进程组创建到最后一个进程离开(终止或转移到另一个进程组)

一个进程可以为自己或子进程设置进程组ID

总结

进程组
  进程的组长
    组里边的第一进程
    进程组的ID–> 进程组的组长的ID
  进程组组长的选择
    进程中的第一个进程
  进程组ID的设定
    进程组的id就是组长的进程ID

基础API

getpgrp

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

getpgid

pid_t getpgid(pid_t pid);
如果pid = 0, 那么该函数作用和getpgrp一样。
获取指定进程的进程组ID

setpgid

int setpgid(pid_t pid, pid_t pgid);
改变进程默认所属的进程组. 通常可用来加入一个现有的进程组或创建一个新进程组
注意:
(1)如改变子进程为新的组, 应fork后, exec前。
(2)权级问题. 非root进程只能改变自己创建的子进程, 或有权限操作的进程

会话

会话: 多个进程组

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

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

基础API

getsid

pid_t getsid(pid_t pid);
获取进程所属的会话ID

成功:返回调用进程的会话ID;失败:-1,设置errno

pid为0表示察看当前进程session ID

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

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

setsid

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

守护进程

Daemon(精灵)进程, 是Linux中的后台服务进程, 通常独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件. 一般采用以d结尾的名字

Linux后台的一些系统服务进程, 没有控制终端, 不能直接和用户交互. 不受用户登录和注销的影响, 一直在运行着, 他们都是守护进程. 如: 预读入缓输出机制的实现; ftp服务器; nfs服务器等

创建守护进程,最关键的一步是调用setsid函数创建一个新的Session,并成为Session Leader。

守护进程的特点
  后台服务程序
  独立于终端控制
  周期性执行某任务
  不受用户登陆注销影响
  一般采用以d结尾的名字(服务)

创建守护进程模型

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

示例程序

函数使用

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <signal.h>

int main(int argc, const char * argv[]) {
    // 创建一个会话
    // 将子进程变为会长
    pid_t pid = fork();
    if (pid > 0) {
        exit(1);
        kill(getpid(), SIGKILL);
        raise(SIGKILL);
        abort();
    }
    else if (pid == 0) {
        // 变为会长
        // 会长变为守护进程
        setsid();
        while (1);
    }
    return 0;
}

练习

写一个守护进程, 每隔2s获取一次系统时间, 将这个时间写入到磁盘文件
思路:
  创建守护进程
  需要一个定时器, 2s触发一次, setitimer
  信号捕捉

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <signal.h>
#include <sys/time.h>
#include <time.h>
#include <fcntl.h>  

void dowork(int nu) {
    // 等到当前时间
    time_t curtime;
    time(&curtime);
    // 格式化时间
    char *p = ctime(&curtime);
    // 将时间写入文件
    int fd = open("/home/zyb/time.txt", O_CREAT | O_WRONLY | O_APPEND, 0664);
    write(fd, p, strlen(p) + 1);  // 加入\0
    close(fd);
}

int main(int argc, const char * argv[]) {
    pid_t pid = fork();
    if (pid > 0) {
        exit(1);
        kill(getpid(), SIGKILL);
        raise(SIGKILL);
        abort();
    }
    else if (pid == 0) {
        // 变为会长, 脱离控制终端, 变为守护进程
        setsid();
        // 改变工作目录
        chdir("/home/zyb");
        // 重设文件掩码
        umask(0);
        // 关闭文件描述符
        close(STDIN_FILENO);
        close(STDOUT_FILENO);
        close(STDERR_FILENO);
        // 执行核心操作
        // 注册信号捕捉
        struct sigaction act;
        act.sa_flags = 0;
        act.sa_handler = dowork;
        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 = 1;
        val.it_interval.tv_usec = 0;
        setitimer(ITIMER_REAL, &val, NULL);
        // 保证子进程处于运行状态
        while (1);
    }
    return 0;
}
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/166839.html原文链接:https://javaforall.net

赞 (0)
全栈程序员-站长的头像全栈程序员-站长

相关推荐

  • 什么是莫兰指数

    什么是莫兰指数什么是莫兰指数?根据百度百科的定义是“空间自相关系数的一种,其值分布在[-1,1],用于判别空间是否存在自相关。”简单的说就是判定一定范围内的空间实体相互之间是否存在相关关系,比如:一座座居民楼它们是聚集在一块还是离散分布在各处。莫兰指数数值分布在[-1,1],[0,1]说明各地理实体之间存在正相关的关系,[-1,0]之间说明存在负相关的关系,而0值则无相关…

    2022年6月25日
  • lua中的weak table及内存回收collectgarbage

    弱表(weaktable)是一个很有意思的东西,像C++/Java等语言是没有的。弱表的定义是:Aweaktableisatablewhoseelementsareweakreferences,元素为弱引用的表就叫弱表。有弱引用那么也就有强引用,有引用那么也就有非引用。我们先要厘这些基本概念:变量、值、类型、对象。(1)变量与值:Lua是一个dynamicallyty

    2022年4月7日
  • 【Java】Red5服务器搭建(实现在线直播,流媒体视频播放)「建议收藏」

    引言流媒体文件是目前非常流行的网络媒体格式之一,这种文件允许用户一边下载一边播放,从而大大减少了用户等待播放的时间。另外通过网络播放流媒体文件时,文件本身不会在本地磁盘中存储,这样就节省了大量的磁盘空间开销。正是这些优点,使得流媒体文件被广泛应用于网络播放。流媒体服务器是通过建立发布点来发布流媒体内容和管理用户连接的。流媒体服务器能够发布从视频采集卡或摄像机等设备中传来的实况流,也可以发布事先…

    2022年4月18日
  • linux vim查看下一页,Linux下vi和vim模式相互切换「建议收藏」

    linux vim查看下一页,Linux下vi和vim模式相互切换「建议收藏」vi和vim常用的三种模式:1,正常模式在这种模式下,可以使用【上下左右】按键来移动光标,也可使用【删除字符】【删除整行】来处理档案内容,也可使用【复制、粘贴】来处理文件数据。2,插入模式/编辑模式按下i,I,o,O,a,A,r,R等任一一个字母之后就会进入到编辑模式,一般来说按i即可。3,命令行模式在这种模式下,可以提供相关指令,完成读取、存盘、替换、离开vim、显示行号等动作。下图为v…

    2022年6月2日
  • mysql中的union和order by、limit

    mysql中的union和order by、limit

    2021年10月15日
  • matlab中示波器的用法_示波器单次触发设置

    matlab中示波器的用法_示波器单次触发设置在做Simulink仿真时,使用的Scope波形显示模块实际上也是一种Figure窗口,不过Matlab把Scope的菜单栏隐藏起来,只提供了几个有限的参数设置。如果需要对Scope中的图加上坐标、更改界面背景色等,没有菜单栏就基本上无从下手了。可以在打开你的mdl文件之后,在Matlab的命令行输入以下指令来恢复显示Scope的Figure菜单栏:>>set(0,’ShowHidd…

    2022年10月12日

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号