文章目录
目录
[1. 前台进程 (Foreground Process)](#1. 前台进程 (Foreground Process))
[2. 后台进程 (Background Process)](#2. 后台进程 (Background Process))
[3. 前后台进程的切换](#3. 前后台进程的切换)
[4. 关键命令和操作](#4. 关键命令和操作)
[5. 注意事项](#5. 注意事项)
[6. 信号处理](#6. 信号处理)
[1. 守护进程的核心特点](#1. 守护进程的核心特点)
[2. 常见守护进程示例](#2. 常见守护进程示例)
[1、 fork()](#1、 fork())
[2. setsid()](#2. setsid())
[3. chdir()](#3. chdir())
[4. umask()](#4. umask())
[5. close()](#5. close())
[4. 如何创建守护进程?](#4. 如何创建守护进程?)
[5. 管理守护进程的工具](#5. 管理守护进程的工具)
[6. 守护进程 vs 前后台进程](#6. 守护进程 vs 前后台进程)
[7. 注意事项](#7. 注意事项)
[1. 三次握手(Three-Way Handshake)](#1. 三次握手(Three-Way Handshake))
[2. 四次挥手(Four-Way Handshake)](#2. 四次挥手(Four-Way Handshake))
[3. 全双工通信(Full-Duplex)](#3. 全双工通信(Full-Duplex))
前言
一、前后台进程
1. 前台进程 (Foreground Process)
- 
定义:默认情况下,用户在终端启动的进程会占用终端输入/输出,称为前台进程。
 - 
特点:
- 
进程运行时,终端被"锁定",用户必须等待进程结束才能输入新命令。
 - 
进程的输出直接显示在终端,用户可以通过键盘输入与进程交互(如输入参数或终止信号)。
 
 - 
 - 
示例:
bash# 启动一个前台进程(例如 ping 命令) ping example.com 
2. 后台进程 (Background Process)
- 
定义:进程在后台运行,不占用终端输入/输出,用户可继续使用终端执行其他命令。
 - 
特点:
- 
终端不会被阻塞,用户可以继续输入命令。
 - 
进程的输出仍可能显示在终端(可能干扰当前操作),建议重定向输出到文件。
 - 
后台进程的生存期可能与终端会话绑定(若终端关闭,后台进程可能被终止,需使用
nohup或disown避免)。 
 - 
 - 
启动方式:
bash# 在命令末尾添加 & 符号 ping example.com & 
3. 前后台进程的切换
- 
将前台进程放到后台:
- 
按下
Ctrl + Z暂停当前前台进程。 - 
输入
bg命令将暂停的进程转为后台运行。 
 - 
 - 
查看后台进程列表:
bashjobs -l - 
将后台进程切换到前台:
bashfg %<job_number> # 例如 fg %1 
4. 关键命令和操作
| 命令 | 作用 | 
|---|---|
command & | 
直接启动后台进程 | 
Ctrl + Z | 
暂停当前前台进程 | 
bg | 
将最近暂停的进程转为后台运行 | 
fg %n | 
将后台进程 n 切换到前台 | 
jobs -l | 
列出所有后台/暂停的进程及其编号 | 
nohup command & | 
启动后台进程,终端关闭后仍存活 | 
disown | 
断开进程与终端的关联 | 
5. 注意事项
- 
输出干扰:后台进程的输出可能混杂在终端中,建议使用输出重定向:
bashcommand > output.log 2>&1 & - 
终端关闭问题:默认情况下,终端退出时会终止所有关联的后台进程。若需持久化运行:
- 
使用
nohup:bashnohup command & - 
使用
disown:bashcommand & disown %1 # 脱离终端关联 - 
使用终端复用工具(如
tmux或screen)。 
 - 
 
6. 信号处理
- 
前台进程可直接接收终端信号(如
Ctrl + C发送SIGINT,Ctrl + Z发送SIGTSTP)。 - 
后台进程默认不接收键盘输入信号,但可能收到终端关闭时的
SIGHUP信号(需通过nohup或disown避免)。 
通过灵活使用前后台进程,可以高效管理终端任务,尤其适合需要长时间运行或并行操作的情景。
在 Linux 中,**守护进程(Daemon Process)**是一种特殊的后台进程,通常用于长期运行的服务或任务(如 Web 服务器、数据库服务等)。它完全脱离终端控制,独立于用户会话,生命周期与系统运行一致。以下是守护进程的特点及示例:
二、守护进程
1. 守护进程的核心特点
- 
脱离终端:不依赖于任何终端,即使启动它的终端关闭,守护进程依然运行。
 - 
无交互界面:不与用户直接交互,通常以静默模式运行。
 - 
长期运行:持续驻留内存,提供系统级服务。
 - 
独立环境:
- 
工作目录通常设置为根目录(
/),避免占用挂载点。 - 
文件权限掩码(
umask)设为0,确保文件操作权限可控。 - 
关闭不必要的文件描述符(如标准输入、输出、错误流)。
 
 - 
 
2. 常见守护进程示例
- 
系统服务:
- 
sshd:SSH 远程登录服务。 - 
nginx/apache:Web 服务器。 - 
cron:定时任务调度服务。 
 - 
 - 
自定义守护进程:
- 
日志监控脚本。
 - 
自动化备份服务。
 
 - 
 
3.接口介绍
1、 fork()
- 功能 :用于创建一个新的进程,这个新进程是调用进程(父进程)的副本,被称为子进程。在调用 
fork()之后,父进程和子进程会从fork()调用处开始继续执行,通过fork()的返回值来区分是父进程还是子进程。 - 返回值 :
- 在父进程中,返回子进程的进程 ID(PID),是一个正整数。
 - 在子进程中,返回 0。
 - 如果出错,返回 -1。
 
 - 示例代码:
 
            
            
              cpp
              
              
            
          
          #include <stdio.h>
#include <unistd.h>
int main() {
    pid_t pid = fork();
    if (pid < 0) {
        perror("fork");
        return 1;
    } else if (pid == 0) {
        // 子进程
        printf("This is child process, PID: %d\n", getpid());
    } else {
        // 父进程
        printf("This is parent process, child PID: %d\n", pid);
    }
    return 0;
}
        2. setsid()
- 功能 :调用 
setsid()的进程会创建一个新的会话,成为新会话的会话首进程,同时也会成为一个新进程组的组长进程,并且脱离原有的控制终端,从而使进程在后台独立运行。 - 返回值 :
- 成功时,返回新会话的会话 ID。
 - 失败时,返回 -1。
 
 - 示例代码:
 
            
            
              cpp
              
              
            
          
          #include <stdio.h>
#include <unistd.h>
int main() {
    pid_t pid = fork();
    if (pid < 0) {
        perror("fork");
        return 1;
    } else if (pid == 0) {
        // 子进程
        pid_t sid = setsid();
        if (sid < 0) {
            perror("setsid");
            return 1;
        }
        printf("New session ID: %d\n", sid);
    }
    return 0;
}
        3. chdir()
- 功能 :用于改变当前进程的工作目录。在创建守护进程时,通常会将工作目录更改为根目录 
/或其他合适的目录,以避免因原工作目录被卸载等情况导致进程出现问题。 - 返回值 :
- 成功时,返回 0。
 - 失败时,返回 -1。
 
 - 示例代码:
 
            
            
              cpp
              
              
            
          
          #include <stdio.h>
#include <unistd.h>
int main() {
    if (chdir("/") == -1) {
        perror("chdir");
        return 1;
    }
    printf("Working directory changed to root.\n");
    return 0;
}
        4. umask()
- 功能:用于设置文件创建掩码,它会影响后续创建文件和目录的默认权限。通过设置合适的掩码,可以确保守护进程创建的文件和目录具有正确的权限。
 - 返回值:返回之前的文件创建掩码。
 - 示例代码:
 
            
            
              cpp
              
              
            
          
          #include <stdio.h>
#include <sys/stat.h>
int main() {
    mode_t old_umask = umask(0);
    printf("Old umask: %o\n", old_umask);
    return 0;
}
        5. close()
- 功能:用于关闭文件描述符。在创建守护进程时,通常会关闭标准输入(文件描述符 0)、标准输出(文件描述符 1)和标准错误输出(文件描述符 2),因为守护进程在后台运行,不需要与终端进行交互。
 - 返回值 :
- 成功时,返回 0。
 - 失败时,返回 -1。
 
 - 示例代码:
 
            
            
              cpp
              
              
            
          
          #include <stdio.h>
#include <unistd.h>
int main() {
    close(0);
    close(1);
    close(2);
    printf("Standard file descriptors closed.\n"); // 这行不会有输出,因为标准输出已关闭
    return 0;
}
        4. 如何创建守护进程?
以下是一个用 Python 编写的简单守护进程示例,实现每隔 5 秒写入日志的功能:
            
            
              cpp
              
              
            
          
          #pragma once
#include <iostream>
#include <cstdlib>
#include <unistd.h>
#include <signal.h>
#include <string>
#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(SIGCLD, SIG_IGN);
    signal(SIGPIPE, SIG_IGN);
    signal(SIGSTOP, SIG_IGN);
    // 2. 将自己变成独立的会话
    if (fork() > 0)
        exit(0);
    setsid();
    // 3. 更改当前调用进程的工作目录
    if (!cwd.empty())
        chdir(cwd.c_str());
    // 4. 标准输入,标准输出,标准错误重定向至/dev/null
    int fd = open(nullfile.c_str(), O_RDWR);
    if(fd > 0)
    {
        dup2(fd, 0);
        dup2(fd, 1);
        dup2(fd, 2);
        close(fd);
    }
}
        步骤解释:
代码中有注释
5. 管理守护进程的工具
- 
systemd(现代 Linux 系统):
bash# 创建服务单元文件(如 /etc/systemd/system/mydaemon.service) [Unit] Description=My Custom Daemon After=network.target [Service] ExecStart=/usr/bin/python3 /path/to/mydaemon.py Restart=always [Install] WantedBy=multi-user.target # 启动并启用守护进程 sudo systemctl start mydaemon sudo systemctl enable mydaemon - 
sysvinit (旧系统):通过
init.d脚本管理。 
6. 守护进程 vs 前后台进程
| 特性 | 前台进程 | 后台进程 | 守护进程 | 
|---|---|---|---|
| 依赖终端 | ✔️ 是(锁定终端) | ❌ 否(但关联终端) | ❌ 完全脱离终端 | 
| 生命周期 | 终端关闭则终止 | 终端关闭可能终止 | 独立运行,与系统共存亡 | 
| 典型用途 | 临时任务 | 临时后台任务 | 系统/网络服务 | 
| 输出交互 | 直接显示在终端 | 可能干扰终端 | 无输出或重定向到日志文件 | 
| 启动方式 | 直接运行 | command & | 
systemd/nohup/自定义 | 
7. 注意事项
- 
日志管理 :守护进程应记录日志(如通过
syslog或自定义文件),方便排查问题。 - 
资源泄漏:长期运行的守护进程需注意内存/文件描述符泄漏。
 - 
信号处理 :需正确处理
SIGTERM、SIGHUP等信号,实现优雅退出或配置重载。 
总结
守护进程是 Linux 系统的"幕后工作者",用于实现核心服务或后台任务。通过脱离终端、配置独立环境,它们能稳定地为系统提供支持。实际开发中,可直接使用 systemd 等工具管理,避免重复造轮子。
三、tcp连接机制
1. 三次握手(Three-Way Handshake)
三次握手用于建立TCP连接,确保双方确认彼此的发送和接收能力正常,并同步初始序列号(Sequence Number)。
步骤:
- 
SYN(同步报文)
- 客户端向服务器发送 
SYN=1报文,并携带初始序列号seq=x,表示请求建立连接。 
 - 客户端向服务器发送 
 - 
SYN-ACK(同步确认报文)
- 服务器收到 
SYN后,回复SYN=1和ACK=1报文,确认客户端的序列号(ack=x+1),并携带自己的初始序列号seq=y。 
 - 服务器收到 
 - 
ACK(确认报文)
- 客户端确认服务器的 
SYN,发送ACK=1报文,ack=y+1,连接正式建立。 
 - 客户端确认服务器的 
 
为什么需要三次握手?
- 
防止历史重复连接的初始化(如旧的延迟
SYN报文被误认为新请求)。 - 
确保双方确认彼此的发送和接收能力正常。
 - 
同步初始序列号,为后续可靠传输奠定基础。
 
2. 四次挥手(Four-Way Handshake)
四次挥手用于安全释放TCP连接,确保双方数据完全传输完毕,并允许双向独立关闭。
步骤:
- 
FIN(终止报文)
- 主动关闭方(如客户端)发送 
FIN=1报文,seq=u,表示不再发送数据,但可接收数据。 
 - 主动关闭方(如客户端)发送 
 - 
ACK(确认报文)
- 被动关闭方(如服务器)回复 
ACK=1,ack=u+1,确认收到FIN。 
 - 被动关闭方(如服务器)回复 
 - 
FIN(终止报文)
- 被动关闭方处理完剩余数据后,发送自己的 
FIN=1报文,seq=v。 
 - 被动关闭方处理完剩余数据后,发送自己的 
 - 
ACK(确认报文)
- 主动关闭方回复 
ACK=1,ack=v+1,连接正式关闭。 
 - 主动关闭方回复 
 
为什么需要四次挥手?
- 
TCP是全双工的,需独立关闭两个方向的数据流。
 - 
被动关闭方可能需要时间处理未发送完的数据,延迟发送自己的
FIN。 
TIME_WAIT状态
主动关闭方在发送最后一个 ACK 后会进入 TIME_WAIT 状态(持续2MSL,即最大报文生存时间),确保:
- 
被动关闭方收到最终的
ACK。 - 
防止旧连接的延迟报文干扰新连接。
 
3. 全双工通信(Full-Duplex)
TCP连接是全双工 的,即通信双方可同时、独立地发送和接收数据,互不影响。
特点:
- 
双向传输:两端均有独立的发送和接收缓冲区。
 - 
并行性:发送数据无需等待对方响应(如:上传文件时仍可接收消息)。
 - 
流量控制:通过滑动窗口机制,独立管理每个方向的数据流速率。
 
示例:
- 
视频通话:双方同时传输音视频数据。
 - 
HTTP/1.1 Keep-Alive:浏览器和服务器复用同一连接处理多个请求。
 
总结
| 机制 | 目的 | 关键点 | 
|---|---|---|
| 三次握手 | 建立可靠的双向连接 | 防历史连接、同步序列号、确认双方能力 | 
| 四次挥手 | 安全释放双向连接 | 独立关闭、处理残留数据、TIME_WAIT防报文丢失 | 
| 全双工通信 | 支持双向同时数据传输 | 独立缓冲区、滑动窗口控制、高效并行通信 | 
这些机制共同保障了TCP的可靠性、有序性和高效性,成为互联网数据传输的基石。
四、网络通信中的序列及反序列
一、序列化的作用
- 
将数据转换为通用格式
- 
在发送端,程序中的复杂数据结构(如对象、数组、字典等)无法直接在网络中传输,必须转换成一种标准化的字节流(如 JSON、XML、Protocol Buffers、二进制等)。
 - 
例如 :一个 Python 字典
{"name": "Alice", "age": 30}会被序列化为 JSON 字符串{"name":"Alice","age":30}或二进制格式。 
 - 
 - 
跨平台/跨语言兼容性
- 不同编程语言(如 Python、Java、C++)对数据结构的实现差异较大,序列化后的通用格式可被任何语言解析,确保异构系统间的通信。
 
 - 
压缩与优化
- 序列化时可通过算法(如 Protocol Buffers、MessagePack)压缩数据体积,减少网络带宽消耗,提升传输效率。
 
 - 
持久化存储
- 序列化后的数据可保存到文件或数据库(如 Redis 缓存对象),便于后续恢复或传输。
 
 
二、反序列化的作用
- 
重建原始数据结构
- 接收端将收到的字节流还原为程序可操作的原生数据结构。例如将 JSON 字符串反序列化为 Java 对象或 Python 字典。
 
 - 
数据校验与安全
- 反序列化时可以对数据进行格式校验(如 JSON Schema),防止恶意构造的数据攻击(如反序列化漏洞)。
 
 - 
支持复杂类型
- 处理嵌套对象、日期、枚举等特殊类型,确保数据完整性(例如将 JSON 中的字符串 
"2023-10-01"转换为Date对象)。 
 - 处理嵌套对象、日期、枚举等特殊类型,确保数据完整性(例如将 JSON 中的字符串 
 
三、应用场景
- 
网络通信协议
- 
HTTP API:客户端发送 JSON/XML 请求,服务端反序列化为对象处理。
 - 
RPC(远程过程调用):如 gRPC 使用 Protocol Buffers 序列化数据。
 - 
消息队列:Kafka、RabbitMQ 传输的消息需序列化。
 
 - 
 - 
分布式系统
- 微服务间通过序列化传递数据(如 Dubbo 使用 Hessian 序列化)。
 
 - 
缓存与存储
- Redis 存储对象时需序列化为字符串或二进制。
 
 
四、常见序列化技术
| 格式/协议 | 特点 | 
|---|---|
| JSON | 人类可读、跨语言支持,但冗余较多,性能较低。 | 
| XML | 标签化结构,支持复杂数据,但体积大。 | 
| Protocol Buffers | 二进制、高效压缩,需预定义 Schema(.proto 文件),适合高性能场景。 | 
| MessagePack | 二进制,比 JSON 更紧凑,无需 Schema。 | 
| Avro | 动态 Schema 支持,常用于大数据系统(如 Hadoop)。 | 
五、安全问题
- 
反序列化漏洞 :攻击者构造恶意序列化数据,触发远程代码执行(如 Java 的
ObjectInputStream漏洞)。 - 
防御措施:使用安全协议(如 TLS)、校验数据签名、限制反序列化类白名单。
 
总结
序列化和反序列化是网络通信的"翻译官",解决了数据在异构环境中的标准化传输 和语义一致性问题,是分布式系统、微服务、实时通信等技术的基础支撑。
总结
在 Linux 中,**守护进程(Daemon Process)**是一种特殊的后台进程,通常用于长期运行的服务或任务(如 Web 服务器、数据库服务等)。它完全脱离终端控制,独立于用户会话,生命周期与系统运行一致。
| 机制 | 目的 | 关键点 | 
|---|---|---|
| 三次握手 | 建立可靠的双向连接 | 防历史连接、同步序列号、确认双方能力 | 
| 四次挥手 | 安全释放双向连接 | 独立关闭、处理残留数据、TIME_WAIT防报文丢失 | 
| 全双工通信 | 支持双向同时数据传输 | 独立缓冲区、滑动窗口控制、高效并行通信 | 
这些机制共同保障了TCP的可靠性、有序性和高效性,成为互联网数据传输的基石。
序列化和反序列化是网络通信的"翻译官",解决了数据在异构环境中的标准化传输 和语义一致性问题,是分布式系统、微服务、实时通信等技术的基础支撑。
