温馨提示×

Linux 进程如何启动

小樊
46
2026-08-31 09:45:43
栏目: 智能运维

在 Linux 中,“进程如何启动”可以从用户视角内核视角两个层面来理解。下面用由浅入深的方式说明。


一、从用户视角:进程是怎么“跑起来”的

1️⃣ 从 shell 启动进程(最常见)

在终端输入一个命令:

ls

背后发生了什么?

  1. Shell(如 bash)读取命令
  2. Shell 调用 fork() 创建一个子进程
  3. 子进程调用 exec() 系列函数(如 execve())加载 /bin/ls
  4. 内核用 /bin/ls 的内容替换子进程的内存空间
  5. ls 开始执行

✅ 本质:fork + exec


2️⃣ 直接运行程序

./my_program

前提:

  • 有执行权限:chmod +x my_program
  • 是 ELF 可执行文件(或脚本)

3️⃣ 启动脚本

./test.sh

脚本第一行决定解释器:

#!/bin/bash

执行流程:

  1. 内核发现是脚本
  2. 读取 shebang
  3. 启动解释器进程(如 /bin/bash
  4. 把脚本作为参数传给解释器

二、从系统调用视角:进程启动的核心流程

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

pid_t pid = fork();

作用:

  • 复制当前进程
  • 得到一个新的子进程
  • 子进程获得父进程的资源副本(写时复制)

⚠️ fork() 之后,父子进程代码相同,但返回值不同:

  • 父进程:pid > 0
  • 子进程:pid == 0

2️⃣ execve() —— 替换进程映像

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

作用:

  • 用新的程序替换当前进程
  • 代码段、数据段、堆栈全部替换
  • PID 不变

常见变体:

  • execl
  • execp
  • execv

3️⃣ 一次完整的进程启动(C 示例)

pid_t pid = fork();

if (pid == 0) {
    // 子进程
    execl("/bin/ls", "ls", NULL);
} else {
    // 父进程
    wait(NULL);
}

三、从内核视角:进程启动的完整过程

1️⃣ 用户执行命令

ls

2️⃣ Shell 调用 fork()

  • 内核分配新的 task_struct
  • 复制父进程资源(COW)

3️⃣ 子进程调用 execve()

  • 内核:
    • 检查文件权限
    • 读取 ELF 文件头
    • 加载代码段、数据段
    • 设置入口地址(entry point)
    • 清空旧地址空间

4️⃣ 跳转到程序入口

  • 通常进入 C 运行时(CRT)
  • 调用 main(argc, argv, envp)

四、Linux 中的特殊进程启动方式

1️⃣ 系统启动时(init / systemd)

  • PID 1 是第一个用户进程
  • 所有其他进程都是它的后代
  • 由内核直接启动

2️⃣ 守护进程(daemon)

特点:

  • 脱离终端
  • 后台运行
  • 父进程通常是 systemd

启动方式:

  • 系统服务
  • nohup / setsid

3️⃣ 定时任务

cron
systemd timer
  • 由 cron / systemd 创建子进程执行命令

五、查看进程启动信息

查看进程

ps -ef
ps aux

查看进程启动命令

cat /proc/<pid>/cmdline

查看启动时间

ps -o lstart -p <pid>

六、一句话总结

Linux 启动进程的本质是:

fork() 创建进程 + exec() 加载程序

所有进程的最终来源:

init / systemd → fork → exec


如果你愿意,我可以继续讲:

  • fork / exec 的内部实现
  • ELF 文件是如何被加载的
  • 进程启动时的内存布局
  • 容器(Docker)中的进程启动有何不同

你想深入哪一块?

0