linux的一些细节记录
1. malloc的实现方式
uclibc中,用户空间的malloc提供了三种实现方式:
malloc
malloc-simple
malloc-standard
具体使用何种方式,取决于.config文件定义(project\xxx\config\normal\config.uClibc),文件中分别对应几个配置符:
MALLOC
MALLOC_SIMPLE
MALLOC_STANDARD
malloc-standard :
This is a version (aka dlmalloc) of malloc/free/realloc written byDoug Lea,通过算法在用户空间实现内存管理
malloc-simple:
malloc/free通过简单mmap/munmap实现,代价比较大
malloc:
使用sbrk-->brk 系统调用实现,速度比mmap快
注意:malloc具体实现与其libc版本有关,例如对于glibc,最新版本只有一种实现方式:ptmalloc。
2. execve使用方法
execve为系统调用函数,功能为启动新的程序:
函数定义 int execve(const char *filename, char *const argv[ ], char *const envp[ ]);
返回值 函数执行成功时没有返回值,执行失败时的返回值为-1.
函数说明 execve()用来执行参数filename字符串所代表的文件路径,第二个参数是利用数组指针来传递给执行文件,并且需要以空指针(NULL)结束,最后一个参数则为传递给执行文件的新环境变量数组。
1 #include<stdio.h> 2 #include<stdlib.h> 3 4 int main(int arg, char **args) 5 { 6 //char *argv[]={"ls","-al","/home", NULL}; 7 char *argv[]={"free",NULL}; 8 //char *envp[]={0,NULL}; //传递给执行文件新的环境变量数组 9 10 execve("/usr/bin/free",argv,NULL); 11 //execve("/bin/ls",argv,envp); 12 }
argv:必须是数组指针,传入NULL或者字符串会出错(例如execve("/usr/bin/free","free",NULL);)
envp:可以设置为NULL
fork()和execve()的区别
fork是分身,execve是变身。
exec系列的系统调用是把当前程序替换成要执行的程序,而fork用来产生一个和当前进程一样的进程(虽然通常执行不同的代码流)。exec系列的系统调用已经是变成别的程序了,已经和本程序无关了。通常运行另一个程序,而同时保留原程序运行的方法是,fork+exec。
fork调clone, clone调do_fork(do_fork是内核函数, 不是系统调用). 当然fork也可以直接调do_fork, 具体怎么做的, 请参看内核代码sys_fork的实现.
pthread_create是调的clone.
简单的说clone就是fork的"泛化"版. 通过clone创建一个新进程时可以指定很多参数, 比如是否共享内存空间等等. Linux内核并没有对线程的实现, 一切都是进程, 线程简单地说不过是共享内存空间的进程而已(当然可能还有点别的细节, 比如是否共享信号处理, 文件描述符之类的).内核中一个task_struct对象代表一个进程/task/调度对象.
对Linux系统来说, 创建一个新线程和创建一个新进程开销是基本一样的(简单的说内核的眼里只有进程, 或者只有任务). 线程切换的开销和进程切换的开销, 主要是切换时是否刷新页表(MMU TLB), 也就是是否切换虚拟内存空间所对应的物理内存页. 别的几乎一致. 嗯, 线程切换是要快一些.
fork<app> --> __libc_fork <libc> --> pid = ARCH_FORK ()<与处理器架构相关> -->
INLINE_SYSCALL (clone, 5, \
CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID | SIGCHLD, \
NULL, NULL, NULL, &THREAD_SELF->tid)
< (\linux\uclibc\libpthread\nptl\sysdeps\unix\sysv\linux\arm\fork.c>
execve<app> -->
_syscall3(int, execve, const char *, filename,
char *const *, argv, char *const *, envp) <libc>
system<app> --> __libc_system <libc> -> vfork+ execl
execl --> execve
系统调用服务例程sys_clone, sys_fork, sys_vfork三者最终都是调用do_fork函数完成:
linux-3.4.x\arch\arm\kernel\sys_arm.c
1 /* Fork a new task - this creates a new program thread. 2 * This is called indirectly via a small wrapper 3 */ 4 asmlinkage int sys_fork(struct pt_regs *regs) 5 { 6 return do_fork(SIGCHLD, regs->ARM_sp, regs, 0, NULL, NULL); 7 8 } 9 10 /* Clone a task - this clones the calling program thread. 11 * This is called indirectly via a small wrapper 12 */ 13 asmlinkage int sys_clone(unsigned long clone_flags, unsigned long newsp, 14 int __user *parent_tidptr, int tls_val, 15 int __user *child_tidptr, struct pt_regs *regs) 16 { 17 if (!newsp) 18 newsp = regs->ARM_sp; 19 20 return do_fork(clone_flags, newsp, regs, 0, parent_tidptr, child_tidptr); 21 } 22 23 asmlinkage int sys_vfork(struct pt_regs *regs) 24 { 25 return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs->ARM_sp, regs, 0, NULL, NULL); 26 } 27 28 /* sys_execve() executes a new program. 29 * This is called indirectly via a small wrapper 30 */ 31 asmlinkage int sys_execve(const char __user *filenamei, 32 const char __user *const __user *argv, 33 const char __user *const __user *envp, struct pt_regs *regs) 34 { 35 int error; 36 char * filename; 37 38 filename = getname(filenamei); 39 error = PTR_ERR(filename); 40 if (IS_ERR(filename)) 41 goto out; 42 error = do_execve(filename, argv, envp, regs); 43 putname(filename); 44 out: 45 return error; 46 }
clone 和fork,vfork系统调用在实现时都是调用核心函数do_fork:
do_fork(unsigned long clone_flag, unsigned long usp, struct pt_regs);
do_fork的第一个参数是clone_flag,该参数可以是:
CLONE_VM, CLONE_FS, CLONE_FILES, CLONE_SIGHAND,CLONE_PID,CLONE_VFORK等等标志位的组合。任何一位被置1了则表明创建的子进程和父进程共享该位对应的资源。
fork时clone_flag = SIGCHLD;
vfork时clone_flag = CLONE_VM | CLONE_VFORK | SIGCHLD;
而在clone中,clone_flag由用户给出。
在vfork的实现中,cloneflags = CLONE_VFORK | CLONE_VM | SIGCHLD,这表示子进程和父进程共享地址空间,同时
do_fork会检查CLONE_VFORK,如果该位被置1了,子进程会把父进程的地址空间锁住,直到子进程退出或执行exec时才释放该锁。
pthread_create
__pthread_create_2_1 -> create_thread ->
clone_flags = (CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGNAL
| CLONE_SETTLS | CLONE_PARENT_SETTID
| CLONE_CHILD_CLEARTID | CLONE_SYSVSEM
do_clone (pd, attr, clone_flags, start_thread,
STACK_VARIABLES_ARGS, 1);
-->
ARCH_CLONE (fct, STACK_VARIABLES_ARGS, clone_flags,
pd, &pd->tid, TLS_VALUE, &pd->tid) == -1)
-->
DO_CALL (clone)
3. 线程/进程
0号进程 swapper: 有一个专门的task_struct结构体类型变量init_task,以它为头系统每个线程的task_struct字段都链接在一起形成一个双向循环链表;它是系统创建的第一个进程,也是唯一一个没有通过fork|kernel_thread产生的进程,完成最开始的初始化后最终会转化为Idle进程。
1号进程 init: 进程0最终会通过调用kernel_thread创建一个内核线程去执行init函数,init函数继续完成剩余的内核初始化,并在函数的最后调用execve系统调用装入用户空间的可执行程序/sbin/init,这时进程1就拥有了自己的属性资源,成为一个普通进程,至此,内核初始化和启动过程结束。下面就进入了用户空间的初始化,init也将变为守护进程监视系统其他进程,收集孤立的进程:当一个进程启动了一个子进程并且在子进程之前终止了,这个子进程立刻成为init的子进程。由init进程领养的进程终止时init就会调用一个wait函数取得其终止状态。
2号进程kthreadd:kthreadd进程由idle通过kernel_thread创建,并始终运行在内核空间, 负责所有内核线程的调度和管理
进程 = 一段程序+堆栈空间+任务控制块+独立存储空间
线程 = 一段程序+堆栈空间+任务控制块
对内核空间来说,线程和进程没有任何区别,因为所有内核线程都使用同一块地址空间(3GB--4GB),对应同一个页表(init_mm.pgd);
对用户空间来说,进程拥有独立的页表(task_struct->mm_struct->pgd),而线程没有独立页表,与进程共享同一个mm_struct;
4. 页表与TLB
ARMv6架构有两个协处理器寄存器用来存放一级页表基地址,TTBR0和TTBR1。操作系统把虚拟内存划分为内核空间和用户空间,TTBR0存放用户空间的一级页表基址,TTBR1存放内核空间的一级页表基址。操作系统为用户空间的每个进程分配各自的页表,即每个进程的一级页表基址是不一样的,故当发生进程上下文切换时,TTBR0需要被存放当前进程的一级页表基址;TTBR1中存放的是内核空间的一级页表基址,内核空间的一级页表基址是固定的,故TTBR1中的基址值不需要改变。
TTBR0:c15:0x2
TTBR1:c15:0x102
TTBR0/TTBR1的高20位代表了对应页表地址(TTB0/TTB1),即TTB0-->task_struct->mm_struct->pgd, TTB1-->init_mm.pgd
例如TTBR0 = 0x24C34890,代表当前用户进程的一级页表地址为0x24C34000
转载于:https://www.cnblogs.com/DF11G/p/10115811.html
linux的一些细节记录相关推荐
- linux terminal教程,Linux入门教程 - 如何记录和重放Linux终端会话
原标题:Linux入门教程 - 如何记录和重放Linux终端会话 来自:https://www.linuxmi.com/replay-linux.html 使用命令,我们可以在type文件中记录终端会 ...
- 基于全志A33开发板linux系统移植学习记录(Boot0)
基于全志A33开发板linux系统移植学习记录 第一章 Boot0基于ARMGCC的编译与修改 文章目录 基于全志A33开发板linux系统移植学习记录 前言 一.全志A33简介以及上电引导流程 二. ...
- linux 配置tensorflow 全过程记录
linux 配置tensorflow 全过程记录 前几天刚下一个deepin系统,是基于linux 内核的,界面的设计有些mac的feel 感觉还是挺不错的,之后就赶紧配置了一下tensorflow ...
- linux下查看中断请求记录 IRQ
linux下查看中断请求记录.通过cpu的中断请求的响应,可以看出cpu都在为哪些设备干活,干的活有多少量等信息. [~]$ cat /proc/interrupts CPU0 CPU1 CPU2 C ...
- linux kernel 内存相关记录
本篇文章是学习了<linux内核设计与实现>和<linux设备驱动开发详解>关于linux 内存部分的记录. MMU 内存管理单元,提供虚拟地址和物理地址映射.内存访问权限.c ...
- linux文件权限记录,linux文件权限学习记录
linux文件权限学习记录 1.Linux 的安全性 1.1/etc/passwd 文件 存放用户的登录名以及相关信息#cat / etc / passwd root: x: 0 : 0 : roo ...
- huggingface调用一些细节记录
huggingface调用一些细节记录 Model Input Model Foward BertModel 写给我自己看的一些小细节,因为不是每天写代码,总是会忘 要多看文档!!! Model In ...
- Java实现CRM项目过程中的细节记录(一)
CRM项目实现过程中的细节记录(一) 文章目录 CRM项目实现过程中的细节记录(一) 一.数据库相关细节 1. 表名 2. 表字段说明 3. 不使用主外键约束 4. 不使用主键自动增长 UUID 5. ...
- electron + vue /打包linux应用报错记录
1. electron + vue 打包linux应用报错记录 > icon图标导致的报错 ⨯ unknown output format set github.com/develar/app ...
最新文章
- Yann LeCun:假如没有深度学习,Facebook就是尘埃
- python学习手册笔记——22.模块代码编写基础
- 工业级POE交换机技术优势及供电方法详解!
- Linux 10分钟让你掌握虚拟地址--写时拷贝技术
- python na不显示 占位_Python学习之路—Python基础(一)
- linux下jdk1.8搭建笔记
- 从小学4年级的数学课开始解释线性回归
- 自定义函数 | R语言偏相关分析及绘图
- 自学网络结构(一):Neural Architecture Search With Reinforcement Learning
- history 路由 vs hash 路由 vs location.href 实现跳转
- 安卓日志:拍照、文件读取的问题
- UPS不间断电源常见问题及解决办法
- remix中错误集合
- Retrofit 通过刷新头部Token解决token过期
- 如何在macOS Big Sur 的Finder中使用终端锁定文件
- 微信小程序糟心开发过程
- Bi-directional Cross-Modality Feature Propagation with Separation-and Aggregation Gate_eccv2020
- 去除字符串中的空格(C语言)
- 关于计算机优点缺点的英语作文,平板电脑的优缺点英语作文
- 产销平衡的运输问题上机实验matlab_产销平衡运输问题