当前位置: 首页 > article >正文

【守护进程 】【序列化与反序列化】

目录

  • 1. 前后台任务
  • 2. 守护进程
  • 3. TCP的其它概念
  • 4. 序列化与反序列化

1. 前后台任务

我们之前在 信号(一)【概念篇】 介绍过 Crtl + c 的本质就是给前台进程发送一个信号,进程执行该信号的默认处理动作,进而终止进程,随后我们还证明后台进程是无法接收到 Crtl + c 之类的信号的,即只有前台进程才能获得标准输入。

并且,我们曾经是这样说的:“ 一次登录就启动一个终端,一个终端一般配一个bash,每一次登陆,只允许一个进程是前台进程 ”,其中每次登录,linux 系统就会形成一个会话,每一个会话都会创建一个 bash 进程,一个session只能有一个前台进程在运行,而键盘信号只能发送给前台进程。而因为键盘文件只有一个,因此前台进程只能有一个。

[outlier@aliyun process]$ ./test >> log.txt &
[1] 9381
[outlier@aliyun process]$ ./test >> log2.txt &
[2] 9383
[outlier@aliyun process]$ ./test >> log3.txt &
[3] 9384
# 其中的[1] [2] [3] 称为后台任务号
[outlier@aliyun process]$ jobs		# 查看后台任务
[1]   Running                 ./test >> log.txt &
[2]-  Running                 ./test >> log2.txt &
[3]+  Running                 ./test >> log3.txt &
[outlier@aliyun process]$ fg 2	# 将后台进程提升为前台进程
./test >> log2.txt

[outlier@aliyun process]$ fg 3	
./test >> log3.txt
^Z		# 通过暂停进程将前台变为后台
[3]+  Stopped                 ./test >> log3.txt
[outlier@aliyun process]$ jobs
[1]-  Running                 ./test >> log.txt &
[3]+  Stopped                 ./test >> log3.txt

[outlier@aliyun process]$ bg 3		# 将暂停的后台任务重新运行
[3]+ ./test >> log3.txt &
[outlier@aliyun process]$ jobs
[1]-  Running                 ./test >> log.txt &
[3]+  Running                 ./test >> log3.txt &
[outlier@aliyun process]$ ./test >> log.txt &
[1] 9490
[outlier@aliyun process]$ sleep 1000 | sleep 2000 | sleep 300 &
[2] 9493
[outlier@aliyun process]$ ps ajx | head -1 ; ps ajx | grep -Ei 'sleep|test'
 			进程组ID 
 PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
 8465  9490  9490  8465 pts/0     9507 S     1001   0:00 ./test
 8465  9491  9491  8465 pts/0     9507 S     1001   0:00 sleep 1000
 8465  9492  9491  8465 pts/0     9507 S     1001   0:00 sleep 2000
 8465  9493  9491  8465 pts/0     9507 S     1001   0:00 sleep 300

在这里插入图片描述

其中的 session id = bash,多个任务(进程组),只要是在同一个会话中启动的,其 session id 是一样的。

任务与进程组的关系:任务的派发是以进程组为单位进行的。每个任务由指定的一个进程组完成。

因此所谓的前、后台进程,我们称为前台任务、后台任务。

当整个会话退出后,理论上,会话内的后台进程也要退出(比如 windows 的用户注销功能),而在连接远端 linux 时,会话关闭了,后台进程是保留下来的(只要不涉及到标准输出)。

在这里插入图片描述
在这里插入图片描述

现象:即便在会话关闭后,后台进程保留下来了,但依旧受到了用户登录和退出的影响:进程转而被OS领养,没有所谓的终端启动信息。

如果我们想要启动一种不受用户登录和退出影响的进程,即进程守护化。


2. 守护进程

进程是在会话中启动的,因此要使得某个进程不受用户登录和退出影响,那么只需要让指定进程自成一个会话,脱离原本的会话,将来会话退出了,该进程就也不受任何影响(因为它已经不属于原本的会话)。

