首页
chatGPT
关于
友链
其它
统计
更多
壁纸
留言
Search
1
cgroup--(4)cgroup v1和cgroup v2的详细介绍
6,535 阅读
2
修改Linux Kernel defconfig的标准方法
6,448 阅读
3
Android系统之VINTF(1)manifests&compatibility matrices
6,054 阅读
4
使用git生成patch和应用patch
3,570 阅读
5
c语言的__attribute__
3,183 阅读
默认分类
文章收集
学习总结
算法
环境配置
知识点
入门系列
vim
shell
Git
Make
Android
Linux
Linux命令
内存管理
Linux驱动
Language
C++
C
Rust
工具
软件工具
Bug
COMPANY
登录
Search
标签搜索
Rust
shell
Linux
c
uboot
Vim
vintf
Linux驱动
Android
device_tree
git
DEBUG
arm64
链表
数据结构
IDR
内核
ELF
gcc
ARM
adtxl
累计撰写
380
篇文章
累计收到
16
条评论
首页
栏目
默认分类
文章收集
学习总结
算法
环境配置
知识点
入门系列
vim
shell
Git
Make
Android
Linux
Linux命令
内存管理
Linux驱动
Language
C++
C
Rust
工具
软件工具
Bug
COMPANY
页面
chatGPT
关于
友链
其它
统计
壁纸
留言
搜索到
380
篇与
的结果
2020-09-11
uboot的介绍
目录::stuck_out_tongue_winking_eye:[TOC]1. uboot是干嘛的uboot 属于bootloader的一种,是用来引导启动内核的,它的最终目的就是,从flash中读出内核,放到内存中,启动内核所以,由上面描述的,就知道,UBOOT需要具有读写flash的能力。2. uboot怎么启动内核uboot刚开始被放到flash中,板子上电后,会自动把其中的一部分代码拷到内存中执行,这部分代码负责把剩余的uboot代码拷到内存中,然后uboot代码再把kernel部分代码也拷到内存中,并且启动,内核启动后,挂着根文件系统,执行应用程序。参考:https://blog.csdn.net/yilongdashi/article/details/87968572
2020年09月11日
741 阅读
0 评论
0 点赞
2020-09-07
动态内存分配
目录:[TOC]1.malloc和free函数原型如下,在头文件stdlib.h中声明:void *malloc(size_t size); void free(void *pointer);malloc的参数是需要分配的内存字节数。如果内存池中的可用内存可以满足这个需求,malloc就返回一个指向被分配的内存块起始位置的指针。malloc分配的是一块连续的内存,并且不对这块内存进行任何的初始化。如果操作系统无法向malloc提供更多的内存,malloc就返回一个NULL指针。因此,对每个从malloc返回的指针都进行检查,确保它并非NULL是非常重要的。free的参数必须要么是NULL,要么是一个先前从malloc、calloc或realloc返回的值。向free传递一个NULL参数不会产生任何效果。malloc返回一个类型为void*的指针,一个void*类型的指针可以转换成其它任何类型的指针2.calloc和realloccalloc和realloc的函数原型如下所示:void *calloc(size_t num_elements, size_t element_size); void realloc(void *ptr, size_t new_size);malloc和calloc之间的主要区别是calloc在返回指向内存的指针之前把它初始化为0。calloc和malloc之间另一个较小的区别是它们请求内存数量的方式不同。calloc的参数包括所需元素的数量和每个元素的字节数。根据这些值,它能够计算出总共需要分配的内存。realloc函数用于修改一个原先已经分配的内存块的大小。使用这个函数,可以使一块内存扩大或缩小。如果它用于扩大一个内存块,那么这块内存原先的内容依然保留,新增加的内存添加到原先内存块的后面,新内存并未以任何方法进行初始化。如果它用于缩小内存块,该内存块尾部的部分内存便被拿掉,剩余部分内存的原先内容依然保留。如果原先的内存块无法改变大小,realloc将分配另一块正确大小的内存,并把原先那块内存的内容复制到新块上。因此,在使用realloc之后,你就不能再使用指向旧内存的指针,而是应该用realloc所返回的新指针。3. 常见的动态内存错误在使用动态内存分配的程序中,常常会出现许多错误。这些错误包括对NULL指针进行解引用操作、对分配的内存进行操作时越过边界、释放并非动态分配的内存、试图释放一块动态分配的内存以及一块动态内存被释放之后被继续使用。动态内存分配最常见的错误就是忘记检查所请求的内存是否成功分配// 记得对分配的内存进行检查 if (new_mem == NULL) printf( "Out of memory" ); exit(1);动态内存分配的第二大错误来源是操作内存时超出了分配内存的边界在malloc和free的有些实现中,它们以链表的形式维护可用的内存池。对分配的内存之外的区域进行访问可能破坏这个链表,这有可能产生异常,从而终止程序。
2020年09月07日
773 阅读
0 评论
0 点赞
2020-09-07
gcc和objdump
1. gcc-ansi 关闭 gnu c中与ansi c不兼容的特性,激活ansi c的专有特性 ( 包括禁止一些 asm inline typeof 关键字 , 以及 UNIX,vax 等预处理宏-lxx 表示动态加载libxx.so库-Lxx 表示增加目录xx,让编译器可以在xx下寻找库文件-Ixx 表示增加目录xx,让编译器可以在xx下寻找头文件优化选项-shared 生成共享目标文件。通常用在建立共享库时-Wall 生成所有警告信息。一下是具体的选项,可以单独使用简单的GCC语法:如果你只有一个文件(或者只有几个文件),那么就可以不写Makefile文件(当然有Makefile更加方便),用gcc直接编译就行了。在这里我们只介绍几个我经常用的几个参数,第一是 “-o”,它后面的参数表示要输出的目标文件,再一个是 “-c”,表示仅编译(Compile),不链接(Make),如果没有”-c”参数,那么就表示链接,如下面的几个命令:gcc –c test.c,表示只编译test.c文件,成功时输出目标文件test.ogcc –c test.c –o test.o ,与上一条命令完全相同gcc –o test test.o,将test.o连接成可执行的二进制文件testgcc –o test test.c,将test.c编译并连接成可执行的二进制文件testgcc test.c –o test,与上一条命令相同gcc –c test1.c,只编译test1.c,成功时输出目标文件test1.ogcc –c test2.c,只编译test2.c,成功时输出目标文件test2.ogcc –o test test1.o test2.o,将test1.o和test2.o连接为可执行的二进制文件testgcc –c test test1.c test2.c,将test1.o和test2.o编译并连接为可执行的二进制文件test注:如果你想编译cpp文件,那么请用g++,否则会有类似如下莫名其妙的错误:cc3r3i2U.o(.eh_frame+0x12): undefined reference to `__gxx_personality_v0’......还有一个参数是”-l”参数,与之紧紧相连的是表示连接时所要的链接库,比如多线程,如果你使用了pthread_create函数,那么你就应该在编译语句的最后加上”-lpthread”,”-l”表示连接,”pthread”表示要连接的库,注意他们在这里要连在一起写,还有比如你使用了光标库curses,那么呢就应该在后面加上”-lcurses”,比如下面的写法:gcc –o test test1.o test2.o –lpthread –lcurses例如: 在ubuntu 环境下编译基于course库函数的程序时,如果不带 -lncurses时,会出现screen1.c:(.text+0x12):对‘initscr’未定义的引用screen1.c:(.text+0x24):对‘wmove’未定义的引用screen1.c:(.text+0x39):对‘printw’未定义的引用screen1.c:(.text+0x4a):对‘wrefresh’未定义的引用screen1.c:(.text+0x5f):对‘endwin’未定义的引用需使用 gcc -o screen1 screen1.c -lncurses2. objdumpgcc命令之 objdump ---------------objdump是用查看目标文件或者可执行的目标文件的构成的GCC工具----------以下3条命令足够那些喜欢探索目标文件与源代码之间的丝丝的关系的朋友。objdump -x obj 以某种分类信息的形式把目标文件的数据组织(被分为几大块)输出 <可查到该文件的所有动态库> objdump -t obj 输出目标文件的符号表()objdump -h obj 输出目标文件的所有段概括()objdump -j .text/.data -S obj 输出指定段的信息,大概就是反汇编源代码把objdump -S obj C语言与汇编语言同时显示 以下为网上摘录文章。关于nm -s的显示请自己man nm查看objdump命令的man手册objdump - 显示二进制文件信息objdump [-a] [-b bfdname | --target=bfdname] [-C] [--debugging] [-d] [-D] [--disassemble-zeroes] [-EB|-EL|--endian={big|little}] [-f] [-h] [-i|--info] [-j section | --section=section] [-l] [-m machine ] [--prefix-addresses] [-r] [-R] [-s|--full-contents] [-S|--source] [--[no-]show-raw-insn] [--stabs] [-t] [-T] [-x] [--start-address=address] [--stop-address=address] [--adjust-vma=offset] [--version] [--help] objfile... --archive-headers-a 显示档案库的成员信息,与 ar tv 类似objdump -a libpcap.a 和 ar -tv libpcap.a 显示结果比较比较 显然这个选项没有什么意思。--adjust-vma=offsetWhen dumping information, first add offset to all the section addresses. This is useful if the sec- tion addresses do not correspond to the symbol table, which can happen when putting sections at particular addresses when using a format which can not represent section addresses, such as a.out.-b bfdname--target=bfdname指定目标码格式。这不是必须的,objdump能自动识别许多格式, 比如:objdump -b oasys -m vax -h fu.o 显示fu.o的头部摘要信息,明确指出该文件是Vax系统下用Oasys 编译器生成的目标文件。objdump -i将给出这里可以指定的 目标码格式列表--demangle-C 将底层的符号名解码成用户级名字,除了去掉所有开头 的下划线之外,还使得C++函数名以可理解的方式显示出来。--debugging显示调试信息。企图解析保存在文件中的调试信息并以C语言 的语法显示出来。仅仅支持某些类型的调试信息。--disassemble-d 反汇编那些应该还有指令机器码的section--disassemble-all-D 与 -d 类似,但反汇编所有section--prefix-addresses反汇编的时候,显示每一行的完整地址。这是一种比较老的反汇编格式。 显示效果并不理想,但可能会用到其中的某些显示,自己可以对比。--disassemble-zeroes一般反汇编输出将省略大块的零,该选项使得这些零块也被反汇编。-EB-EL--endian={big|little}这个选项将影响反汇编出来的指令。 little-endian就是我们当年在dos下玩汇编的时候常说的高位在高地址, x86都是这种。 --file-headers-f 显示objfile中每个文件的整体头部摘要信息。--section-headers--headers-h 显示目标文件各个section的头部摘要信息。--help 简短的帮助信息。--info-i 显示对于 -b 或者 -m 选项可用的架构和目标格式列表。--section=name-j name 仅仅显示指定section的信息--line-numbers-l 用文件名和行号标注相应的目标代码,仅仅和-d、-D或者-r一起使用 使用-ld和使用-d的区别不是很大,在源码级调试的时候有用,要求 编译时使用了-g之类的调试编译选项。--architecture=machine-m machine指定反汇编目标文件时使用的架构,当待反汇编文件本身没有描述 架构信息的时候(比如S-records),这个选项很有用。可以用-i选项 列出这里能够指定的架构 --reloc-r 显示文件的重定位入口。如果和-d或者-D一起使用,重定位部分以反汇 编后的格式显示出来。--dynamic-reloc-R 显示文件的动态重定位入口,仅仅对于动态目标文件有意义,比如某些 共享库。--full-contents-s 显示指定section的完整内容。objdump --section=.text -s inet.o | more --source-S 尽可能反汇编出源代码,尤其当编译的时候指定了-g这种调试参数时, 效果比较明显。隐含了-d参数。--show-raw-insn反汇编的时候,显示每条汇编指令对应的机器码,除非指定了 --prefix-addresses,这将是缺省选项。 --no-show-raw-insn反汇编时,不显示汇编指令的机器码,这是指定 --prefix-addresses 选项时的缺省设置。 --stabsDisplay the contents of the .stab, .stab.index, and .stab.excl sections from an ELF file. This is only useful on systems (such as Solaris 2.0) in which .stab debugging symbol-table entries are carried in an ELF section. In most other file formats, debug- ging symbol-table entries are interleaved with linkage symbols, and are visible in the --syms output. --start-address=address从指定地址开始显示数据,该选项影响-d、-r和-s选项的输出。 --stop-address=address显示数据直到指定地址为止,该选项影响-d、-r和-s选项的输出。 --syms-t 显示文件的符号表入口。类似于nm -s提供的信息--dynamic-syms-T 显示文件的动态符号表入口,仅仅对动态目标文件有意义,比如某些 共享库。它显示的信息类似于 nm -D|--dynamic 显示的信息。--version 版本信息objdump --version --all-headers-x 显示所有可用的头信息,包括符号表、重定位入口。-x 等价于 -a -f -h -r -t 同时指定。objdump -x inet.o 参看 nm(1)★ objdump应用举例(待增加)/*g++ -g -Wstrict-prototypes -Wall -Wunused -o objtest objtest.c*/includeincludeint main ( int argc, char * argv[] ){execl( "/bin/sh", "/bin/sh", "-i", 0 ); return 0;}g++ -g -Wstrict-prototypes -Wall -Wunused -o objtest objtest.cobjdump -j .text -Sl objtest | more/main(查找)08048750:main():/home/scz/src/objtest.c:7*/includeincludeint main ( int argc, char * argv[] ){8048750: 55 pushl %ebp8048751: 89 e5 movl %esp,%ebp/home/scz/src/objtest.c:8 execl( "/bin/sh", "/bin/sh", "-i", 0 );8048753: 6a 00 pushl $0x08048755: 68 d0 87 04 08 pushl $0x80487d0804875a: 68 d3 87 04 08 pushl $0x80487d3804875f: 68 d3 87 04 08 pushl $0x80487d38048764: e8 db fe ff ff call 8048644 <_init+0x40>8048769: 83 c4 10 addl $0x10,%esp/home/scz/src/objtest.c:9 return 0;804876c: 31 c0 xorl %eax,%eax804876e: eb 04 jmp 8048774 8048770: 31 c0 xorl %eax,%eax8048772: eb 00 jmp 8048774 /home/scz/src/objtest.c:10}8048774: c9 leave 8048775: c3 ret 8048776: 90 nop如果说上面还不够清楚,可以用下面的命令辅助一下:objdump -j .text -Sl objtest --prefix-addresses | moreobjdump -j .text -Dl objtest | more用以上不同的命令去试会得到惊喜!
2020年09月07日
934 阅读
0 评论
0 点赞
2020-09-07
目标文件格式
编译器编译源代码后生成的文件叫做目标文件。目标文件从结构上讲,它是已经编译后的可执行文件格式,只是还没有经过链接的过程,其中可能有些符号或有些地址还没有被调整。其实它本身就是按照可执行文件格式存储的,只是跟真正的可执行文件在结构上稍有不同。1.1 目标文件的格式Linux下的可执行文件格式是ELF(Executable Linkable Format)。ELF文件标准把系统中采用的ELF格式的文件分为四类,分别是:可重定位文件(Relocatable File)如Linux下的.o可执行文件(Executable File)共享目标文件(Shared Object File)如Linux下的.so核心转储文件(Core Dump File)当进程意外终止时,系统可以将该进程的地址空间的内容及终止时的一些其它信息转储到核心转储文件如Linux下的core dumpLinux下使用file命令可以查看相应的文件格式,file test.o1.2 目标文件时什么样的section(节)与segment(段)二者唯一的区别是在ELF的链接视图和装载视图的时候code section(代码段)存放编译后的机器指令,如".code"、".text"data section(数据段)存放编译后的全局变量和局部静态变量,如.data文件头ELF文件的开头是一个“文件头”,它描述了整个文件的文件属性,包括文件是否可执行、是静态链接还是动态链接及入口地址(如果是可执行文件)、目标硬件、目标操作系统等信息,文件头还包括一个段表。section table(段表)段表是一个描述文件中各个段的数组。段表描述了文件中各个段在文件中的偏移位置及段的属性等,从段表里面可以得到各个段的所有信息。.bss段未初始化的全局变量和局部静态变量一般放在.bss段。程序运行的时候这些变量确是要占内存空间的,并且可执行文件必须记录所有未初始化的全局变量和局部静态变量的大小总和。所以.bss段只是为位初始化的全局变量和局部静态变量预留位置而已,它并没有内容,所以在文件中也不占据空间。总体来说,程序源代码被编译以后主要分成两种段:程序指令和程序数据。代码段属于程序指令,而数据段和.bss段属于程序数据。1.3 一个实例:simple_section.osimple_section.c代码如下:/* * Linux: gcc -c simple-section.c */ int printf( const char* format, ... ); int global_int_var = 84; int global_uninit_var; void func1(int i) { printf( "%d\n", i); } int main(void) { static int static_var = 85; static int static_var2; int a = 1; int b; func1( static_var + static_var2 + a + b ); return a; }使用gcc编译文件(-c参数表示只编译不链接)gcc -c simple_section.c 使用objdump查看目标文件的结构和内容,Linux下还有一个工具readelf,是专门针对elf文件格式的解析器参数-h把各个段的信息打印出来,-x打印更多的信息objdump -h simple_section.o显示如下:user@user-HP-ProDesk-600-G5-MT:~/txl/code/c/simlesection$ objdump -h simple_section.o simple_section.o: 文件格式 elf64-x86-64 节: Idx Name Size VMA LMA File off Algn 0 .text 00000057 0000000000000000 0000000000000000 00000040 2**0 CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE 1 .data 00000008 0000000000000000 0000000000000000 00000098 2**2 CONTENTS, ALLOC, LOAD, DATA 2 .bss 00000004 0000000000000000 0000000000000000 000000a0 2**2 ALLOC 3 .rodata 00000004 0000000000000000 0000000000000000 000000a0 2**0 CONTENTS, ALLOC, LOAD, READONLY, DATA 4 .comment 0000002a 0000000000000000 0000000000000000 000000a4 2**0 CONTENTS, READONLY 5 .note.GNU-stack 00000000 0000000000000000 0000000000000000 000000ce 2**0 CONTENTS, READONLY 6 .eh_frame 00000058 0000000000000000 0000000000000000 000000d0 2**3 CONTENTS, ALLOC, LOAD, RELOC, READONLY, DATA 其中Size表示段的长度,File off表示段所在的位置,每个段的第二行CONTENTS, ALLOC等表示段的属性,“CONTENTS”表示段在文件中存在。使用size命令,可以用来查看ELF文件代码段、数据段和bss段的长度(dec表示3个段长度和的十进制,hex表示长度和的十六进制)user@user-HP-ProDesk-600-G5-MT:~/txl/code/c/simlesection$ size simple_section.o text data bss dec hex filename 179 8 4 191 bf simple_section.o objdump的“-s”参数可以将所有段的内容以十六进制的方式打印出来,“-d”参数可以将所有包含指令的段反汇编objdump -s -d simple_section.o显示内容如下:simple_section.o: 文件格式 elf64-x86-64 Contents of section .text: 0000 554889e5 4883ec10 897dfc8b 45fc89c6 UH..H....}..E... 0010 488d3d00 000000b8 00000000 e8000000 H.=............. 0020 0090c9c3 554889e5 4883ec10 c745f801 ....UH..H....E.. 0030 0000008b 15000000 008b0500 00000001 ................ 0040 c28b45f8 01c28b45 fc01d089 c7e80000 ..E....E........ 0050 00008b45 f8c9c3 ...E... Contents of section .data: 0000 54000000 55000000 T...U... Contents of section .rodata: 0000 25640a00 %d.. Contents of section .comment: 0000 00474343 3a202855 62756e74 7520372e .GCC: (Ubuntu 7. 0010 352e302d 33756275 6e747531 7e31382e 5.0-3ubuntu1~18. 0020 30342920 372e352e 3000 04) 7.5.0. Contents of section .eh_frame: 0000 14000000 00000000 017a5200 01781001 .........zR..x.. 0010 1b0c0708 90010000 1c000000 1c000000 ................ 0020 00000000 24000000 00410e10 8602430d ....$....A....C. 0030 065f0c07 08000000 1c000000 3c000000 ._..........<... 0040 00000000 33000000 00410e10 8602430d ....3....A....C. 0050 066e0c07 08000000 .n...... Disassembly of section .text: 0000000000000000 <func1>: 0: 55 push %rbp 1: 48 89 e5 mov %rsp,%rbp 4: 48 83 ec 10 sub $0x10,%rsp 8: 89 7d fc mov %edi,-0x4(%rbp) b: 8b 45 fc mov -0x4(%rbp),%eax e: 89 c6 mov %eax,%esi 10: 48 8d 3d 00 00 00 00 lea 0x0(%rip),%rdi # 17 <func1+0x17> 17: b8 00 00 00 00 mov $0x0,%eax 1c: e8 00 00 00 00 callq 21 <func1+0x21> 21: 90 nop 22: c9 leaveq 23: c3 retq 0000000000000024 <main>: 24: 55 push %rbp 25: 48 89 e5 mov %rsp,%rbp 28: 48 83 ec 10 sub $0x10,%rsp 2c: c7 45 f8 01 00 00 00 movl $0x1,-0x8(%rbp) 33: 8b 15 00 00 00 00 mov 0x0(%rip),%edx # 39 <main+0x15> 39: 8b 05 00 00 00 00 mov 0x0(%rip),%eax # 3f <main+0x1b> 3f: 01 c2 add %eax,%edx 41: 8b 45 f8 mov -0x8(%rbp),%eax 44: 01 c2 add %eax,%edx 46: 8b 45 fc mov -0x4(%rbp),%eax 49: 01 d0 add %edx,%eax 4b: 89 c7 mov %eax,%edi 4d: e8 00 00 00 00 callq 52 <main+0x2e> 52: 8b 45 f8 mov -0x8(%rbp),%eax 55: c9 leaveq 56: c3 retq 在.text段,最左面一列是偏移量,中间四列是十六进制内容,最右面一列是.text段的ASCII码形式。.data段保存的是那些已经初始化了的全局静态变量和局部静态变量。字符串常量"%d\n",它是一种只读数据,所以它被放到了“.rodata”段,我们可以从输出结果看到".rodata"这个段的4个字节刚好是这个字符串常量的ASCII字节序,最后以\0结尾。.bss段存放的是未初始化的全局变量和局部静态变量。还有一些其它的常用段如下:常用的段名说明.rodata跟.rodata一样.interp/lib64/ld-linux.so.2(动态链接器的路径,有入口函数,装载的时候启动).dynamic动态链接信息.symtab用于静态链接和调试(符号表保留在文件中,不加载进内存).dynsym用于动态链接(符号表会被加载进内存).init和.finit1 共享对象可能会持有这两个段,做为共享对象的入口和出口函数 2 c++中的全局对象,或者static对象的构造函数和析构函数.comment存放的是编译器版本信息.debug调试信息.plt和.got动态链接的跳转表和全局入口表1.4 ELF文件结构描述ELF目标文件格式的最前部是ELF文件头,它描述了整个文件的基本属性,比如ELF文件版本、目标机器型号、程序入口地址等。紧接着就是各个段。其中ELF文件中与段有关的重要结构就是段表,该表描述了ELF文件包含的所有段的信息,比如每个段的段名、段的长度、在文件中的偏移、读写权限及段的其他属性。1.4.1 文件头使用readelf命令来详细查看ELF文件,readelf -h simple_section.o输出如下:ELF 头: Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 类别: ELF64 数据: 2 补码,小端序 (little endian) 版本: 1 (current) OS/ABI: UNIX - System V ABI 版本: 0 类型: REL (可重定位文件) 系统架构: Advanced Micro Devices X86-64 版本: 0x1 入口点地址: 0x0 程序头起点: 0 (bytes into file) Start of section headers: 1104 (bytes into file) 标志: 0x0 本头的大小: 64 (字节) 程序头大小: 0 (字节) Number of program headers: 0 节头大小: 64 (字节) 节头数量: 13 字符串表索引节头: 12 ELF魔数:最前面的16个字节刚好对应“Elf32_Ehdr”的e_ident这个成员。1.4.2 段表使用readelf工具来查看文件的段,它显示出来的结果才是真正的段表结构readelf -S simple_section.o输出如下:There are 13 section headers, starting at offset 0x450: 节头: [号] 名称 类型 地址 偏移量 大小 全体大小 旗标 链接 信息 对齐 [ 0] NULL 0000000000000000 00000000 0000000000000000 0000000000000000 0 0 0 [ 1] .text PROGBITS 0000000000000000 00000040 0000000000000057 0000000000000000 AX 0 0 1 [ 2] .rela.text RELA 0000000000000000 00000340 0000000000000078 0000000000000018 I 10 1 8 [ 3] .data PROGBITS 0000000000000000 00000098 0000000000000008 0000000000000000 WA 0 0 4 [ 4] .bss NOBITS 0000000000000000 000000a0 0000000000000004 0000000000000000 WA 0 0 4 [ 5] .rodata PROGBITS 0000000000000000 000000a0 0000000000000004 0000000000000000 A 0 0 1 [ 6] .comment PROGBITS 0000000000000000 000000a4 000000000000002a 0000000000000001 MS 0 0 1 [ 7] .note.GNU-stack PROGBITS 0000000000000000 000000ce 0000000000000000 0000000000000000 0 0 1 [ 8] .eh_frame PROGBITS 0000000000000000 000000d0 0000000000000058 0000000000000000 A 0 0 8 [ 9] .rela.eh_frame RELA 0000000000000000 000003b8 0000000000000030 0000000000000018 I 10 8 8 [10] .symtab SYMTAB 0000000000000000 00000128 0000000000000198 0000000000000018 11 11 8 [11] .strtab STRTAB 0000000000000000 000002c0 000000000000007c 0000000000000000 0 0 1 [12] .shstrtab STRTAB 0000000000000000 000003e8 0000000000000061 0000000000000000 0 0 1 Key to Flags: W (write), A (alloc), X (execute), M (merge), S (strings), I (info), L (link order), O (extra OS processing required), G (group), T (TLS), C (compressed), x (unknown), o (OS specific), E (exclude), l (large), p (processor specific) 1.4.3 重定位表.rela.text类型位RELA,是一个重定位表。链接器在处理目标文件时,需要对目标文件中某些部位进行重定位,即代码段和数据段中那些绝对地址的引用的位置。这些重定位信息都记录在重定位表里面。.rela.text段就是.text段的重定位表,因为.text段中至少有一个绝对地址的引用,那就是对printf函数的调用。而.data段则没有对绝对地址的引用,它只包含了几个常量,所以没有针对data段的重定位表。1.4.4 字符串表.strtab(string table): 字符串表,用来保存普通的字符串 .shstrtab(section header string table):用来保存段表中用到的字符串,最常见的就是段名
2020年09月07日
1,262 阅读
0 评论
0 点赞
2020-09-01
内核配置
1.内核源码编译过程1.遍历每个源码目录的(或配置指定的源码目录)Makefile2.每个目录的Makefile会根据Kconfig文件定制要编译的对象3.最后回到顶层目录的Makefile执行编译据此,我们可以得出各个文件的作用Kconfig--->(每个源码目录下)提供选项.config--->(源码顶层目录下)保存选择结果Makefile--->(每个源码目录下)根据.config中的内容来告知编译系统如何编译2. Kconfig基本语法2.1 单一选项总体原则:每一个config就是一个选项,最上面跟着控制句柄,下面则是对这个选项的配置,如选项名是什么,依赖什么,选中这个后同时会选择什么。config CPU_S5PC100 bool "选项名" select S5P_EXT_INT select SAMSUNG_DMADEV help Enable S5PC100 CPU supportconfig —> 选项CPU_S5PC100 —>句柄,可用于控制Makefile 选择编译方式bool —>选择可能:TRUE选中、FALSE不选 选中则编译,不选中则不编译。如果后面没有字符串名称,则表示其不会出现在选择软件列表中select —> 当前选项选中后则select后指定的选项自动被选择depends on ARM || BLACKFIN || MIPS || COLDFIREdepend on 依赖,后面的四个选择其中至少一个被选择,这个选项才能被选config DM9000 tristate "DM9000 support"tristate —> 选中并编译进内核、不选编译成模块2.2 选项为数字config ARM_DMA_IOMMU_ALIGNMENT int "Maximum PAGE_SIZE order of alignment for DMA IOMMU buffers" ---->该选项是一个整型值 range 4 9 ---->该选项的范围值 default 8 ---->该选项的默认值 help DMA mapping framework by default aligns all buffers to the smallest ...这里的defult其实也可以用在bool中config STACKTRACE_SUPPORT bool --->该选项可以选中或不选,且不会出现在选择列表中 default y ---->表示缺省情况是选中2.3 if..endifif ARCH_S5PC100 --->如果ARCH_S5PC100选项选中了,则在endif范围内的选项才会被选 config CPU_S5PC100 bool "选项名" select S5P_EXT_INT select SAMSUNG_DMADEV help Enable S5PC100 CPU support endif举个例子,如果CPU没有选择使用多核CPU,则不会出现CPU个数的选项。2.4 choice多个选项choice --->表示选择列表 prompt "Default I/O scheduler" //主目录名字 default DEFAULT_CFQ //默认CFQ help Select the I/O scheduler which will be used by default for all block devices. config DEFAULT_DEADLINE bool "Deadline" if IOSCHED_DEADLINE=y config DEFAULT_CFQ bool "CFQ" if IOSCHED_CFQ=y config DEFAULT_NOOP bool "No-op" endchoice2.5 menu与menuconfigmenu "Boot options" ----> menu表示该选项是不可选的菜单,其后是在选择列表的菜单名 config USE_OF bool "Flattened Device Tree support" select IRQ_DOMAIN select OF select OF_EARLY_FLATTREE help Include support for flattened device tree machine descriptions. .... endmenu ----> menu菜单结束menu指的是不可编辑的menu,而menuconfig则是带选项的menumenu和choice的区别menu 可以多选 choice 是单项选择题menuconfig的用法menuconfig MODULES ---> menuconfig表示MODULE是一个可选菜单,其选中后是CONFIG_MODULES bool "菜单名" if MODULES ... endif # MODULES说到底,menconfig 就是一个带选项的菜单,在下面需要用bool判断一下,选择成立后,进入if …endif 中间得空间。3.基本使用方法将写好的驱动添加到内核编译选项中1.配置Kconfig在driver目录下新建一个目录test,进入test目录,创建Kconfig文件config TEST bool "Test driver" help this for test!这里定义了一个TEST的句柄,Kconfig可以通过这个句柄来控制Makefile中是否编译,”Test driver”是显示在终端的名称。2.配置Makefile在test目录,新建一个Makefileobj-$(CONFIG_TEST) += test.o意义:obj-$(CONFIG_选项名) += xxx.o /* 当CONFIG_选项名=y时,表示对应目录下的xxx.c将被编译进内核 当CONFIG_选项名=m时对应目录下的xxx.c将被编译成模块*/3.配置上层目录的Makefile与Kconfig在上一层目录的Kconfig中,menu "Device Drivers" source "drivers/test/Kconfig"表示将test文件夹中的Kconfig加入搜寻目录在上一层目录的Makefile中,更改如下obj-y += irqchip/ obj-y += bus/ obj-y += test/ #新添加最后,运行make menuconfig,可进行配置,保存后生成.config文件。
2020年09月01日
960 阅读
0 评论
0 点赞
2020-08-21
字符设备驱动
1.Linux字符设备驱动结构1.1 cdev结构体在Linux内核中,使用cdev结构体描述一个字符设备,cdev结构体定义如下,struct cdev { struct kobject kobj; /* 内嵌的kobject对象 */ struct module *owner; /* 所属模块 */ struct file_operations *ops; /* 文件操作结构体 */ struct list_head list; dev_t dev; /* 设备号 */ unsigned int count; }cdev结构体的dev_t成员定义了设备号,为32位,其中12位为主设备号,20位为次设备号。使用下列宏可以从dev_t获得主设备号和次设备号;MAJOR(dev_t dev) MINOR(dev_t dev)使用下列宏可以通过主设备号和次设备号生成dev_t:MKDEV(int major, int minor)Linux内核提供了一组函数以用于操作cdev结构体:/* 初始化cdev成员,并建立cdev和file_operations之间的连接*/ void cdev_init(struct cdev *, struct file_operations *); /* 动态申请一个cdev内存 */ struct cdev *cdev_alloc(void); /* */ void cdev_put(struct cdev *p); /* 向系统添加一个cdev,完成字符设备的注册。对cdev_add()的调用通常发生在字符设备驱动模块加载函数中 */ int cdev_add(struct cdev *, dev_t, unsigned); /* 对cdev_del()函数的调用通常发生在字符设备驱动模块卸载函数中 */ void cdev_del(struct cdev *);1.2 分配和释放设备号在调用cdev_add()函数向系统注册字符设备之前,应首先调用register_chrdev_region()或alloc_chrdev_region()函数向系统申请设备号,相应地,在调用cdev_del()函数从系统注销字符设备之后,unregister_chrdev_region()会被调用以释放原先申请的设备号,函数原型为:/* 已知起始设备的设备号的情况,from:要分配设备编号范围的初始值(次设备号常设为0),count为连续编号范围,name为编号相关联的设备名称 */ int register_chrdev_region(dev-t from, unsigned count, const char *name); /* 设备号未知,向系统动态申请未被占用的设备号的情况,函数调用成功后,会把得到的设备号放入第一个参数dev中,其优点是自动避开设备号重复的冲突 */ int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name); /* 在调用cdev_del()函数从系统注销字符设备之后,unregister_chrdev_region()被调用以释放原先申请的设备号 */ void unregister_chrdev_region(dev_t from, unsigned count);1.3 file_operations结构体file_operations结构体中的成员函数是字符设备驱动程序设计的主体内容,这些函数实际会在应用程序进行Linux的open()、write()、read()、close()等系统调用时最终被内核调用。file_operations结构体的主要成员:llseek()函数用来修改一个文件的当前读写位置,并将新位置返回,在出错时,这个函数返回一个负值。read()函数用来从设备中读取数据,成功时函数返回读取的字节数,出错时返回一个负值。write()函数向设备发送数据,成功时该函数返回写入的字节数。read()和write()如果返回0,则暗示end-of-file(EOF)unlocked_ioctl()提供设备相关控制命令的实现(既不是读操作,也不是写操作),当调用成功时,返回给调用程序一个非负值。mmap()函数将设备内存映射到进程的虚拟地址空间中,如果设备驱动未实现此函数,用户进行mmap()系统调用时将获得-ENODEV返回值。当用户空间调用Linux API函数open()打开设备文件时,设备驱动的open()函数最终被调用。驱动程序可以不实现这个函数,在这种情况下,设备的打开操作永远成功。与open()对应的是release()函数。poll()函数一般用于询问设备是否可被非阻塞地立即读写。当询问的条件未触发时,用户空间进行select()和poll()系统调用将引起进程的阻塞。aio_read()和aio_write()函数分别对与文件描述符对应的设备进行异步读、写操作。1.4 字符设备驱动的组成1.4.1 字符设备驱动模块加载与卸载函数在字符设备驱动模块加载函数中应该实现设备号的申请和cdev的注册,而在卸载函数中应实现设备号的释放和cdev的注销。Linux内核的编码习惯是为设备定义一个设备相关的结构体,该结构体包含设备所涉及的cdev、私有数据及锁等信息。1.4.2 字符设备驱动的file_operations结构体中成员函数file_operations结构体中的成员函数是字符设备驱动与内核虚拟文件系统的结口,是用户空间对Linux进行系统调用最终的落实者。大多数字符设备会实现read()、write()和ioctl()函数。由于用户空间不能直接访问内核空间的内存,因此借助了函数copy_from_user()完成用户空间缓冲区到内核空间的复制,以及copy_to_user()完成内核空间到用户空间缓冲区的复制。以上函数均返回不能被复制的字节数,因此,如果完全复制成功,返回值为0.如果复制失败,则返回负值。如果复制的内存是简单类型,如char、int、long等,则可以使用简单的put_user()和get_user()
2020年08月21日
852 阅读
0 评论
0 点赞
2020-08-21
IDR机制分析
1.IDR简述idr在linux内核中指的就是整数ID管理机制,从本质上来说,这就是一种将整数ID号和特定指针关联在一起的机制。这个机制最早是在2003年2月加入内核的,当时是作为POSIX定时器的一个补丁。现在,在内核的很多地方都可以找到idr的身影。idr机制适用在那些需要把某个整数和特定指针关联在一起的地方。举个例子,在I2C总线中,每个设备都有自己的地址,要想在总线上找到特定的设备,就必须要先发送该设备的地址。如果我们的PC是一个I2C总线上的主节点,那么要访问总线上的其他设备,首先要知道他们的ID号,同时要在pc的驱动程序中建立一个用于描述该设备的结构体。此时,问题来了,我们怎么才能将这个设备的ID号和他的设备结构体联系起来呢?最简单的方法当然是通过数组进行索引,但如果ID号的范围很大(比如32位的ID号),则用数组索引显然不可能;第二种方法是用链表,但如果网络中实际存在的设备较多,则链表的查询效率会很低。遇到这种清况,我们就可以采用idr机制,该机制内部采用radix树实现,可以很方便地将整数和指针关联起来,并且具有很高的搜索效率。2.idr源码分析idr的源码位于linux/idr.h文件,本文使用linux5.5.4内核代码2.1 idr结构体代码定义与初始化struct idr { struct radix_tree_root idr_rt; unsigned int idr_base; unsigned int idr_next; };其中,radix_tree_root定义为define radix_tree_root xarrayidr的初始化// 静态初始化 #define IDR_INIT(name) IDR_INIT_BASE(name, 0) // 动态初始化 static inline void idr_init(struct idr *idr);2.2 为idr分配内存void idr_preload(gfp_t gfp_mask);2.3 分配id号和指针关联int idr_alloc(struct idr , void ptr, int start, int end, gfp_t);2.4 通过id号搜索对应的指针void idr_find(const struct idr , unsigned long id);2.5 删除idvoid idr_remove(struct idr , unsigned long id);3.IDR使用
2020年08月21日
1,240 阅读
0 评论
0 点赞
2020-08-17
misc驱动结构分析
1.misc驱动简介misc中文名就是杂项设备\杂散设备,因为现在的硬件设备多种多样,有好些设备不好对他们进行一个单独的分类,所以就将这些设备全部归属于杂散设备,也就是misc设备,例如像adc、buzzer等这些设备一般都归属于misc中。所有的misc类设备都是字符设备,也就是misc类是字符设备中分出来的一个小类。misc类设备在应用层的操作接口:/dev/xxx,设备类对应在/sys/class/miscmisc类设备有自己的一套驱动框架,所以我们写一个misc设备的驱动直接利用的是内核中提供的驱动程序框架来实现的。misc驱动框架是对内核提供的原始的字符设备注册接口的一个类层次的封装,很多典型的字符设备都可以归于misc设备,都可以利用misc提供的驱动框架来编写驱动代码,通过misc驱动框架来进行管理。2.misc驱动框架源码分析在内核源码中,misc驱动框架源码的实现在:driver/char/misc.c,相应的头文件在:include/linux/miscdevice.h如果我们添加自己的misc类设备,那么驱动源文件最好在driver/misc这个目录下先看miscdevice.h的源代码:/* SPDX-License-Identifier: GPL-2.0 */ #ifndef _LINUX_MISCDEVICE_H #define _LINUX_MISCDEVICE_H #include <linux/major.h> #include <linux/list.h> #include <linux/types.h> #include <linux/device.h> /* * These allocations are managed by device@lanana.org. If you use an * entry that is not in assigned your entry may well be moved and * reassigned, or set dynamic if a fixed value is not justified. */ #define PSMOUSE_MINOR 1 #define MS_BUSMOUSE_MINOR 2 /* unused */ #define ATIXL_BUSMOUSE_MINOR 3 /* unused */ /*#define AMIGAMOUSE_MINOR 4 FIXME OBSOLETE */ #define ATARIMOUSE_MINOR 5 /* unused */ #define SUN_MOUSE_MINOR 6 /* unused */ #define APOLLO_MOUSE_MINOR 7 /* unused */ #define PC110PAD_MINOR 9 /* unused */ /*#define ADB_MOUSE_MINOR 10 FIXME OBSOLETE */ #define WATCHDOG_MINOR 130 /* Watchdog timer */ #define TEMP_MINOR 131 /* Temperature Sensor */ #define APM_MINOR_DEV 134 #define RTC_MINOR 135 #define EFI_RTC_MINOR 136 /* EFI Time services */ #define VHCI_MINOR 137 #define SUN_OPENPROM_MINOR 139 #define DMAPI_MINOR 140 /* unused */ #define NVRAM_MINOR 144 #define SGI_MMTIMER 153 #define STORE_QUEUE_MINOR 155 /* unused */ #define I2O_MINOR 166 #define AGPGART_MINOR 175 #define HWRNG_MINOR 183 #define MICROCODE_MINOR 184 #define IRNET_MINOR 187 #define D7S_MINOR 193 #define VFIO_MINOR 196 #define TUN_MINOR 200 #define CUSE_MINOR 203 #define MWAVE_MINOR 219 /* ACP/Mwave Modem */ #define MPT_MINOR 220 #define MPT2SAS_MINOR 221 #define MPT3SAS_MINOR 222 #define UINPUT_MINOR 223 #define MISC_MCELOG_MINOR 227 #define HPET_MINOR 228 #define FUSE_MINOR 229 #define KVM_MINOR 232 #define BTRFS_MINOR 234 #define AUTOFS_MINOR 235 #define MAPPER_CTRL_MINOR 236 #define LOOP_CTRL_MINOR 237 #define VHOST_NET_MINOR 238 #define UHID_MINOR 239 #define USERIO_MINOR 240 #define VHOST_VSOCK_MINOR 241 #define RFKILL_MINOR 242 #define MISC_DYNAMIC_MINOR 255 struct device; struct attribute_group; struct miscdevice { int minor; // 次设备号 const char *name; //设备名称 const struct file_operations *fops; // 文件操作结构体 struct list_head list; // 作为一个链表结点挂接到misc设备维护的一个链表头上去 struct device *parent; // 次设备的父设备 struct device *this_device; // 本设备的device结构体指针 const struct attribute_group **groups; const char *nodename; umode_t mode; }; extern int misc_register(struct miscdevice *misc); extern void misc_deregister(struct miscdevice *misc); /* * Helper macro for drivers that don't do anything special in the initcall. * This helps in eleminating of boilerplate code. */ #define builtin_misc_device(__misc_device) \ builtin_driver(__misc_device, misc_register) /* * Helper macro for drivers that don't do anything special in module init / exit * call. This helps in eleminating of boilerplate code. */ #define module_misc_device(__misc_device) \ module_driver(__misc_device, misc_register, misc_deregister) #define MODULE_ALIAS_MISCDEV(minor) \ MODULE_ALIAS("char-major-" __stringify(MISC_MAJOR) \ "-" __stringify(minor)) #endif 下面是misc驱动框架的源码实现,misc.c #include <linux/module.h> #include <linux/fs.h> #include <linux/errno.h> #include <linux/miscdevice.h> #include <linux/kernel.h> #include <linux/major.h> #include <linux/mutex.h> #include <linux/proc_fs.h> #include <linux/seq_file.h> #include <linux/stat.h> #include <linux/init.h> #include <linux/device.h> #include <linux/tty.h> #include <linux/kmod.h> #include <linux/gfp.h> /* * Head entry for the doubly linked miscdevice list */ static LIST_HEAD(misc_list); static DEFINE_MUTEX(misc_mtx); /* * Assigned numbers, used for dynamic minors */ #define DYNAMIC_MINORS 64 /* like dynamic majors */ static DECLARE_BITMAP(misc_minors, DYNAMIC_MINORS); #ifdef CONFIG_PROC_FS static void *misc_seq_start(struct seq_file *seq, loff_t *pos) { mutex_lock(&misc_mtx); return seq_list_start(&misc_list, *pos); } static void *misc_seq_next(struct seq_file *seq, void *v, loff_t *pos) { return seq_list_next(v, &misc_list, pos); } static void misc_seq_stop(struct seq_file *seq, void *v) { mutex_unlock(&misc_mtx); } static int misc_seq_show(struct seq_file *seq, void *v) { const struct miscdevice *p = list_entry(v, struct miscdevice, list); seq_printf(seq, "%3i %s\n", p->minor, p->name ? p->name : ""); return 0; } static const struct seq_operations misc_seq_ops = { .start = misc_seq_start, .next = misc_seq_next, .stop = misc_seq_stop, .show = misc_seq_show, }; #endif static int misc_open(struct inode *inode, struct file *file) { int minor = iminor(inode); struct miscdevice *c; int err = -ENODEV; const struct file_operations *new_fops = NULL; mutex_lock(&misc_mtx); list_for_each_entry(c, &misc_list, list) { if (c->minor == minor) { new_fops = fops_get(c->fops); break; } } if (!new_fops) { mutex_unlock(&misc_mtx); request_module("char-major-%d-%d", MISC_MAJOR, minor); mutex_lock(&misc_mtx); list_for_each_entry(c, &misc_list, list) { if (c->minor == minor) { new_fops = fops_get(c->fops); break; } } if (!new_fops) goto fail; } /* * Place the miscdevice in the file's * private_data so it can be used by the * file operations, including f_op->open below */ file->private_data = c; err = 0; replace_fops(file, new_fops); if (file->f_op->open) err = file->f_op->open(inode, file); fail: mutex_unlock(&misc_mtx); return err; } static struct class *misc_class; static const struct file_operations misc_fops = { .owner = THIS_MODULE, .open = misc_open, .llseek = noop_llseek, }; /** * misc_register - register a miscellaneous device * @misc: device structure * * Register a miscellaneous device with the kernel. If the minor * number is set to %MISC_DYNAMIC_MINOR a minor number is assigned * and placed in the minor field of the structure. For other cases * the minor number requested is used. * * The structure passed is linked into the kernel and may not be * destroyed until it has been unregistered. By default, an open() * syscall to the device sets file->private_data to point to the * structure. Drivers don't need open in fops for this. * * A zero is returned on success and a negative errno code for * failure. */ int misc_register(struct miscdevice *misc) { dev_t dev; int err = 0; bool is_dynamic = (misc->minor == MISC_DYNAMIC_MINOR); INIT_LIST_HEAD(&misc->list); mutex_lock(&misc_mtx); if (is_dynamic) { int i = find_first_zero_bit(misc_minors, DYNAMIC_MINORS); if (i >= DYNAMIC_MINORS) { err = -EBUSY; goto out; } misc->minor = DYNAMIC_MINORS - i - 1; set_bit(i, misc_minors); } else { struct miscdevice *c; list_for_each_entry(c, &misc_list, list) { if (c->minor == misc->minor) { err = -EBUSY; goto out; } } } dev = MKDEV(MISC_MAJOR, misc->minor); misc->this_device = device_create_with_groups(misc_class, misc->parent, dev, misc, misc->groups, "%s", misc->name); if (IS_ERR(misc->this_device)) { if (is_dynamic) { int i = DYNAMIC_MINORS - misc->minor - 1; if (i < DYNAMIC_MINORS && i >= 0) clear_bit(i, misc_minors); misc->minor = MISC_DYNAMIC_MINOR; } err = PTR_ERR(misc->this_device); goto out; } /* * Add it to the front, so that later devices can "override" * earlier defaults */ list_add(&misc->list, &misc_list); out: mutex_unlock(&misc_mtx); return err; } EXPORT_SYMBOL(misc_register); /** * misc_deregister - unregister a miscellaneous device * @misc: device to unregister * * Unregister a miscellaneous device that was previously * successfully registered with misc_register(). */ void misc_deregister(struct miscdevice *misc) { int i = DYNAMIC_MINORS - misc->minor - 1; if (WARN_ON(list_empty(&misc->list))) return; mutex_lock(&misc_mtx); list_del(&misc->list); device_destroy(misc_class, MKDEV(MISC_MAJOR, misc->minor)); if (i < DYNAMIC_MINORS && i >= 0) clear_bit(i, misc_minors); mutex_unlock(&misc_mtx); } EXPORT_SYMBOL(misc_deregister); static char *misc_devnode(struct device *dev, umode_t *mode) { struct miscdevice *c = dev_get_drvdata(dev); if (mode && c->mode) *mode = c->mode; if (c->nodename) return kstrdup(c->nodename, GFP_KERNEL); return NULL; } // misc_init()函数是misc驱动框架模块注册时的一个初始化函数,只有进行了初始化,我们才能够利用misc提供的框架来进行编写misc设备驱动程序和管理misc类设备。 static int __init misc_init(void) { int err; struct proc_dir_entry *ret; ret = proc_create_seq("misc", 0, NULL, &misc_seq_ops); // 在sys文件系统下创建misc设备类 misc_class = class_create(THIS_MODULE, "misc"); err = PTR_ERR(misc_class); if (IS_ERR(misc_class)) goto fail_remove; err = -EIO; // 注册misc字符设备,主设备是10 if (register_chrdev(MISC_MAJOR, "misc", &misc_fops)) goto fail_printk; misc_class->devnode = misc_devnode; return 0; fail_printk: pr_err("unable to get major %d for misc devices\n", MISC_MAJOR); class_destroy(misc_class); fail_remove: if (ret) remove_proc_entry("misc", NULL); return err; } subsys_initcall(misc_init);
2020年08月17日
1,116 阅读
0 评论
0 点赞
2020-08-12
Linux内核中链表list文件结构分析
1. 函数原型内核中的链表结构如下,只有前后两个指针,没有数据项,可以很方便的构成双向链表struct list_head { struct list_head *next, *prev; };1.1. static inline void INIT_LIST_HEAD(struct list_head *list)运行的时候初始化链表,两个指针都指向结点自己的地址static inline void INIT_LIST_HEAD(struct list_head *list) { WRITE_ONCE(list->next, list); list->prev = list; }1.2. static inline void list_add(struct list_head new, struct list_head head);从指定结点后面插入一个结点,new为要插入的新节点的地址,head为要插入的结点,新结点从head结点后面插入/** * list_add - add a new entry * @new: new entry to be added * @head: list head to add it after * * Insert a new entry after the specified head. * This is good for implementing stacks. */ static inline void list_add(struct list_head *new, struct list_head *head) { __list_add(new, head, head->next); }其中,__list_add()函数定义如下,在知道前后结点的情况下,插入结点/* * Insert a new entry between two known consecutive entries. * * This is only for internal list manipulation where we know * the prev/next entries already! */ static inline void __list_add(struct list_head *new, struct list_head *prev, struct list_head *next) { if (!__list_add_valid(new, prev, next)) return; next->prev = new; new->next = next; new->prev = prev; WRITE_ONCE(prev->next, new); }1.3. static inline void list_add_tail(struct list_head new, struct list_head head)从链表尾部插入结点/** * list_add_tail - add a new entry * @new: new entry to be added * @head: list head to add it before * * Insert a new entry before the specified head. * This is useful for implementing queues. */ static inline void list_add_tail(struct list_head *new, struct list_head *head) { __list_add(new, head->prev, head); }1.4. static inline void list_del(struct list_head *entry)两个宏定义,删除下来的prev、next指针指向这两个特殊值,这样设置是为了保证不在链表中的结点项不可访问--对LIST_POISON1和LIST_POISON2的访问都将引起页故障。 /* * These are non-NULL pointers that will result in page faults * under normal circumstances, used to verify that nobody uses * non-initialized list entries. */ #define LIST_POISON1 ((void *) 0x100 + POISON_POINTER_DELTA) #define LIST_POISON2 ((void *) 0x122 + POISON_POINTER_DELTA)static inline void list_del(struct list_head *entry) { __list_del_entry(entry); entry->next = LIST_POISON1; entry->prev = LIST_POISON2; }其中__list_del_entry()函数定义如下:/* * Delete a list entry by making the prev/next entries * point to each other. * * This is only for internal list manipulation where we know * the prev/next entries already! */ static inline void __list_del(struct list_head * prev, struct list_head * next) { next->prev = prev; WRITE_ONCE(prev->next, next); } /* * Delete a list entry and clear the 'prev' pointer. * * This is a special-purpose list clearing method used in the networking code * for lists allocated as per-cpu, where we don't want to incur the extra * WRITE_ONCE() overhead of a regular list_del_init(). The code that uses this * needs to check the node 'prev' pointer instead of calling list_empty(). */ static inline void __list_del_clearprev(struct list_head *entry) { __list_del(entry->prev, entry->next); entry->prev = NULL; } /** * list_del - deletes entry from list. * @entry: the element to delete from the list. * Note: list_empty() on entry does not return true after this, the entry is * in an undefined state. */ static inline void __list_del_entry(struct list_head *entry) { if (!__list_del_entry_valid(entry)) return; __list_del(entry->prev, entry->next); }1.5. static inline void list_replace(struct list_head old,struct list_head new)替换链表中的结点,/** * list_replace - replace old entry by new one * @old : the element to be replaced * @new : the new element to insert * * If @old was empty, it will be overwritten. */ static inline void list_replace(struct list_head *old, struct list_head *new) { new->next = old->next; new->next->prev = new; new->prev = old->prev; new->prev->next = new; }1.6. static inline void list_replace_init(struct list_head old,struct list_head new)替换,将被替换的结点初始化为一个新链表static inline void list_replace_init(struct list_head *old, struct list_head *new) { list_replace(old, new); INIT_LIST_HEAD(old); }1.7. static inline void list_swap(struct list_head entry1,struct list_head entry2)交换两个结点/** * list_swap - replace entry1 with entry2 and re-add entry1 at entry2's position * @entry1: the location to place entry2 * @entry2: the location to place entry1 */ static inline void list_swap(struct list_head *entry1, struct list_head *entry2) { struct list_head *pos = entry2->prev; list_del(entry2); list_replace(entry1, entry2); if (pos == entry1) pos = entry2; list_add(entry1, pos); } 1.8. static inline void list_del_init(struct list_head *entry)删除一项并初始化/** * list_del_init - deletes entry from list and reinitialize it. * @entry: the element to delete from the list. */ static inline void list_del_init(struct list_head *entry) { __list_del_entry(entry); INIT_LIST_HEAD(entry); }1.9. static inline void list_move(struct list_head list, struct list_head head)搬移操作,将原本属于链表的一个结点移动到另一个链表的操作/** * list_move - delete from one list and add as another's head * @list: the entry to move * @head: the head that will precede our entry */ static inline void list_move(struct list_head *list, struct list_head *head) { __list_del_entry(list); list_add(list, head); }1.10. static inline void list_move_tail(struct list_head list,struct list_head head)/** * list_move_tail - delete from one list and add as another's tail * @list: the entry to move * @head: the head that will follow our entry */ static inline void list_move_tail(struct list_head *list, struct list_head *head) { __list_del_entry(list); list_add_tail(list, head); }1.11. static inline void list_bulk_move_tail(struct list_head head, struct list_head first, struct list_head *last)/** * list_bulk_move_tail - move a subsection of a list to its tail * @head: the head that will follow our entry * @first: first entry to move * @last: last entry to move, can be the same as first * * Move all entries between @first and including @last before @head. * All three entries must belong to the same linked list. */ static inline void list_bulk_move_tail(struct list_head *head, struct list_head *first, struct list_head *last) { first->prev->next = last->next; last->next->prev = first->prev; head->prev->next = first; first->prev = head->prev; last->next = head; head->prev = last; } 1.12. static inline int list_is_first(const struct list_head list,const struct list_head head)判断结点是否为首结点/** * list_is_first -- tests whether @list is the first entry in list @head * @list: the entry to test * @head: the head of the list */ static inline int list_is_first(const struct list_head *list, const struct list_head *head) { return list->prev == head; }1.13. static inline int list_is_last(const struct list_head list, const struct list_head head)判断结点是否为尾结点/** * list_is_last - tests whether @list is the last entry in list @head * @list: the entry to test * @head: the head of the list */ static inline int list_is_last(const struct list_head *list, const struct list_head *head) { return list->next == head; }1.14. static inline int list_empty(const struct list_head *head)判断是否是一个空链表/** * list_empty - tests whether a list is empty * @head: the list to test. */ static inline int list_empty(const struct list_head *head) { return READ_ONCE(head->next) == head; }1.15. static inline int list_empty_careful(const struct list_head *head)/** * list_empty_careful - tests whether a list is empty and not being modified * @head: the list to test * * Description: * tests whether a list is empty _and_ checks that no other CPU might be * in the process of modifying either member (next or prev) * * NOTE: using list_empty_careful() without synchronization * can only be safe if the only activity that can happen * to the list entry is list_del_init(). Eg. it cannot be used * if another CPU could re-list_add() it. */ static inline int list_empty_careful(const struct list_head *head) { struct list_head *next = head->next; return (next == head) && (next == head->prev); }1.16. static inline void list_rotate_left(struct list_head *head)翻转链表/** * list_rotate_left - rotate the list to the left * @head: the head of the list */ static inline void list_rotate_left(struct list_head *head) { struct list_head *first; if (!list_empty(head)) { first = head->next; list_move_tail(first, head); } } 1.17. static inline void list_rotate_to_front(struct list_head list,struct list_head head)/** * list_rotate_to_front() - Rotate list to specific item. * @list: The desired new front of the list. * @head: The head of the list. * * Rotates list so that @list becomes the new front of the list. */ static inline void list_rotate_to_front(struct list_head *list, struct list_head *head) { /* * Deletes the list head from the list denoted by @head and * places it as the tail of @list, this effectively rotates the * list so that @list is at the front. */ list_move_tail(head, list); }1.18. static inline int list_is_singular(const struct list_head *head)判断一个链表是否只有一项/** * list_is_singular - tests whether a list has just one entry. * @head: the list to test. */ static inline int list_is_singular(const struct list_head *head) { return !list_empty(head) && (head->next == head->prev); } 1.19. static inline void list_cut_position(struct list_head list,struct list_head head, struct list_head *entry)将一个链表拆分为两个/** * list_cut_position - cut a list into two * @list: a new list to add all removed entries * @head: a list with entries * @entry: an entry within head, could be the head itself * and if so we won't cut the list * * This helper moves the initial part of @head, up to and * including @entry, from @head to @list. You should * pass on @entry an element you know is on @head. @list * should be an empty list or a list you do not care about * losing its data. * */ static inline void list_cut_position(struct list_head *list, struct list_head *head, struct list_head *entry) { if (list_empty(head)) return; if (list_is_singular(head) && (head->next != entry && head != entry)) return; if (entry == head) INIT_LIST_HEAD(list); else __list_cut_position(list, head, entry); }1.20. static inline void list_cut_before(struct list_head list,struct list_head head,struct list_head *entry)/** * list_cut_before - cut a list into two, before given entry * @list: a new list to add all removed entries * @head: a list with entries * @entry: an entry within head, could be the head itself * * This helper moves the initial part of @head, up to but * excluding @entry, from @head to @list. You should pass * in @entry an element you know is on @head. @list should * be an empty list or a list you do not care about losing * its data. * If @entry == @head, all entries on @head are moved to * @list. */ static inline void list_cut_before(struct list_head *list, struct list_head *head, struct list_head *entry) { if (head->next == entry) { INIT_LIST_HEAD(list); return; } list->next = head->next; list->next->prev = list; list->prev = entry->prev; list->prev->next = list; head->next = entry; entry->prev = head; }1.21. static inline void list_splice(const struct list_head list,struct list_head head)连接两个链表/** * list_splice - join two lists, this is designed for stacks * @list: the new list to add. * @head: the place to add it in the first list. */ static inline void list_splice(const struct list_head *list, struct list_head *head) { if (!list_empty(list)) __list_splice(list, head, head->next); }1.22. static inline void list_splice_tail(struct list_head list,struct list_head head)/** * list_splice_tail - join two lists, each list being a queue * @list: the new list to add. * @head: the place to add it in the first list. */ static inline void list_splice_tail(struct list_head *list, struct list_head *head) { if (!list_empty(list)) __list_splice(list, head->prev, head); }1.23. static inline void list_splice_init(struct list_head list, struct list_head head)/** * list_splice_init - join two lists and reinitialise the emptied list. * @list: the new list to add. * @head: the place to add it in the first list. * * The list at @list is reinitialised */ static inline void list_splice_init(struct list_head *list, struct list_head *head) { if (!list_empty(list)) { __list_splice(list, head, head->next); INIT_LIST_HEAD(list); } }1.24. static inline void list_splice_tail_init(struct list_head list, struct list_head head)/** * list_splice_tail_init - join two lists and reinitialise the emptied list * @list: the new list to add. * @head: the place to add it in the first list. * * Each of the lists is a queue. * The list at @list is reinitialised */ static inline void list_splice_tail_init(struct list_head *list, struct list_head *head) { if (!list_empty(list)) { __list_splice(list, head->prev, head); INIT_LIST_HEAD(list); } }2. 使用的宏定义2.1. LIST_HEAD_INIT#define LIST_HEAD_INIT(name) { &(name), &(name) }2.2. LIST_HEAD#define LIST_HEAD(name) \ struct list_head name = LIST_HEAD_INIT(name) 2.3. list_entry#define list_entry(ptr, type, member) \ container_of(ptr, type, member)2.4. list_first_entry#define list_first_entry(ptr, type, member) \ list_entry((ptr)->next, type, member)2.5. list_last_entry#define list_last_entry(ptr, type, member) \ list_entry((ptr)->prev, type, member)2.6. list_first_entry_or_null#define list_first_entry_or_null(ptr, type, member) ({ \ struct list_head *head__ = (ptr); \ struct list_head *pos__ = READ_ONCE(head__->next); \ pos__ != head__ ? list_entry(pos__, type, member) : NULL; \ })2.7. list_next_entry#define list_next_entry(pos, member) \ list_entry((pos)->member.next, typeof(*(pos)), member)2.8. list_prev_entry#define list_prev_entry(pos, member) \ list_entry((pos)->member.prev, typeof(*(pos)), member)2.9. list_for_each#define list_for_each(pos, head) \ for (pos = (head)->next; pos != (head); pos = pos->next)2.10. list_for_each_prev#define list_for_each_prev(pos, head) \ for (pos = (head)->prev; pos != (head); pos = pos->prev)2.11. list_for_each_safe#define list_for_each_safe(pos, n, head) \ for (pos = (head)->next, n = pos->next; pos != (head); \ pos = n, n = pos->next)2.12. list_for_each_prev_safe#define list_for_each_prev_safe(pos, n, head) \ for (pos = (head)->prev, n = pos->prev; \ pos != (head); \ pos = n, n = pos->prev)2.13. list_for_each_entry#define list_for_each_entry(pos, head, member) \ for (pos = list_first_entry(head, typeof(*pos), member); \ &pos->member != (head); \ pos = list_next_entry(pos, member))2.14. list_for_each_entry_reverse#define list_for_each_entry_reverse(pos, head, member) \ for (pos = list_last_entry(head, typeof(*pos), member); \ &pos->member != (head); \ pos = list_prev_entry(pos, member))2.15. list_prepare_entry#define list_prepare_entry(pos, head, member) \ ((pos) ? : list_entry(head, typeof(*pos), member))2.16. list_for_each_entry_continue#define list_for_each_entry_continue(pos, head, member) \ for (pos = list_next_entry(pos, member); \ &pos->member != (head); \ pos = list_next_entry(pos, member))2.17. list_for_each_entry_from_reverse#define list_for_each_entry_from_reverse(pos, head, member) \ for (; &pos->member != (head); \ pos = list_prev_entry(pos, member))2.18. list_for_each_entry_safe#define list_for_each_entry_safe(pos, n, head, member) \ for (pos = list_first_entry(head, typeof(*pos), member), \ n = list_next_entry(pos, member); \ &pos->member != (head); \ pos = n, n = list_next_entry(n, member))2.19. list_for_each_entry_safe_continue#define list_for_each_entry_safe_continue(pos, n, head, member) \ for (pos = list_next_entry(pos, member), \ n = list_next_entry(pos, member); \ &pos->member != (head); \ pos = n, n = list_next_entry(n, member))2.20. list_for_each_entry_safe_from#define list_for_each_entry_safe_from(pos, n, head, member) \ for (n = list_next_entry(pos, member); \ &pos->member != (head); \ pos = n, n = list_next_entry(n, member))2.21. list_for_each_entry_safe_reverse#define list_for_each_entry_safe_reverse(pos, n, head, member) \ for (pos = list_last_entry(head, typeof(*pos), member), \ n = list_prev_entry(pos, member); \ &pos->member != (head); \ pos = n, n = list_prev_entry(n, member))2.22. list_safe_reset_next#define list_safe_reset_next(pos, n, member) \ n = list_next_entry(pos, member)
2020年08月12日
875 阅读
0 评论
0 点赞
2020-08-10
Linux-系统操作
1.帮助命令manman是manual的缩写用法: man 命令man 也是一条命令,分为9章,可以使用man命令获得man的帮助,如man 7 manhelp内部命令使用help帮助,help 命令外部命令使用help帮助,命令 --help使用type 命令可以查看是内部命令还是外部命令infoinfo帮助比help更详细2.文件命令pwd命令显示当前目录的绝对路径ls命令查看当前目录下的文件基本语法:ls [选项,选型] 参数......常用选项-l 长格式显示文件-a 显示隐藏文件-r 逆序显示-t 按照时间顺序显示-R 递归显示cd命令更改当前的操作目录常用操作:cd - :返回上一目录mkdir命令建立目录常用选项:-p 建立多级目录rmdir命令删除空目录cp命令复制文件和目录基本语法:cp [选项] 文件路径cp [选项] 文件... 路径常用选项:-r 复制目录,不加选项只能复制文件-p 复制时保留用户、权限、时间等文件属性-a 等同于-dpR,显示复制过程mv命令移动或者重命名文件基本语法:mv [选项] 源文件 目标文件mv [选项] 源文件 目录rm命令删除文件常用选项:-r 删除目录,非空的-f 删除文件不进行提示通配符定义:shell 内建的符号用途:操作多个相似的文件常用的通配符:* 匹配任何字符串? 匹配一个字符串[xyz] 匹配xyz任意一个字符[a-z] 匹配一个范围[!xyz] 不匹配3. 文本查看命令cat 文本内容显示到终端head 查看文件开头tail 查看文件结尾常用参数:-f 文件内容更新后,显示信息同步更新wc 统计文件内容信息moreless4.打包与压缩命令打包命令tar命令是Linux中的备份命令,在打包完成后,需要对文件进行压缩,压缩的命令是gzip和bzip2.经常使用的扩展名是.tar.gz .tar.bz2 .tgz .tbz2常用选项: c 打包x 解包f 指定操作类型为文件压缩和解压缩可以先使用tar命令打包,再单独使用命令gzip和bzip2命令。但在日常的使用中,通常和tar命令配合使用常用选项:-z: gzip格式压缩和解压缩-j: bzip2格式压缩和解压缩5.Vi编辑器进入vim后即为正常模式,可以复制粘贴。按i进入插入模式,可以进行文本的输入。从插入模式退出,按ESC进入正常模式,然后输入:或者\进入命令模式,在命令行下输入:wq,:q可退出。正常模式进入其他模式的转换命令i 进入插入模式v 进入可视化模式: 进入命令模式esc 从其他模式回到正常模式基本操作:使用h j k l控制上下左右的移动,一些基本操作y 复制一般都是按行复制,使用yy命令,使用数字加yy可以复制多行,使用y$可以复制从光标到行尾全部内容d 剪切dd剪切一整行p 粘贴u 撤销ctrl+r 重做,把撤销指令重做x 删除单个字符r 替换单个字符G 定位指定的行数字加G定位到指定行^ 定位到行首$ 定位到行尾命令模式:w 写入:q 退出:! 执行shell命令:s 替换使用方法s/old/new,只是用s只替换光标所在行的内容,使用%s可替换所有行的第一个字符。使用%s/old/new/g可以替换所有,global3,5s/old/new是指替换3到5行的/ 查找使用n查看下一个查找到的内容,使用shift+n查看上一个:set 设置命令:set nu, :set nonu可视模式进入可视模式的方式v 字符可视模式V 行可视模式ctrl+v 块可视化模式6.用户和用户组管理及密码管理用户管理常用命令useradd 新建用户useradd 用户名用户是否存在,使用 id 用户名 可以知道用户是否存在userdel 删除用户userdel 用户名即可删除用户,但会保留用户的家目录使用-r参数可以删除用户目录passwd 修改用户密码usermod 修改用户属性可以修改用户家目录、用户组等信息chage 修改用户属性可以修改用户的生命周期用户组管理命令groupadd 新建用户组groupdel 删除用户组su和sudo命令su 切换用户su - USERNAMEsudo 以其他用户身份执行命令visudo 设置需要使用sudo的用户(组)用户和用户组配置文件/etc/passwd/ /etc/shadow//etc/group/7.文件权限文件类型:- 普通文件 d 目录文件b 块特殊文件c 字符特殊文件l 符号链接(类似Windows快捷方式)f 命名管道s 套接字文件文件权限的表示字符权限的表示法:r 读w 写x 执行数字权限的表示法:r = 4w = 2x = 1如 rw-r-xr--意为rw- 文件属主的权限r-x 文件属组的权限r-- 其它用户的权限文件权限的修改root用户权限不受限chmod 更该文件、目录权限字符表示法:u g o a参数表示用户属主、属组、其他用户、和全部u=x,u+x,u-x设置、增加、减少权限chmod u+x /tmp/testfile数字表示法:chmod 755 /tmp/testfilechown 更改属主、属组chgrp 可以单独改属组,不常用使用ctrl+r,可以查找历史命令
2020年08月10日
792 阅读
0 评论
0 点赞
1
...
37
38