相干概念
程序 vs 过程 vs 命令 : Linux 零碎上所有的操作由 过程
实现,过程
的运行是动静的,在此之前是一个动态的 程序
。用户用一个程序来启动一个过程,这个程序能够是他人写好的(最终被编译成可执行文件),比方ls
、pwd
、cat
,也能够是咱们本人写的。
零碎调用 : 无论如何,程序最初运行起来都是过程,并且一个程序想要在零碎上跑,要用到 零碎调用
, 这是零碎给用户提供的 API 接口。
trace 命令: Linux 有个命令 strace,罕用来跟踪过程执行时零碎调用和所接管的信号。通过manstrace
查看具体形容。
Glibc: 作为一个开发者,兴许平时并没有间接应用零碎调用,而是 Glibc 库。Glibc 是 Linux 下应用的开源的规范 C 库它是 GNU 公布的 libc 库。Glibc
即零碎调用的封装。
介绍零碎调用
而后本文开始介绍这些零碎调用,先上图
过程治理
linux 操作系统应用 fork
从父过程中创立子过程,子过程 execve
运行程序 (二进制文件), 父过程waitpid
期待子过程完结。
所有过程都是父过程 fork
进去的,对于操作系统而言第一个鼻祖过程是哪来的呢? 系统启动的时候先创立一个所有用户过程的“祖宗过程”。
内存治理
在操作系统中,每个过程都有本人的 过程内存空间
。其中布局就有 代码段
、 数据段
、 堆
。
一个过程的内存空间是很大的,32 位的是 4G,64 位的就更大了。物理空间是无限的,所以过程的空间不能当时调配好的,肯定是须要的时候再调配。brk
和 mmap
是官员堆分配内存的零碎调用,分配内存数量比拟小的时候,应用 brk
会和原来的堆的数据连在一起。当调配的内存数量比拟大的时候,应用 mmap,会从新划分一块区域。
文件治理
文件系统相当于公司的资料库,用于保留一些永恒性质的数据。能做到长期保留,文件之所以能做到这一点,一方面是因为 介质
,另一方面是因为 格局
。
对于文件的操作,无非是 创立 creat
、关上 open
、读 read
、写 write
等等 Linux 中所有皆文件
就包含 二进制文件
、 文本文件
、stdout 文件
、Socket 文件
、 设施文件
、 目录文件
,包含过程运行起来在/proc
下生成的过程号也是文件。对于每一个文件,Linux 调配了 文件描述符
,这是一个整数。
信号处理
信号是异步解决机制,用于紧急突发状况。每一种信号都有默认动作,当然用户也能编写信号处理函数,通过 sigaction
零碎调用进行解决。
过程间通信
本地过程之间实现数据的互通,比拟常见的解决机制有 音讯队列
和共享内存
。
通过 msgget
创立一个新的队列,msgsnd
将音讯发送到音讯队列,而音讯接管方能够应用 msgrcv
从队列中取音讯; 咱们能够通过 shmget
创立一个共享内存块,通过 shmat
将共享内存映射到本人的内存空间,而后就能够读写了。
网络通信
内核中有 TCP/IP 网络协议栈的实现,能够通过 socket 来实现跨零碎的过程间通信。
查看源码
下载内核源码,找到 ./include/asm-x86_64/unistd.h
文件,外面对于零碎调用的定义
hinzer@ubuntu:linux-2.6.11$ head ./include/asm-x86_64/unistd.h
#ifndef _ASM_X86_64_UNISTD_H_
#define _ASM_X86_64_UNISTD_H_
#ifndef __SYSCALL
#define __SYSCALL(a,b)
#endif
/*
* This file contains the system call numbers.
*
查看过程
过程通过零碎调用从用户态到内核态,用户态 - 零碎调用 - 保留寄存器 - 内核态执行零碎调用 - 复原寄存器 - 返回用户态
。对于利用开发,下层还通过glibc
库(对系统调用的一个封装库)。
从 glibc 提供的 open 函数登程,分析如何从 glibc 的
open
调用到内核的sys_open
!!!
glibc 封装
# 以下相干文件
./sysdeps/unix/syscalls.list # 列出所有 glibc 的函数对应的零碎调用
./sysdeps/unix/make-syscalls.sh # 依据下面的配置文件,对于每一个封装好的零碎调用,生成一个文件
./sysdeps/unix/syscall-template.S # 定义了这个零碎调用的调用形式
./sysdeps/hppa/sysdep.h # 通过 `vim -t PSEUDO` 找到 PSEUDO 这个宏的定义。
通过剖析: open 函数的代码逻辑, 得出结论: 对于任何的零碎调用,会调用DO_CALL
。这也是一个宏,这个宏 32 位和 64 位的定义是不一样的。
32 位零碎调用过程
持续剖析 glibc 源码,发现宏 DO_CALL
定义处 unix/sysv/linux/i386/sysdep.h
。
了解: 用户调用通过软中断进入内核态,在零碎调用表中找到对应的内核零碎调用,执行内核调用之前保留用户态寄存器,执行内核调用后返回并复原用户态。
64 位零碎调用过程
DO_CALL 定义在源码地位 unix/sysv/linux/x86_64/sysdep.h
, 还是将零碎调用名称转换为零碎调用号,放到寄存器 rax。和 32 位不同的是,这里是真正进行调用,不是用中断了,而是改用 syscall 指令了,而且传递参数的寄存器也变了。
了解: 用户调用通过 syscall 指令进入内核态,在零碎调用表中找到对应的内核零碎调用,执行内核调用之前保留用户态寄存器,执行内核调用后返回并复原用户态。
整个流程
补充
零碎调用表
数据结构定义在arch/x86/entry/syscall_64.c
, 零碎调用列表输入在arch/x86/entry/syscalls/syscall_64.tbl
# 零碎调用号 abi 类型 函数名 零碎调用名
2 common open sys_open
零碎调用函数申明
申明在include/linux/syscalls.h
, 找到有 sys_open 的申明
零碎调用函数实现
内核零碎调用实现和申明统一,其中fs/open.c
编译规定
接下来,在编译的过程中,须要依据 syscall_32.tbl 和 syscall_64.tbl 生成本人的 unistd_32.h 和 unistd_64.h。在文件 arch/x86/entry/syscalls/Makefile
中
参考资料
- 趣谈 Linux 操作系统 – 05 零碎调用
- 趣谈 Linux 零碎专栏 – 09 零碎调用
- 如何下载查看 glibc 源代码
- glibc 源码剖析(一)零碎调用
- linux 下实现一个零碎调用