守护进程:自成进程组 && 自成会话

NAME
      setsid - create session and set process group ID

SYNOPSIS
      #include <unistd.h>

      pid_t setsid(void);  // 使调用进程成为新会话的唯一进程(调用进程不能是一个进程组的组长)

RETURN VALUE
      Upon successful completion, setsid() shall return the value of the new process group ID of the calling process. 
      Otherwise, it shall return (pid_t)-1 and set errno to indicate the error.
// Daemon.hpp

#pragma once
#include <iostream>
#include <string>
#include <cstdlib>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

const std::string nullfile = "/dev/null";

void Daemon(const std::string& cwd = "") 
{
    // 1. 忽略其它异常信号
    signal(SIGCHLD, SIG_IGN);
    signal(SIGSTOP, SIG_IGN);
    signal(SIGPIPE, SIG_IGN);

    // 2. 使进程独立会话
    if(fork() > 0) exit(0);    /* 因为守护进程不能是进程组的leader,所以fork一次,然后父进程直接退出
    setsid();                     因此守护进程的本质也是孤儿进程,只不过孤儿时设置了新的session id   */

    // 3. 更改调用进程的工作目录
    if(!cwd.empty()) chdir(cwd.c_str());

    // 4. 标准输入、输出、错误重定向到 /dev/null
    //    /dev/null 相当于一个垃圾桶,凡是写到这个字符文件的数据都会被丢弃
    //    之所以不直接关闭,原本向标准输入、输出、错误打印的数据就会出错。
    int fd = open(nullfile.c_str(), O_WRONLY);
    if(fd > 0)
    {
        dup2(fd, 0);
        dup2(fd, 1);
        dup2(fd, 2);
        close(fd);
    }
}
// TcpServer.hpp

#include "Daemon.hpp"
class TcpServer
{
	void Start()
    {   
        log.Enable(Classfile);      // 文件日志
        Daemon();   // 进程守护化	
		...
    }
}

具体的TCP通信编码请跳转 tcp_scoket 观阅。

关于守护进程,Linux 也提供了可直接调用的系统接口

NAME
      daemon - run in the background

SYNOPSIS
      #include <unistd.h>
      
      int daemon(int nochdir, int noclose)

参数:
nochdir:是否改变当前工作目录,0 表更改为根目录 /, 非零则保留当前工作目录。
noclose:是否关闭标准输入、标准输出和标准错误,0表关闭,非0表保持。

3. TCP的其它概念

TCP 通信是全双工的,全双工指的是:允许读和写操作同时进行,服务端在往客户端写入数据的同时也能够接收来自客户端的信息。

在这里插入图片描述

TCP 之所以能做到全双工,就是因为底层的发送和接收的缓冲区是独立的,因此即便读和写同时进行,数据也不会互相影响。


