Linux下C库函数到系统调用函数到内核函数调用的过程
程序员文章站
2024-01-21 21:27:16
...
当我们在shell写入一个程序的时候
#include <stdio.h>
此处调用了stdio.h的C标准库,他是存在在glibc中的库函数,他里面通过一些预处理最终会调用系统调用函数,其中,系统调用函数一般是放在
#include <unistd.h>
当然,我们也可以直接写一个系统调用函数调用内核函数
ssize_t write(int fd, const void *buf, size_t count);
/*************************************************************************
> File Name: hello.c
> Author: liuhao
> Mail: [email protected]
> Created Time: Sat 19 Jun 2021 11:02:59 AM CST
************************************************************************/
#include<stdio.h>
#include <unistd.h>
int main() {
printf("Hello World\n");
write(1, "Hello World\n", 20);
return 0;
}
➜ C ./a.out
Hello World
Hello World
以write为例,系统调用函数和内核函数有一个接口来调用内核函数sys_write,该方法会直接调用硬件(比如write,会直接写入到屏幕上进行显示)
- open: 打开文件或设备
- read: 从打开的文件或设备中读取数据
- write: 向打开的文件或设备中写入数据
- close:关闭文件或者设备
- ioctl:把控制信息传递给设备驱动文件
具体执行讲解可见ELF可执行文件执行全过程
strace ./a.out
execve("./a.out", ["./a.out"], 0x7fffc106e0d0 /* 27 vars */) = 0
brk(NULL) = 0x564fe1e53000
arch_prctl(0x3001 /* ARCH_??? */, 0x7fff87737bd0) = -1 EINVAL (Invalid argument)
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=102952, ...}) = 0
mmap(NULL, 102952, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f0272e45000
close(3) = 0
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\360q\2\0\0\0\0\0"..., 832) = 832
pread64(3, "\6\0\0\0\4\0\0\[email protected]\0\0\0\0\0\0\[email protected]\0\0\0\0\0\0\[email protected]\0\0\0\0\0\0\0"..., 784, 64) = 784
pread64(3, "\4\0\0\0\20\0\0\0\5\0\0\0GNU\0\2\0\0\300\4\0\0\0\3\0\0\0\0\0\0\0", 32, 848) = 32
pread64(3, "\4\0\0\0\24\0\0\0\3\0\0\0GNU\0\t\233\222%\274\260\320\31\331\326\10\204\276X>\263"..., 68, 880) = 68
fstat(3, {st_mode=S_IFREG|0755, st_size=2029224, ...}) = 0
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f0272e43000
pread64(3, "\6\0\0\0\4\0\0\[email protected]\0\0\0\0\0\0\[email protected]\0\0\0\0\0\0\[email protected]\0\0\0\0\0\0\0"..., 784, 64) = 784
pread64(3, "\4\0\0\0\20\0\0\0\5\0\0\0GNU\0\2\0\0\300\4\0\0\0\3\0\0\0\0\0\0\0", 32, 848) = 32
pread64(3, "\4\0\0\0\24\0\0\0\3\0\0\0GNU\0\t\233\222%\274\260\320\31\331\326\10\204\276X>\263"..., 68, 880) = 68
mmap(NULL, 2036952, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f0272c51000
mprotect(0x7f0272c76000, 1847296, PROT_NONE) = 0
mmap(0x7f0272c76000, 1540096, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x25000) = 0x7f0272c76000
mmap(0x7f0272dee000, 303104, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x19d000) = 0x7f0272dee000
mmap(0x7f0272e39000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1e7000) = 0x7f0272e39000
mmap(0x7f0272e3f000, 13528, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f0272e3f000
close(3) = 0
arch_prctl(ARCH_SET_FS, 0x7f0272e44540) = 0
mprotect(0x7f0272e39000, 12288, PROT_READ) = 0
mprotect(0x564fe0654000, 4096, PROT_READ) = 0
mprotect(0x7f0272e8c000, 4096, PROT_READ) = 0
munmap(0x7f0272e45000, 102952) = 0
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(0x88, 0), ...}) = 0
brk(NULL) = 0x564fe1e53000
brk(0x564fe1e74000) = 0x564fe1e74000
write(1, "Hello World\n", 12Hello World
) = 12
exit_group(0) = ?
+++ exited with 0 +++
其中的第一个系统调用函数execve
int execve(const char *pathname, char *const argv[], char *const envp[]);
进入内核态执行sys_execve检查argv和envp、do_execve读入目标镜像文件、search_binary_handler搜索处理该二进制文件的队列、load_elf_binary检查elf文件架构并分配内存、没有动态链接库直接执行/有动态链接库进行链接write,_再次进入系统调用sys_write、把字符串写到屏幕上的进程进行等待、执行程序
上一篇: PHP 全局变量(global)
下一篇: global(全局变量)