来聊聊进程(二)
LI Rui

让我们继续上次的话题,这次我们该研究进程是怎么被执行(创建)的?或许你已经知道,我们打开的shell会fork出一个新的进程然后使用exec接口切换到目标执行文件上。

execve接口

Linux内核启动新程序的接口为do_execve,你可以在fs/exec.c中看到函数定义:

1
2
3
4
5
6
7
8
static int do_execve(struct filename *filename,
const char __user *const __user *__argv,
const char __user *const __user *__envp)
{
struct user_arg_ptr argv = { .ptr.native = __argv };
struct user_arg_ptr envp = { .ptr.native = __envp };
return do_execveat_common(AT_FDCWD, filename, argv, envp, 0);
}

argv和envp分别是参数和环境变量,do_execveat_common函数在同文件的第1962行。其中第一个参数是文件描述符,如果值为AT_FDCWD(-100)即代表当前的工作目录。

do_execveat_common干了下面几件事情:

  • 判断filename是否为错误
  • 判断进程数量是否超过上限
  • 初始化bprm(binary parameter,保存了加载二进制时的参数)
  • 将bprm中的文件名从内核栈中拷贝到新的进程的栈中
  • 将参数和环境信息从当前进程栈拷贝到新的进程的栈中
  • 调用bprm_execve启动新的程序

之后的一些调用过程如下:bprm_execve(打开文件,使调度器负载均衡等) -> exec_binprm -> search_binary_handler(查找支持的二进制格式)-> load_binary(调用格式对应的载入函数)-> START_THREAD(开始执行线程)。至此,我们的程序终于被启动。

写个C语言程序来启动进程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <sys/types.h>

int main() {
int tty_fd = open("/dev/tty", O_WRONLY, 0666);
int output_fd = open("foo.txt", O_WRONLY|O_CREAT, 0666);
dup2(output_fd, 1);
pid_t pid = fork();
if (pid == -1) {
fprintf(stderr, "Failed to fork\n");
exit(1);
} else if (pid == 0) {
printf("Here is the child process %d\n", (int) getpid());
} else {
int wait_res = wait(0);
dup2(tty_fd, 1);
printf("Here is the parent process %d\n", (int) getpid());
printf("I created the child process %d\n", (int) pid);
}
return 0;
}

这里我们模拟了shell的重定向到文件的工作,在Linux中,有以下几点需要注意:

  • /dev/tty永远代表当前程序的终端,因此我们在stdout被重定向后仍然可以输出到终端。
  • 进程启动的时候,自动打开三个文件,0为stdin,1为stdout,2为stderr。
  • 我们的父进程使用wait等待进程退出。

当我们执行程序,终端会输出:

1
2
Here is the parent process 30153
I created the child process 30154

同时新建foo.txt文件,内容为:

1
Here is the child process 30154

参考资料

  • Linux 5.15源码
  • Linux Insides
  • 《深入理解Linux内核架构》
  • 本文标题:来聊聊进程(二)
  • 本文作者:LI Rui
  • 创建时间:2021-12-27 21:09:46
  • 本文链接:https://www.lirui.tech/post/2021/c78db820c9d4.html
  • 版权声明:本博客所有文章除特别声明外,均采用 BY-SA 许可协议。转载请注明出处!