文章目录
目录
[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的可靠性、有序性和高效性,成为互联网数据传输的基石。
序列化和反序列化是网络通信的"翻译官",解决了数据在异构环境中的标准化传输 和语义一致性问题,是分布式系统、微服务、实时通信等技术的基础支撑。