4. 序列化与反序列化

  • 在TCP、UDP通信中,我们通常使用 write 发送数据、read 接收数据。但是,当我们从网络中读取数据时,接收到的内容实际上是一个字符串,这就引发了几个关键问题,比如:对方发送数据的速度非常快,可能会连续发送多个报文,但是另一方 read 一次性就读取了多个报文,那么我们该如何确定读取到的数据是一个完整的报文,或者我怎么知道从哪里到哪里是一个报文,之后是一个报文?即如何保证能够正常的协议解析?

    因此,面对这些问题,我们需要在网络通信中引入一些机制,以确保报文的完整性和可解析性,即协议定制。

  • TCP 通信是全双工的,可以同时进行数据发送与接收。但是当用户把数据通过系统调用拷贝到 TCP 的发送缓冲区,接着由TCP 负责由数据发送到对方的缓冲区,而 TCP 是保证可靠性的,可能在网络传输中会有数据丢包等问题,TCP 就需要做数据重传等动作。换言之,TCP是传输控制协议,即,数据从用户层拷贝到了TCP 的缓冲区后,TCP 对数据的一切操作由 TCP 自己来控制(什么时候发数据,发多少,出错了如何处理),上层调用的 write、read等接口,只是将数据从用户拷贝到内核的某个缓冲区而已(相当于用户把数据交给OS处理),用户调用了 write,即便调用完成返回了,但这只是把数据拷贝到 TCP 的发送缓冲区,不一定就已经发送给对方了(这一点就如同我们在介绍用户缓冲区是一致的,用户调用 fprintf 之类的接口将数据写入外设,但函数调用完成后,不代表数据就已经写入到外设中了,数据只是到了内核的缓冲区而已,接着由内核全权负责接下来的工作)。

    数据发送了,总得有人接收吧?但是接收方可并不知道发送方一次发送多少数据啊,用户自己都不知道(因为用户把数据交给 TCP 了,可能用户一次性 write 5个报文,但 TCP 只发送了一个),那接收方就无法得知,自己 read 读取到的数据是完整的一个报文,还是半个,还是多个,每个报文之间的界限在哪里,这些对接收方都是完全不确定的事情。

  • 因此,基于上述问题,我们就需要定制协议:

    比如我们要实现一个网络版计算器,实现 + - × ÷ %,客户端把要计算的两个加数发过去,然后由服务器进行计算,最后再把结果返回给客户端。

    客户端以 struct Request { int a; int b; }; 将数据发送给服务端,服务端以同样的 struct 结构体对数据进行接收,对于 struct Response { int sum; }; 计算结果的返回也是如此,双方都约定好相同的数据类型。

    但是,在应用层一般还要再加上一种机制 ---- 序列化与反序列化,因为结构体有内存对齐的概念,在不同平台的不同编译器下,同样的结构体编译出来的结构体大小可能是不一样的(内存对齐的标准不一样),所以如果直接以结构体类型发送数据,那么客户端和服务端可能发送与接收到的数据大小就不一致,那这还玩个毛。

在这里插入图片描述

下一篇文章 网络版计算器,我们会自定义协议 + 序列化与反序列的具体实现,结合 TCP 网络通信实现一个网络版计算器。


如果感觉该篇文章给你带来了收获,可以 点赞👍 + 收藏⭐️ + 关注➕ 支持一下!

感谢各位观看!


http://www.kler.cn/a/446073.html

相关文章:

  • 网络安全的攻防战争
  • libmodbus安装使用
  • 多屏幕编程时用pygame指定窗口出现在第二块显示器上的方法
  • 【进阶编程】MVC和MVVM实现前后端分离的实现
  • Moretl开箱即用日志采集
  • 微信小程序实现画板画布自由绘制、选择画笔粗细及颜色、记录撤回、画板板擦、清空、写字板、导出绘图、canvas,开箱即用
  • 吉利前端、AI面试
  • 工业大数据分析算法实战-day11
  • opencv sdk for java中提示无stiching模块接口的问题
  • 鸿蒙Next自定义组件的布局
  • 数据结构顺序表和链表
  • 【21天学习AI底层概念】day8 什么是类意识?
  • Linux 下的 GPT 和 MBR 分区表详解
  • Qt Quick:CheckBox 复选框
  • 无人机+自组网+飞手:低空集群飞行技术详解
  • Angular学习路线图
  • skyler实战渗透笔记—Kioptrix-1
  • 【算法】栈
  • 配置TypeScript:tsconfig.json详解
  • Ubuntu上如何部署Nginx?
  • 中国人工智能学会技术白皮书
  • FPGA设计-使用 lspci 和 setpci 调试xilinx的PCIe 问题
  • 【libuv】Fargo信令1:client发connect消息给到server
  • 利用DnslogSqlinj工具DNSlog注入
  • 指令-v-for的key
  • 《 OpenCV 环境搭建》