温馨提示×

Linux进程如何启动运行

小樊
37
2026-07-21 19:55:49
栏目: 智能运维

在 Linux 中,进程的启动和运行涉及 内核调度、系统调用、可执行文件格式、进程状态 等多个方面。下面从 用户视角 → 系统调用 → 内核机制 逐步说明。


一、用户视角:进程是如何“启动”的

1️⃣ 在 shell 中启动进程

ls
./my_program

执行流程:

  1. shell 解析命令
  2. shell 调用 fork() 创建子进程
  3. 子进程调用 exec() 加载新程序
  4. 新程序开始运行

二、核心系统调用(关键)

1️⃣ fork() —— 创建新进程

pid_t pid = fork();

作用:

  • 复制当前进程
  • 得到一个新的子进程
  • 父子进程代码相同,但 PID 不同

返回值:

  • 父进程:返回子进程 PID
  • 子进程:返回 0
  • 失败:返回 -1

fork 之后,子进程几乎完全复制父进程(但写时复制 COW)


2️⃣ exec() —— 加载新程序

execve("/bin/ls", argv, envp);

作用:

  • 新的可执行文件替换当前进程
  • 进程 ID 不变
  • 代码段、数据段、堆栈全部替换

常见变体:

execl, execv, execle, execvp, execvpe

exec 成功后,不会返回


3️⃣ 典型进程启动代码

pid_t pid = fork();

if (pid == 0) {
    // 子进程
    execve("./app", args, env);
} else {
    // 父进程
    waitpid(pid, &status, 0);
}

三、从可执行文件到运行(内核视角)

1️⃣ 可执行文件格式(ELF)

Linux 常用 ELF(Executable and Linkable Format)

ELF 包含:

  • 代码段(.text)
  • 数据段(.data / .bss)
  • 入口地址(entry point)
  • 依赖的动态库

查看:

file a.out
readelf -h a.out

2️⃣ execve 的内核流程(简化)

  1. 打开可执行文件
  2. 检查权限
  3. 释放旧进程地址空间
  4. 映射 ELF 到内存
  5. 设置程序入口点
  6. 准备用户态栈(argv、envp)
  7. 跳转到程序入口(如 _start

四、进程的运行与调度

1️⃣ 进程状态

创建 → 就绪 → 运行 → 阻塞 → 终止

查看:

ps -eo pid,stat,cmd

常见状态:

  • R 运行中 / 就绪
  • S 可中断睡眠
  • D 不可中断睡眠
  • Z 僵尸进程
  • T 停止

2️⃣ CPU 调度(内核负责)

  • Linux 使用 CFS(完全公平调度器)
  • 进程只是“获得 CPU 时间片”
  • 调度是 抢占式

五、进程启动的完整生命周期

shell
 ↓
fork()
 ↓
子进程
 ↓
execve()
 ↓
加载 ELF
 ↓
跳转到 _start
 ↓
__libc_start_main
 ↓
main()
 ↓
exit()
 ↓
进程终止

六、特殊进程启动方式

1️⃣ daemon(守护进程)

  • fork 两次
  • setsid
  • 关闭标准输入输出

2️⃣ 线程(pthread)

pthread_create()
  • 线程是 共享地址空间的轻量级进程

3️⃣ systemd / init

  • PID 1
  • 负责启动系统所有进程

七、常见调试与观察命令

ps aux
top
htop
strace ./app
lsof -p PID
cat /proc/PID/maps

八、一句话总结

Linux 进程通过 fork 创建、exec 加载程序、由内核调度在 CPU 上运行。

如果你愿意,我可以:

  • 画一张 进程启动流程图
  • 深入讲 ELF 加载细节
  • 对比 进程 vs 线程
  • fork + exec 为什么这样设计

你想深入哪一部分?

0