进程和线程区别、管道和套接字、共享变量、TCP三次握手,是否可以少一次握手、子进程和主进程区别和API——Nodejs

发布于:2025-06-16 ⋅ 阅读:(21) ⋅ 点赞:(0)

首先讲了进程和线程区别

然后讲解 管道和套接字,它是进程间通信的方式

接着讲解共享变量 ,它是线程间通信

最后讲解TCP三次握手,因为套接字使用了TCP协议

一、线程和进程的区别

线程(Thread)和进程(Process)是操作系统中并发执行的两个基本单位。它们都可以执行任务,但作用范围和资源管理方式不同。


🧩 一张对比表快速理解

比较项 进程(Process) 线程(Thread)
基本定义 系统资源分配的最小单位 CPU调度的最小单位(属于进程)
是否独立 相互独立,拥有各自内存空间 同一进程下线程共享内存空间
资源占用 占用资源较多(独立地址空间) 占用资源少(共享进程资源)
通信方式 进程间通信(IPC)复杂(如管道、socket) 线程间通信简单(共享变量)
创建销毁代价 创建和销毁开销大 开销相对小
崩溃影响 一个进程崩溃不会影响其他进程 一个线程崩溃可能影响整个进程
举例 Chrome每个标签页一个进程 Chrome标签页中的多个脚本线程


🔧 举个例子说明

想象你开了一家餐厅:

  • 一个进程就像一个餐厅,彼此独立。

  • 一个线程就像餐厅里的服务员(共享厨房、菜单、收银台)。

你开的多个餐厅之间(进程)是互不影响的,但一个餐厅中的多个服务员(线程)可以一起服务,但他们共享资源,配合更紧密。


📦 编程角度理解

  • 在 Java、Python、Node.js 中,我们可以通过 thread, child_process, worker_threads 创建线程或进程。

Node.js 中示例

// 创建子进程
const { fork } = require('child_process');
fork('child.js');

// 创建线程(Node.js 10+)
const { Worker } = require('worker_threads');
new Worker('./worker.js');

📌 总结记忆口诀

“进程是房子,线程是房间”
一个进程里可以有多个线程,但线程不能脱离进程单独存在。


如果你在学 Node.js、Java 或 Python 并发模型,我可以分别帮你分析线程/进程的用法和适用场景。需要我进一步展开哪门语言下的实践案例?

二、进程间通信中管道是什么

在操作系统中,管道(Pipe)是一种常见的进程间通信(IPC, Inter-Process Communication)机制,允许一个进程把数据发送给另一个进程,就像水流通过水管一样。


🧱 一句话解释

管道就是一个连接两个进程的通信通道,一个进程写入数据,另一个进程读取数据。


🧠 管道的原理

  • 管道本质上是一个内核缓冲区(FIFO 队列)

  • 写端写入数据 → 内核缓冲 → 读端读取数据

  • 半双工通信(数据只能单向流动)。


📦 管道的类型

类型 说明
匿名管道 父子进程间通信(只能在具有亲缘关系的进程间使用)
命名管道 FIFO 允许无亲缘关系的进程通信,有名称(文件路径),位于文件系统中


🔧 举例(Node.js)

匿名管道(spawn 使用标准输入输出)

const { spawn } = require('child_process');

const child = spawn('node', ['-e', `process.stdin.pipe(process.stdout)`]);

child.stdin.write('Hello from parent!\n');
child.stdin.end();
child.stdout.on('data', data => {
  console.log(`Child says: ${data}`);
});

命名管道(Linux Bash 示例)

# 创建命名管道
mkfifo mypipe

# 一个终端:向管道写入
echo "Hello" > mypipe

# 另一个终端:从管道读取
cat < mypipe

🚦 特点和限制

  • ✅ 进程间数据共享,快速。

  • ✅ 管道通信不需要借助中间文件。

  • ❌ 匿名管道只能用于父子进程。

  • ❌ 半双工通信(除非建立两个管道)。

  • ❌ 不能跨网络使用(仅限本地进程)。


