
——进程管隔离,线程管并发,信号管异步通知
从 RTOS 切到 Linux,很多人会下意识把 Linux 里的进程、线程,理解成 RTOS 任务的另一种叫法。 这样理解能帮你入门,但很快会卡住。
因为 Linux 的进程、线程、信号背后,是一套更完整的应用运行模型。进程提供资源和故障边界,线程共享进程资源,信号是一种受限的异步通知机制。这些概念如果混在一起,后面写后台服务、多线程应用、串口服务、网络服务时都会很别扭。
一句话先确认:
进程解决隔离,线程解决并发,信号解决异步通知。
Linux 里的进程,不只是一个正在运行的函数。它更像一个应用的运行容器。
一个进程通常有:
这和 RTOS Task 最大的差别,就是隔离。
典型 MCU RTOS 项目里,多个任务通常共享同一片地址空间。Linux 里每个进程有自己的虚拟地址空间,一个进程崩了,通常不会直接把另一个进程内存踩坏。
所以在 Linux 里,进程天然适合做应用边界。比如一个设备里可以有:
它们之间通过 socket、pipe、共享内存等方式通信,而不是像 RTOS 任务那样随便共享全局变量。
进程也不是“程序文件”的同义词。磁盘上的 ELF 文件是程序,内核把它加载起来,分配地址空间、PID 和资源以后,才形成进程。同一个程序可以同时启动多个进程,每个进程都有自己的运行状态。
线程通常比新建独立进程更容易共享数据。
同一个进程里的多个线程,共享这个进程的大部分资源,比如地址空间、全局变量、堆和文件描述符。它们各自有自己的寄存器现场、栈、线程局部存储和调度状态。
Linux 内核对进程和线程的看法也没有用户态 API 那么泾渭分明:它们都是可调度的 task。我们通常把共享同一地址空间的一组 task 叫作一个进程,把其中每条执行流叫作线程。进程有常用的 PID 概念,每个线程在内核里也有自己的 TID。
这就带来一个熟悉的问题:共享资源要保护。
如果多个线程同时访问同一个全局变量、队列、链表或文件描述符,你还是要考虑互斥锁、条件变量、原子操作这些同步手段。共享带来了低成本,也把竞态和生命周期问题一起带了进来。
可以这么理解:
线程很方便,但不要一上来就滥用。线程一多,竞态、死锁、退出顺序、资源回收都会变复杂。
工程里可以先按边界判断。
比如一个网关设备里,Web 配置服务和采集主服务可以分成不同进程。Web 服务崩了,采集还能跑。
但在采集服务内部,如果你要同时处理串口、网络和数据缓存,可以用线程,也可以用事件循环。关键看复杂度和共享数据多少。
Linux 里的 signal,很多人会和 RTOS 信号量混淆。
注意:Linux 信号不是 RTOS 里的 semaphore。
Linux 信号更像一种异步通知,比如:
SIGINT:Ctrl+CSIGTERM:请求进程退出SIGKILL:强制杀死进程SIGSEGV:非法内存访问SIGCHLD:子进程状态变化信号的特点是异步。它可能在程序运行过程中被递送,触发默认动作,或者让某个线程执行已注册的处理函数。

这里有三个很容易踩的坑:
SIGKILL 和 SIGSTOP 不能被捕获、忽略或自定义处理所以信号处理函数里不要 printf()、申请内存、加普通互斥锁,更不要塞一大坨业务逻辑。最简单的做法,是只设置一个 sig_atomic_t 标志,让主循环自己收尾:
#include <signal.h>
static volatile sig_atomic_t stop_requested = 0;
static void on_term(int signo)
{
(void)signo;
stop_requested = 1; // 只通知,清理工作交给主循环
}
复杂一些的事件循环,可以让 handler 向 pipe 写一个字节;也可以先阻塞相应信号,再在 Linux 上通过 signalfd 像读 fd 一样接收。多线程程序则常把信号集中交给一个专门线程处理,避免任意线程突然进入 handler。
信号适合通知“发生了一件事”,不适合替代消息队列传递完整业务数据。
嵌入式 Linux 里经常会听到 daemon,也就是守护进程。
它通常指长期运行的后台服务,比如日志服务、网络服务、采集服务、升级服务。传统 daemon 会主动脱离终端、转到后台;但在 systemd 等服务管理器下,程序通常直接保持前台运行,把拉起、重启、日志和退出状态交给服务管理器,反而更容易管理。
如果你做产品,真正跑业务的往往就是这些守护进程。
这时候你要考虑:
这些问题在 RTOS 里可能是任务管理问题,在 Linux 里就变成进程和服务管理问题。
Linux 里的进程、线程、信号,不要简单套 RTOS Task 的思维。
可以先这么记:
言而总之:
进程划分资源和故障边界,线程承担进程内的并发执行,信号负责轻量的异步通知。
你写 Linux 应用时,更容易被多线程坑到,还是被信号退出流程坑到?