代码,程序,进程
特此说明: 刘超的趣谈linux操作系统是比较重要的参考资料,本文大部分内容和图片来源于这个专栏。
1 实验环境
运行一个demo,主要功能是主进程通过系统调用fork一个新的进程,子进程功能是加载二进制文件,运行ls命令。
1.1 代码: 文本文件
1、 目录结构
[root@VM_16_8_centos test]# tree .
.
|-- createprocess.c
`-- process.c
0 directories, 2 files
2、 编辑&编译&运行
// process.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
extern int create_process (char* program, char** arg_list);
int create_process (char* program, char** arg_list)
{
pid_t child_pid;
child_pid = fork (); // 1.创建子进程
if (child_pid != 0)
return child_pid;
else {
execvp (program, arg_list); //2.子进程通过execvp去运行一个新的程序
abort ();
}
}
// createprocess.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
extern int create_process (char* program, char** arg_list);
int main ()
{
char* arg_list[] = {
"ls",
"-l",
"/etc/yum.repos.d/",
NULL
};
create_process ("ls", arg_list); //让子进程执行ls命令
return 0;
}
1.2 程序: 二进制文件
#### 第一种方式 ####
# 编译成.o文件
gcc -c -fPIC process.c
gcc -c -fPIC createprocess.c
# 生成库文件
ar cr libstaticprocess.a process.o # process.o编译成静态库
# 链接为可执行文件
gcc -o staticcreateprocess createprocess.o -L. -lstaticprocess
#### 第二种方式 ####
# 编译成.o文件
gcc -c -fPIC process.c
gcc -c -fPIC createprocess.c
# 生成库文件
gcc -shared -fPIC -o libdynamicprocess.so process.o # process.o编译成共享库
# 链接为可执行文件
gcc -o dynamiccreateprocess createprocess.o -L. -ldynamicprocess
1.3 进程: 运行时
#### 第一种方式 ####
$ ./staticcreateprocess
$ total 40
-rw-r--r--. 1 root root 1572 Oct 24 18:38 CentOS-Base.repo
......
#### 第二种方式 ####
$ export LD_LIBRARY_PATH=.
$ ./dynamiccreateprocess
$ total 40
-rw-r--r--. 1 root root 1572 Oct 24 18:38 CentOS-Base.repo
......
2 过程分析
2.1 总体框图
代码经过编译和链接生成ELF格式的可执行文件,进程通过exec系列系统调用,将可执行文件加载到内存中执行。
图右边的文件编译过程,生成 so 文件和可执行文件,后面对部分内容整理。
图左边对刚才的代码进行补充:用户态的进程 A 执行 fork,创建进程 B,在进程 B 的处理逻辑中,执行 exec 系列系统调用。这个系统调用会通过 load_elf_binary 方法,将刚才生成的可执行文件,加载到进程 B 的内存中执行。
2.2 编译和链接
1、文本文件转换到二进制文件
编译工具链将我们写好的代码转换成二进制文件,在Linux系统中识别的是elf格式的二进制文件。大致流程为
elf格式有可重定位文件
、可执行文件
、动态链接库
几种类型。
2.3 运行程序为进程
如何将elf格式的二进制程序加载到内存运行的呢?
1、 内核源码
内核中定义加载二进制文件的方法
struct linux_binfmt {
struct list_head lh;
struct module *module;
int (*load_binary)(struct linux_binprm *);
int (*load_shlib)(struct file *);
int (*core_dump)(struct coredump_params *cprm);
unsigned long min_coredump; /* minimal dump size */
} __randomize_layout;
对于 ELF 文件格式,有对应的实现
static struct linux_binfmt elf_format = {
.module = THIS_MODULE,
.load_binary = load_elf_binary,
.load_shlib = load_elf_library,
.core_dump = elf_core_dump,
.min_coredump = ELF_EXEC_PAGESIZE,
};
对于方法load_elf_binary
,源码中有调用链: do_execve->do_execveat_common->exec_binprm->search_binary_handler。
而do_execve
其实被一组exec的系统调用实现
SYSCALL_DEFINE3(execve,
const char __user *, filename,
const char __user *const __user *, argv,
const char __user *const __user *, envp)
{
return do_execve(getname(filename), argv, envp);
}
2、 理解
之前子进程代码中执行了方法exec
,运行一个elf文件。其实通过命令行执行一个程序的时候,也有类似的实现。通过strace
命令查看其中一个命令执行了哪些系统调用
[root@VM_16_8_centos demo]# strace ls
execve("/usr/bin/ls", ["ls"], [/* 28 vars */]) = 0
brk(NULL) = 0xb87000
....
2.4 fork新进程
所有进程都是从父进程fork过来的,那总归有一个祖宗进程,这就是咱们系统启动的init
进程。下面是linux进程树,也可以通过ps -ef
命令查看
2.5 补充知识
1、 了解ELF文件格式
ELF格式有可重定位文件
、可执行文件
、动态链接库
几种类型。
- 第一种是
可重定位文件
,也就是代码编译后生成的.o文件,这类文件还没有被链接,所以相当于一个代码片段。 - 第二种是
可执行文件
,链接后的程序,可直接加载到虚拟内存上运行的一类文件。 - 第三种是
动态链接库
,生成的库文件,动态库能被多个程序共享。程序并没有将动态库的代码包含进去(静态库的方式),相当于仅仅包括对动态链接库的引用。
2、 了解Linux系统中的进程,如查看进程
命令ps -ef
查看Linux系统中的进程
[root@VM_16_8_centos demo]# ps -ef
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 Mar01 ? 00:05:19 /usr/lib/systemd/systemd --switched-root --system --deserialize 22
root 2 0 0 Mar01 ? 00:00:00 [kthreadd]
root 3 2 0 Mar01 ? 00:01:40 [ksoftirqd/0]
root 5 2 0 Mar01 ? 00:00:00 [kworker/0:0H]
root 7 2 0 Mar01 ? 00:00:00 [migration/0]
root 8 2 0 Mar01 ? 00:00:00 [rcu_bh]
root 9 2 0 Mar01 ? 00:04:46 [rcu_sched]
root 10 2 0 Mar01 ? 00:00:00 [lru-add-drain]
root 11 2 0 Mar01 ? 00:00:16 [watchdog/0]
root 13 2 0 Mar01 ? 00:00:00 [kdevtmpfs]
....
3、 几种查看文件格式的工具
readelf
工具用于分析 ELF 的信息,objdump
工具用来显示二进制文件的信息,hexdump
工具用来查看文件的十六进制编码,nm
工具用来显示关于指定文件中符号的信息。
9 References
- https://time.geekbang.org/column/article/90855