✅ 常见应用场景

  • Shell 中的命令管道(ls | grep txt

  • 父子进程间传递数据(如 Node.js 中子进程通信)

  • 轻量级、无锁通信场景


💬 总结口诀

匿名管道亲缘深,命名管道通陌生;写端数据流向读,半双工最常见。


如果你想知道在 Java、C/C++、Python 中怎么实现管道通信,也可以举例说明。是否需要我给出跨语言的管道实现?

三、进程间通信socket是什么

在操作系统中,Socket(套接字)是最强大、最灵活的进程间通信(IPC)机制之一,它不仅能实现本地通信,还能支持网络通信(跨主机)


🧠 一句话解释

Socket 就是一种通信端点,用于两个进程之间发送和接收数据,无论这两个进程是在同一台机器上,还是在不同主机上。


🏗️ 工作原理

Socket 通信的基本模型是 客户端-服务器(Client-Server)模型

  1. 服务器进程监听某个地址(IP + 端口)。

  2. 客户端进程主动连接这个地址。

  3. 双方建立连接后就可以双向通信(全双工)。


📦 套接字类型(按通信范围)

类型 范围 示例说明
本地套接字 同一台机器上通信 Unix Domain Socket(文件路径)
网络套接字 跨主机通信 TCP/IP、UDP 套接字


🔧 Node.js 中的 Socket 通信示例

1. 本地通信(UNIX Socket)

// server.js
const net = require('net');
const server = net.createServer((socket) => {
  socket.on('data', (data) => {
    console.log('Server received:', data.toString());
  });
});
server.listen('/tmp/mysocket');  // 本地 socket 文件
// client.js
const net = require('net');
const client = net.createConnection('/tmp/mysocket', () => {
  client.write('Hello from client');
});

2. 网络通信(TCP Socket)

// tcp-server.js
const net = require('net');
const server = net.createServer((socket) => {
  console.log('Client connected');
  socket.on('data', (data) => {
    console.log('Server received:', data.toString());
    socket.write('pong');
  });
});
server.listen(3000);
// tcp-client.js
const net = require('net');
const client = net.createConnection(3000, 'localhost', () => {
  client.write('ping');
});
client.on('data', (data) => {
  console.log('Client received:', data.toString());
});

🚦 特点

特性 描述
📡 通用性强 本地通信、远程通信都可用
🔁 全双工 可以双向同时读写
📁 文件描述符 在类 UNIX 系统中 Socket 是一种特殊的文件描述符
🔐 可配加密 可以集成 TLS/SSL 实现安全通信(HTTPS、WSS 等)
🧱 适合复杂通信 比如多客户端并发、长连接、大数据传输等


🆚 与其他通信方式对比

通信方式 是否跨主机 是否双向 应用难度 性能 常见用途
管道 pipe ❌ 本地 🚫 半双工 简单 父子进程简单数据传输
消息队列 ❌ 本地 多进程异步通信
Socket ✅ 本地+远程 ✅ 全双工 ✅ 复杂 中-高 服务端、网络通信
共享内存 ❌ 本地 复杂 ⚡ 极快 高性能数据交换


🧪 Socket 常用场景

  • 本地服务与客户端通信(如数据库客户端)

  • 网络服务(Web Server、Chat Server)

  • 微服务间通信(配合 TCP/HTTP)

  • 实时推送(WebSocket)


✅ 总结口诀

Socket 是个洞,进程互打通。可远程可本地,网络通信最通用。


如果你想继续了解:

  • UDP socketTCP socket 区别

  • WebSocket 和原始 socket 的差异

  • socket.io 使用实例(前后端实时通信)

可以告诉我,我可以一并讲清楚。

四、TCP三次握手

我们从原理到实战场景全面讲清楚 TCP 三次握手(Three-way Handshake),并且深入探讨你提到的重点问题:“是否可以省略第三次握手?


🔍 一、什么是 TCP 三次握手?

三次握手是建立 TCP 连接时客户端和服务器之间必须进行的 三步通信流程,确保双方都具备发送和接收能力,才能正式开始数据传输。


🧠 二、三次握手详解(图 + 文)


lua

复制编辑

客户端 服务端 | ------ SYN -------> | (第一次握手:客户端发起请求) | <----- SYN-ACK -----| (第二次握手:服务端确认并回应) | ------ ACK ------->| (第三次握手:客户端确认)

✅ 第一次握手(客户端 → 服务端)

  • 客户端发送一个带 SYN 标志的数据包。

  • 表示:“我要连接你,并且我能发送数据了。”

  • 此时客户端进入 SYN_SENT 状态。

✅ 第二次握手(服务端 → 客户端)

  • 服务端收到 SYN 后,确认客户端能发送。

  • 回复一个带 SYN+ACK 标志的包。

  • 表示:“我知道你要来,我也可以发送数据。”

  • 服务端进入 SYN_RECEIVED 状态。

✅ 第三次握手(客户端 → 服务端)

  • 客户端收到 SYN+ACK 后,再发一个 ACK

  • 表示:“我知道你也准备好了,我们开始吧!”

  • 此时双方进入 ESTABLISHED 状态,连接建立成功。


📌 三次握手的目的总结

目的 举例
✅ 确保客户端能发送 第一次握手
✅ 确保服务端能接收并能发送 第二次握手
✅ 确保客户端能接收 第三次握手


❓是否可以省略第三次握手?

不能省略。

原因:

  1. 如果省略,服务端无法确认客户端“收到了”自己的 SYN+ACK

  2. 网络不稳定或中间设备重发,可能导致服务端误以为连接成功,出现半连接(SYN Flood 漏洞)

  3. TCP 协议设计就是为了防止这种“单向认为建立连接”的问题。


🧪 示例场景:省略第三次握手会怎么样?

  • 客户端发了 SYN,服务端发了 SYN+ACK,但客户端没有发 ACK 就宕机了

  • 如果不等待第三次握手:

    • 服务端误以为连接建立。

    • 客户端其实已经没了。

    • 服务端一直占用连接资源,白白浪费(资源泄漏)。


🧱 补充:SYN Flood 攻击

攻击者连续伪造 IP 发送 SYN 包,但不发送第三次 ACK。

  • 服务端进入 SYN_RECEIVED,等待 ACK 却等不到,连接被挂起。

  • 持续攻击会让服务端连接数爆满,无法为正常用户服务。

解决方案:

  • 使用 SYN Cookie 技术。

  • 连接队列限制。


✅ 总结口诀

三次握手不多余,确认彼此都OK。缺一不可防欺骗,少了连接不安全。


如果你还想进一步了解:

  • 四次挥手断开连接机制

  • TCP 状态转换图(如 TIME_WAIT, CLOSE_WAIT

  • 和 HTTP 的握手区别(如 HTTP/1.1 vs HTTP/2)

我可以继续为你展开。

五、子进程和主进程区别和API

在 Node.js 中,子进程(child process)用于执行一个独立的任务或运行另一个程序,与主进程并行执行、互不干扰。这是 Node 在单线程模型中实现多任务处理(并发/并行)的常用手段。


🧠 一、主进程 vs 子进程 的区别

对比项 主进程 子进程
启动方式 node 直接启动 主进程通过 child_process 模块创建
内存空间 独立进程,拥有自己的内存空间 也是独立的,和主进程不共享内存(需显式通信)
执行上下文 当前运行的 Node.js 文件和上下文 可运行任意 Node.js 文件或外部命令
是否共享事件循环 ❌ 不共享事件循环 每个子进程有自己的事件循环
通信方式 - 通过 IPC(进程间通信)或标准输入输出
错误影响 主进程出错影响全局 子进程崩溃不影响主进程,反之亦然


🚀 二、创建子进程的 4 个核心 API(child_process 模块)

你需要通过:

const child_process = require('child_process');

进行调用。


1️⃣ exec(command[, options], callback)

  • 用于执行一个命令行命令,适合处理结果较小的任务。

  • 会将子进程的输出(stdout 和 stderr)缓存在内存中,传给回调函数。

const { exec } = require('child_process');

exec('ls -la', (err, stdout, stderr) => {
  if (err) {
    console.error('错误:', err);
    return;
  }
  console.log('标准输出:', stdout);
});

2️⃣ execFile(file[, args][, options][, callback])

  • 直接执行一个可执行文件(比如 .exe, .sh, .py),不会通过 shell 解析

  • 安全性更高、性能更好,适用于直接调用程序文件

const { execFile } = require('child_process');

execFile('node', ['--version'], (err, stdout) => {
  if (err) throw err;
  console.log('Node 版本是:', stdout);
});

3️⃣ spawn(command[, args][, options])

  • 更底层,适合**处理大数据流(持续输出)**的子进程。

  • 返回一个 ChildProcess 对象,你可以用 stdout.on('data') 来获取数据。

const { spawn } = require('child_process');

const child = spawn('ping', ['baidu.com']);

child.stdout.on('data', (data) => {
  console.log(`输出:${data}`);
});

child.stderr.on('data', (data) => {
  console.error(`错误:${data}`);
});

child.on('close', (code) => {
  console.log(`子进程退出码:${code}`);
});

4️⃣ fork(modulePath[, args][, options])

✅ 专为 Node.js 脚本间的多进程通信而设计,默认通过 IPC 通信(process.send()message 事件)。

// parent.js
const { fork } = require('child_process');
const child = fork('./child.js');

child.send({ msg: 'Hello from parent!' });

child.on('message', (msg) => {
  console.log('父进程接收:', msg);
});
// child.js
process.on('message', (msg) => {
  console.log('子进程接收:', msg);
  process.send({ reply: 'Got it!' });
});

✅ 三、适用场景对比总结

API 适合场景 是否支持通信 是否缓冲输出
exec 简单命令 + 小输出结果 ✅(内存缓存)
execFile 运行本地程序、脚本
spawn 处理持续输出(如视频转码) ❌(流式处理)
fork Node 之间多进程通信


🔧 四、如何终止子进程?

使用:

child.kill(); // 默认发送 SIGTERM 信号

📦 五、补充推荐阅读

  • 官方文档:https://nodejs.org/api/child_process.html

  • Node.js进程模型分析文章

  • Node.js高级编程书籍中的“多进程架构”


需要我结合实战:比如用子进程处理图片压缩、日志分析、CPU 密集计算等例子吗?或者再带你写一个完整的 fork 通信项目也可以。


网站公告

今日签到

点亮在社区的每一天
去签到