首页 > 代码库 > 后台开发知识点总结(一、Linux和OS)
后台开发知识点总结(一、Linux和OS)
偶然在知乎上看到想要从事linux后台开发需要的能力集锦,总结的挺全面的,鉴于自己贫弱的记忆力,还是在这里总结一下供以后查看,顺便检验一下自己。
1、 命令:netstat tcpdump ipcs ipcrm 这四个命令的熟练掌握程度基本上能体现实际开发和调试程序的经验
在《TCP/IP》协议一书中,经常使用到netstat和tcpdump这两个命令,netstat常用于显示各种网络信息与自己的主机信息,例如路由表信息。tcpdump用于网络数据包的截获分析,例如三次握手,数据交换等等的显示。这里推荐一个更好用的工具wireshark,有比较好的交互界面,可以设置过滤信息等等,做爬虫,分析网络问题的利器。
下面给出几个简单的例子,具体的使用可以参照linux的man命令或者鸟哥的私房菜。
- Proto :网络的封包协议,主要分为 TCP 与 UDP 封包;
- Recv-Q:非由用户程序链接到此socket 的复制的总 bytes 数;
- Send-Q:非由进程主机传送过来的 acknowledged 总 bytes 数;
- Local Address :本地端的IP:port 情况
- Foreign Address:进程主机的 IP:port 情况
- State:联机状态,主要有建立(ESTABLISED)及监听(LISTEN);
这里显示的信息从左至右分别是时间;源地址到目的地址;报文的flags,S是SYN,F是FIN,.是无标记;报文的序列号;下次期望的序列号;接收缓存的窗口大小。这些在《TCP/IP》卷一有详细的论述。
ipcs和ipcrm命令是用于显示ipc信息和移除ipc消息对象的命令。这里首先要对ipc有个大致概念。IPC是(interprocess communication)的简称,是运行在操作系统上的不同进程间通讯的方式。
使用命令ipcs -a可以得到下面的信息,可以看到这里显示的方式有三种,下面介绍进程间通讯时再详细讲。
- 共享内存
- 信号量
- 消息队列
lijun0914:~/workspace $ ipcs -a
------ Shared Memory Segments --------
key shmid owner perms bytes nattch status
------ Semaphore Arrays --------
key semid owner perms nsems
------ Message Queues --------
key msqid owner perms used-bytes messages
2、cpu 内存 硬盘 等等与系统性能调试相关的命令必须熟练掌握,设置修改权限 tcp网络状态查看 各进程状态 抓包相关等相关命令 必须熟练掌握
这一条后面提到的tcp网络状态查看,抓包其实在上一条的命令中已经涵盖了。设置修改权限的chmod感觉没什么可将的,就是修改文件的访问权限,记住读写可执行的数值为4,2,1,u,g,o为用户,用户当前组,其他用户,使用命令设置相应用户的权限就可以了。看一下简单的例子就明白了。
chmod u+x file 给file的属主增加执行权限
chmod 751 file 给file的属主分配读、写、执行(7)的权限,给file的所在组分配读、执行(5)的权限,给其他用户分配执行(1)的权限
chmod u=rwx,g=rx,o=x file 上例的另一种形式
chmod =r file 为所有用户分配读权限
chmod 444 file 同上例
chmod a-wx,a+r file 同上例
chmod -R u+r directory 递归地给directory目录下所有文件和子目录的属主分配读的权限
chmod 4755 设置用ID,给属主分配读、写和执行权限,给组和其他用户分配读、执行的权限。
观察运行中的进程状态可以使用静态的ps和动态的top以及top的增强版htop。这些命令可以统计各个进程的CPU和内存MEM使用率。当然还有专门针对cpu,内存,io做监控的各个命令,mpstat,vmstat,iostat。
htop
这里会有一些进程优先级的概念,PRI越低越先被CPU执行,PRI(new)=PRI(old)+nice,人越不nice越爱抢嘛,很好记。
其中nice和renice可以设置优先级,区别是nice是在进程启动前调整,renice可以在进程运行中动态调整。
3、awk sed需掌握
有时候你也许会想要提取多行的某列信息,或者想要对文本按某一规则进行处理,这时候你可能会选择python或者shell编写脚本,不过awk和sed可能会是更好的选择,因为需求经常一行就可以搞定。AWK是文本处理语言,常用来进行查询,支持正则。sed是用程序进行文本编辑的流编辑器,只支持简单的条件处理,使用label。sed同样使用正则匹配,可以对文本进行修改,如果你对vim熟悉的话,sed上手会非常快,因为他们有许多相似的命令,例如s/a/b/g 这样的文本替换。
关于工具使用没有什么特别的,注意awk是语言,可以使用if else等逻辑判断,也可以使用system运行shell指令。
这里直接给三个链接。
sed:http://coolshell.cn/articles/9070.html
awk:http://www.delightpress.com.tw/bookRead/skns00004_read.pdf
http://wanggen.myweb.hinet.net/ach3/ach3.html?MywebPageId=2016161473995373791#sed_and_awk
4、共享内存的使用实现原理、然后共享内存段被映射进进程空间之后,存在于进程空间的什么位置?共享内存段最大限制是多少?
共享内存区是可用IPC形式里面最快的。共享内存允许多个进程同时访问同一内存区,进程会将内存区映射到自己的地址空间中。这样进程间数据的传递不再涉及内核,减少了数据复制的动作。例如一个客户从服务器读的操作,使用管道消息队列等形式的话,需要内核将数据复制到进程空间的服务器上,然后服务器写到内核空间的IPC上。这样一次读取或者写入需要将数据复制两次。
使用共享内存
- 进程必须首先分配它
- 随后需要访问这个共享内存块的每一个进程都必须将这个共享内存绑定到自己的地址空间中
- 当完成通信之后,所有进程都将脱离共享内存,并且由一个进程释放该共享内存块
在/proc/sys/kernel/目录下,记录着共享内存的一些限制,如一个共享内存区的最大字节数shmmax,系统范围内最大共享内存区标识符数shmmni等,可以手工对其调整,但不推荐这样做。
共享内存的使用,主要有以下几个API:ftok()、shmget()、shmat()、shmdt()及shmctl()。
#include <sys/shm.h>
void *shmat(int shm_id, const void *shm_addr, int shmflg);
int shmctl(int shm_id, int cmd, struct shmid_ds *buf);
int shmdt(const void *shm_addr);
int shmget(key_t key, size_t size, int shmflg);
shmget():创建一个新的共享内存区,或者访问一个已经存在的内存区。
shmat():创建或者打开后,通过shmat把它连接到调用进程的地址空间。
shmdt():断开连接的内存区。当一个进程终止时,它所有链接的共享内存区都会自动断掉,注意这个函数并不删除共享内存区。
shmctl():提供对共享内存区的多种操作,例如删除。
这里需要提及一下mmap
mmap系统调用并不是完全为了用于共享内存而设计的。它本身提供了不同于一般对普通文件的访问方式,进程可以像读写内存一样对普通文件的操作。而Posix或系统V的共享内存IPC则纯粹用于共享目的,当然mmap()实现共享内存也是其主要应用之一。
mmap系统调用使得进程之间通过映射同一个普通文件实现共享内存。普通文件被映射到进程地址空间后,进程可以像访问普通内存一样对文件进行访问,不必再 调用read(),write()等操作。mmap并不分配空间, 只是将文件映射到调用进程的地址空间里, 然后你就可以用memcpy等操作写文件, 而不用write()了.写完后用msync()同步一下, 你所写的内容就保存到文件里了. 不过这种方式没办法增加文件的长度, 因为要映射的长度在调用mmap()的时候就决定了.
简单说就是把一个文件的内容在内存里面做一个映像,内存比磁盘快些。
PS:不是所有文件都可以内存映射。例如访问终端或套接字的描述符,必须使用read和write或者其变体来访问。
这里不介绍shm_open这些POSIX操作了,更详细的信息可以参考《unix网络编程》(卷二)第12-14章
顺便提一下区别:
Both methods are viable. mmap method is a little bit more restrictive then shmget, but easier to use. shmget is the old System V shared memory model and has the widest support. mmap/shm_open is the new POSIX way to do shared memory and is easier to use. If your OS permits the use of POSIX shared memory then I would suggest going with that.
Some hints:
- If you create your children via fork then mmap with MAP_ANONYMOUS |
MAP_SHARED is by far the easiest way - just one call. - If you start the processes independently, but can supply them with a
shared memory name then shm_open (+ ftruncate) + mmap with MAP_SHARED
is two/three calls. Requires librt on some OSes. - If your OS has /dev/shm/ then shm_open is equivalent to opening a
file in /dev/shm/.
注意共享内存本身不提供同步技术,需要自己使用互斥或者信号量来保证同步。
5、c++进程内存空间分布(注意各部分的内存地址谁高谁低,注意栈从高道低分配,堆从低到高分配)
在《深入理解计算机系统》(第九章、虚拟存储器)中对动态存储器分配有比较详细的讲解。
6、ELF是什么?其大小与程序中全局变量的是否初始化有什么关系(注意.bss段)
ELF(Executable and Linking Format)是一种对象文件的格式,用于定义不同类型的对象文件(Object files)中都放了什么东西、以及都以什么样的格式去放这些东西。它自最早在 System V 系统上出现后,被 xNIX 世界所广泛接受,作为缺省的二进制文件格式来使用。可以说,ELF是构成众多xNIX系统的基础之一。
ELF文件有三种类型:
- 可重定位的对象文件(Relocatable file) 由汇编器汇编生成的 .o 文件
- 可执行的对象文件(Executable file) 可执行应用程序
- 可被共享的对象文件(Shared object file) 动态库文件,也即 .so 文件
在Unix下使用readelf命令来显示可执行程序的信息,功能与objdump相似,但是显示的更加具体。
下面是我做CSAPP的bomb实验的可执行文件信息。
lijun0914:~/workspace/bomb $ readelf -all bomb
ELF Header:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
Class: ELF64
Data: 2‘s complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: EXEC (Executable file)
Machine: Advanced Micro Devices X86-64
Version: 0x1
Entry point address: 0x400c90
Start of program headers: 64 (bytes into file)
Start of section headers: 18616 (bytes into file)
Flags: 0x0
Size of this header: 64 (bytes)
Size of program headers: 56 (bytes)
Number of program headers: 9
Size of section headers: 64 (bytes)
Number of section headers: 36
Section header string table index: 33
Section Headers:
[Nr] Name Type Address Offset
Size EntSize Flags Link Info Align
[ 0] NULL 0000000000000000 00000000
0000000000000000 0000000000000000 0 0 0
[ 1] .interp PROGBITS 0000000000400238 00000238
000000000000001c 0000000000000000 A 0 0 1
[ 2] .note.ABI-tag NOTE 0000000000400254 00000254
0000000000000020 0000000000000000 A 0 0 4
[ 3] .note.gnu.build-i NOTE 0000000000400274 00000274
0000000000000024 0000000000000000 A 0 0 4
[ 4] .gnu.hash GNU_HASH 0000000000400298 00000298
0000000000000030 0000000000000000 A 5 0 8
[ 5] .dynsym DYNSYM 00000000004002c8 000002c8
0000000000000300 0000000000000018 A 6 1 8
[ 6] .dynstr STRTAB 00000000004005c8 000005c8
000000000000016d 0000000000000000 A 0 0 1
[ 7] .gnu.version VERSYM 0000000000400736 00000736
0000000000000040 0000000000000002 A 5 0 2
[ 8] .gnu.version_r VERNEED 0000000000400778 00000778
0000000000000060 0000000000000000 A 6 1 8
[ 9] .rela.dyn RELA 00000000004007d8 000007d8
0000000000000060 0000000000000018 A 5 0 8
[10] .rela.plt RELA 0000000000400838 00000838
0000000000000288 0000000000000018 A 5 12 8
[11] .init PROGBITS 0000000000400ac0 00000ac0
000000000000000e 0000000000000000 AX 0 0 4
[12] .plt PROGBITS 0000000000400ad0 00000ad0
00000000000001c0 0000000000000010 AX 0 0 16
[13] .text PROGBITS 0000000000400c90 00000c90
0000000000001614 0000000000000000 AX 0 0 16
[14] .fini PROGBITS 00000000004022a4 000022a4
0000000000000009 0000000000000000 AX 0 0 4
[15] .rodata PROGBITS 00000000004022b0 000022b0
00000000000004e5 0000000000000000 A 0 0 16
[16] .eh_frame_hdr PROGBITS 0000000000402798 00002798
0000000000000104 0000000000000000 A 0 0 4
[17] .eh_frame PROGBITS 00000000004028a0 000028a0
0000000000000454 0000000000000000 A 0 0 8
[18] .init_array INIT_ARRAY 0000000000602df8 00002df8
0000000000000008 0000000000000000 WA 0 0 8
[19] .fini_array FINI_ARRAY 0000000000602e00 00002e00
0000000000000008 0000000000000000 WA 0 0 8
[20] .jcr PROGBITS 0000000000602e08 00002e08
0000000000000008 0000000000000000 WA 0 0 8
[21] .dynamic DYNAMIC 0000000000602e10 00002e10
00000000000001d0 0000000000000010 WA 6 0 8
[22] .got PROGBITS 0000000000602fe0 00002fe0
0000000000000008 0000000000000008 WA 0 0 8
[23] .got.plt PROGBITS 0000000000602fe8 00002fe8
00000000000000f0 0000000000000008 WA 0 0 8
[24] .data PROGBITS 00000000006030e0 000030e0
0000000000000660 0000000000000000 WA 0 0 32
[25] .bss NOBITS 0000000000603740 00003740
00000000000006d0 0000000000000000 WA 0 0 32
[26] .comment PROGBITS 0000000000000000 00003740
0000000000000053 0000000000000001 MS 0 0 1
[27] .debug_aranges PROGBITS 0000000000000000 00003793
0000000000000030 0000000000000000 0 0 1
[28] .debug_info PROGBITS 0000000000000000 000037c3
00000000000007a3 0000000000000000 0 0 1
[29] .debug_abbrev PROGBITS 0000000000000000 00003f66
000000000000021f 0000000000000000 0 0 1
[30] .debug_line PROGBITS 0000000000000000 00004185
0000000000000161 0000000000000000 0 0 1
[31] .debug_str PROGBITS 0000000000000000 000042e6
00000000000002f3 0000000000000001 MS 0 0 1
[32] .debug_loc PROGBITS 0000000000000000 000045d9
0000000000000188 0000000000000000 0 0 1
[33] .shstrtab STRTAB 0000000000000000 00004761
0000000000000153 0000000000000000 0 0 1
[34] .symtab SYMTAB 0000000000000000 000051b8
0000000000000eb8 0000000000000018 35 57 8
[35] .strtab STRTAB 0000000000000000 00006070
00000000000006b6 0000000000000000 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings), l (large)
I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)
O (extra OS processing required) o (OS specific), p (processor specific)
There are no section groups in this file.
Program Headers:
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
PHDR 0x0000000000000040 0x0000000000400040 0x0000000000400040
0x00000000000001f8 0x00000000000001f8 R E 8
INTERP 0x0000000000000238 0x0000000000400238 0x0000000000400238
0x000000000000001c 0x000000000000001c R 1
[Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
LOAD 0x0000000000000000 0x0000000000400000 0x0000000000400000
0x0000000000002cf4 0x0000000000002cf4 R E 200000
LOAD 0x0000000000002df8 0x0000000000602df8 0x0000000000602df8
0x0000000000000948 0x0000000000001018 RW 200000
DYNAMIC 0x0000000000002e10 0x0000000000602e10 0x0000000000602e10
0x00000000000001d0 0x00000000000001d0 RW 8
NOTE 0x0000000000000254 0x0000000000400254 0x0000000000400254
0x0000000000000044 0x0000000000000044 R 4
GNU_EH_FRAME 0x0000000000002798 0x0000000000402798 0x0000000000402798
0x0000000000000104 0x0000000000000104 R 4
GNU_STACK 0x0000000000000000 0x0000000000000000 0x0000000000000000
0x0000000000000000 0x0000000000000000 RW 8
GNU_RELRO 0x0000000000002df8 0x0000000000602df8 0x0000000000602df8
0x0000000000000208 0x0000000000000208 R 1
Section to Segment mapping:
Segment Sections...
00
01 .interp
02 .interp .note.ABI-tag .note.gnu.build-id .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt .init .plt .text .fini .rodata .eh_frame_hdr .eh_frame
03 .init_array .fini_array .jcr .dynamic .got .got.plt .data .bss
04 .dynamic
05 .note.ABI-tag .note.gnu.build-id
06 .eh_frame_hdr
07
08 .init_array .fini_array .jcr .dynamic .got
Dynamic section at offset 0x2e10 contains 24 entries:
Tag Type Name/Value
0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
0x000000000000000c (INIT) 0x400ac0
0x000000000000000d (FINI) 0x4022a4
0x0000000000000019 (INIT_ARRAY) 0x602df8
0x000000000000001b (INIT_ARRAYSZ) 8 (bytes)
0x000000000000001a (FINI_ARRAY) 0x602e00
0x000000000000001c (FINI_ARRAYSZ) 8 (bytes)
0x000000006ffffef5 (GNU_HASH) 0x400298
0x0000000000000005 (STRTAB) 0x4005c8
0x0000000000000006 (SYMTAB) 0x4002c8
0x000000000000000a (STRSZ) 365 (bytes)
0x000000000000000b (SYMENT) 24 (bytes)
0x0000000000000015 (DEBUG) 0x0
0x0000000000000003 (PLTGOT) 0x602fe8
0x0000000000000002 (PLTRELSZ) 648 (bytes)
0x0000000000000014 (PLTREL) RELA
0x0000000000000017 (JMPREL) 0x400838
0x0000000000000007 (RELA) 0x4007d8
0x0000000000000008 (RELASZ) 96 (bytes)
0x0000000000000009 (RELAENT) 24 (bytes)
0x000000006ffffffe (VERNEED) 0x400778
0x000000006fffffff (VERNEEDNUM) 1
0x000000006ffffff0 (VERSYM) 0x400736
0x0000000000000000 (NULL) 0x0
Relocation section ‘.rela.dyn‘ at offset 0x7d8 contains 4 entries:
Offset Info Type Sym. Value Sym. Name + Addend
000000602fe0 001000000006 R_X86_64_GLOB_DAT 0000000000000000 __gmon_start__ + 0
000000603740 001d00000005 R_X86_64_COPY 0000000000603740 stdout + 0
000000603748 001e00000005 R_X86_64_COPY 0000000000603748 stdin + 0
000000603750 001f00000005 R_X86_64_COPY 0000000000603750 stderr + 0
Relocation section ‘.rela.plt‘ at offset 0x838 contains 27 entries:
Offset Info Type Sym. Value Sym. Name + Addend
000000603000 000100000007 R_X86_64_JUMP_SLO 0000000000000000 getenv + 0
000000603008 000200000007 R_X86_64_JUMP_SLO 0000000000000000 __errno_location + 0
000000603010 000300000007 R_X86_64_JUMP_SLO 0000000000000000 strcpy + 0
000000603018 000400000007 R_X86_64_JUMP_SLO 0000000000000000 puts + 0
000000603020 000500000007 R_X86_64_JUMP_SLO 0000000000000000 write + 0
000000603028 000600000007 R_X86_64_JUMP_SLO 0000000000000000 __stack_chk_fail + 0
000000603030 000700000007 R_X86_64_JUMP_SLO 0000000000000000 alarm + 0
000000603038 000800000007 R_X86_64_JUMP_SLO 0000000000000000 close + 0
000000603040 000900000007 R_X86_64_JUMP_SLO 0000000000000000 read + 0
000000603048 000a00000007 R_X86_64_JUMP_SLO 0000000000000000 __libc_start_main + 0
000000603050 000b00000007 R_X86_64_JUMP_SLO 0000000000000000 fgets + 0
000000603058 000c00000007 R_X86_64_JUMP_SLO 0000000000000000 signal + 0
000000603060 000d00000007 R_X86_64_JUMP_SLO 0000000000000000 gethostbyname + 0
000000603068 000e00000007 R_X86_64_JUMP_SLO 0000000000000000 __memmove_chk + 0
000000603070 000f00000007 R_X86_64_JUMP_SLO 0000000000000000 __memcpy_chk + 0
000000603078 001100000007 R_X86_64_JUMP_SLO 0000000000000000 strtol + 0
000000603080 001200000007 R_X86_64_JUMP_SLO 0000000000000000 fflush + 0
000000603088 001300000007 R_X86_64_JUMP_SLO 0000000000000000 __isoc99_sscanf + 0
000000603090 001400000007 R_X86_64_JUMP_SLO 0000000000000000 __printf_chk + 0
000000603098 001500000007 R_X86_64_JUMP_SLO 0000000000000000 fopen + 0
0000006030a0 001600000007 R_X86_64_JUMP_SLO 0000000000000000 exit + 0
0000006030a8 001700000007 R_X86_64_JUMP_SLO 0000000000000000 connect + 0
0000006030b0 001800000007 R_X86_64_JUMP_SLO 0000000000000000 __fprintf_chk + 0
0000006030b8 001900000007 R_X86_64_JUMP_SLO 0000000000000000 sleep + 0
0000006030c0 001a00000007 R_X86_64_JUMP_SLO 0000000000000000 __ctype_b_loc + 0
0000006030c8 001b00000007 R_X86_64_JUMP_SLO 0000000000000000 __sprintf_chk + 0
0000006030d0 001c00000007 R_X86_64_JUMP_SLO 0000000000000000 socket + 0
The decoding of unwind sections for machine type Advanced Micro Devices X86-64 is not currently supported.
Symbol table ‘.dynsym‘ contains 32 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000000000 0 FUNC GLOBAL DEFAULT UND getenv@GLIBC_2.2.5 (2)
2: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __errno_location@GLIBC_2.2.5 (2)
3: 0000000000000000 0 FUNC GLOBAL DEFAULT UND strcpy@GLIBC_2.2.5 (2)
4: 0000000000000000 0 FUNC GLOBAL DEFAULT UND puts@GLIBC_2.2.5 (2)
5: 0000000000000000 0 FUNC GLOBAL DEFAULT UND write@GLIBC_2.2.5 (2)
6: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __stack_chk_fail@GLIBC_2.4 (3)
7: 0000000000000000 0 FUNC GLOBAL DEFAULT UND alarm@GLIBC_2.2.5 (2)
8: 0000000000000000 0 FUNC GLOBAL DEFAULT UND close@GLIBC_2.2.5 (2)
9: 0000000000000000 0 FUNC GLOBAL DEFAULT UND read@GLIBC_2.2.5 (2)
10: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __libc_start_main@GLIBC_2.2.5 (2)
11: 0000000000000000 0 FUNC GLOBAL DEFAULT UND fgets@GLIBC_2.2.5 (2)
12: 0000000000000000 0 FUNC GLOBAL DEFAULT UND signal@GLIBC_2.2.5 (2)
13: 0000000000000000 0 FUNC GLOBAL DEFAULT UND gethostbyname@GLIBC_2.2.5 (2)
14: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __memmove_chk@GLIBC_2.3.4 (4)
15: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __memcpy_chk@GLIBC_2.3.4 (4)
16: 0000000000000000 0 NOTYPE WEAK DEFAULT UND __gmon_start__
17: 0000000000000000 0 FUNC GLOBAL DEFAULT UND strtol@GLIBC_2.2.5 (2)
18: 0000000000000000 0 FUNC GLOBAL DEFAULT UND fflush@GLIBC_2.2.5 (2)
19: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __isoc99_sscanf@GLIBC_2.7 (5)
20: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __printf_chk@GLIBC_2.3.4 (4)
21: 0000000000000000 0 FUNC GLOBAL DEFAULT UND fopen@GLIBC_2.2.5 (2)
22: 0000000000000000 0 FUNC GLOBAL DEFAULT UND exit@GLIBC_2.2.5 (2)
23: 0000000000000000 0 FUNC GLOBAL DEFAULT UND connect@GLIBC_2.2.5 (2)
24: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __fprintf_chk@GLIBC_2.3.4 (4)
25: 0000000000000000 0 FUNC GLOBAL DEFAULT UND sleep@GLIBC_2.2.5 (2)
26: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __ctype_b_loc@GLIBC_2.3 (6)
27: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __sprintf_chk@GLIBC_2.3.4 (4)
28: 0000000000000000 0 FUNC GLOBAL DEFAULT UND socket@GLIBC_2.2.5 (2)
29: 0000000000603740 8 OBJECT GLOBAL DEFAULT 25 stdout@GLIBC_2.2.5 (2)
30: 0000000000603748 8 OBJECT GLOBAL DEFAULT 25 stdin@GLIBC_2.2.5 (2)
31: 0000000000603750 8 OBJECT GLOBAL DEFAULT 25 stderr@GLIBC_2.2.5 (2)
Symbol table ‘.symtab‘ contains 157 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000400238 0 SECTION LOCAL DEFAULT 1
2: 0000000000400254 0 SECTION LOCAL DEFAULT 2
3: 0000000000400274 0 SECTION LOCAL DEFAULT 3
4: 0000000000400298 0 SECTION LOCAL DEFAULT 4
5: 00000000004002c8 0 SECTION LOCAL DEFAULT 5
6: 00000000004005c8 0 SECTION LOCAL DEFAULT 6
7: 0000000000400736 0 SECTION LOCAL DEFAULT 7
8: 0000000000400778 0 SECTION LOCAL DEFAULT 8
9: 00000000004007d8 0 SECTION LOCAL DEFAULT 9
10: 0000000000400838 0 SECTION LOCAL DEFAULT 10
11: 0000000000400ac0 0 SECTION LOCAL DEFAULT 11
12: 0000000000400ad0 0 SECTION LOCAL DEFAULT 12
13: 0000000000400c90 0 SECTION LOCAL DEFAULT 13
14: 00000000004022a4 0 SECTION LOCAL DEFAULT 14
15: 00000000004022b0 0 SECTION LOCAL DEFAULT 15
16: 0000000000402798 0 SECTION LOCAL DEFAULT 16
17: 00000000004028a0 0 SECTION LOCAL DEFAULT 17
18: 0000000000602df8 0 SECTION LOCAL DEFAULT 18
19: 0000000000602e00 0 SECTION LOCAL DEFAULT 19
20: 0000000000602e08 0 SECTION LOCAL DEFAULT 20
21: 0000000000602e10 0 SECTION LOCAL DEFAULT 21
22: 0000000000602fe0 0 SECTION LOCAL DEFAULT 22
23: 0000000000602fe8 0 SECTION LOCAL DEFAULT 23
24: 00000000006030e0 0 SECTION LOCAL DEFAULT 24
25: 0000000000603740 0 SECTION LOCAL DEFAULT 25
26: 0000000000000000 0 SECTION LOCAL DEFAULT 26
27: 0000000000000000 0 SECTION LOCAL DEFAULT 27
28: 0000000000000000 0 SECTION LOCAL DEFAULT 28
29: 0000000000000000 0 SECTION LOCAL DEFAULT 29
30: 0000000000000000 0 SECTION LOCAL DEFAULT 30
31: 0000000000000000 0 SECTION LOCAL DEFAULT 31
32: 0000000000000000 0 SECTION LOCAL DEFAULT 32
33: 0000000000400cbc 0 FUNC LOCAL DEFAULT 13 call_gmon_start
34: 0000000000000000 0 FILE LOCAL DEFAULT ABS crtstuff.c
35: 0000000000602e08 0 OBJECT LOCAL DEFAULT 20 __JCR_LIST__
36: 0000000000400ce0 0 FUNC LOCAL DEFAULT 13 deregister_tm_clones
37: 0000000000400d10 0 FUNC LOCAL DEFAULT 13 register_tm_clones
38: 0000000000400d50 0 FUNC LOCAL DEFAULT 13 __do_global_dtors_aux
39: 0000000000603758 1 OBJECT LOCAL DEFAULT 25 completed.6976
40: 0000000000602e00 0 OBJECT LOCAL DEFAULT 19 __do_global_dtors_aux_fin
41: 0000000000400d70 0 FUNC LOCAL DEFAULT 13 frame_dummy
42: 0000000000602df8 0 OBJECT LOCAL DEFAULT 18 __frame_dummy_init_array_
43: 0000000000000000 0 FILE LOCAL DEFAULT ABS bomb.c
44: 0000000000000000 0 FILE LOCAL DEFAULT ABS phases.c
45: 00000000004024b0 16 OBJECT LOCAL DEFAULT 15 array.3449
46: 0000000000000000 0 FILE LOCAL DEFAULT ABS support.c
47: 00000000004012a0 86 FUNC LOCAL DEFAULT 13 sig_handler
48: 0000000000000000 0 FILE LOCAL DEFAULT ABS driverlib.c
49: 000000000040168e 286 FUNC LOCAL DEFAULT 13 rio_readlineb
50: 0000000000000000 0 FILE LOCAL DEFAULT ABS crtstuff.c
51: 0000000000402cf0 0 OBJECT LOCAL DEFAULT 17 __FRAME_END__
52: 0000000000602e08 0 OBJECT LOCAL DEFAULT 20 __JCR_END__
53: 0000000000602e00 0 NOTYPE LOCAL DEFAULT 18 __init_array_end
54: 0000000000602e10 0 OBJECT LOCAL DEFAULT 21 _DYNAMIC
55: 0000000000602df8 0 NOTYPE LOCAL DEFAULT 18 __init_array_start
56: 0000000000602fe8 0 OBJECT LOCAL DEFAULT 23 _GLOBAL_OFFSET_TABLE_
57: 00000000004022a0 2 FUNC GLOBAL DEFAULT 13 __libc_csu_fini
58: 00000000004013f9 65 FUNC GLOBAL DEFAULT 13 skip
59: 0000000000000000 0 FUNC GLOBAL DEFAULT UND getenv@@GLIBC_2.2.5
60: 00000000004015c4 149 FUNC GLOBAL DEFAULT 13 phase_defused
61: 0000000000603190 24 OBJECT GLOBAL DEFAULT 24 n31
62: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __errno_location@@GLIBC_2
63: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _ITM_deregisterTMCloneTab
64: 0000000000603740 8 OBJECT GLOBAL DEFAULT 25 stdout@@GLIBC_2.2.5
65: 00000000006030e0 0 NOTYPE WEAK DEFAULT 24 data_start
66: 0000000000603780 1600 OBJECT GLOBAL DEFAULT 25 input_strings
67: 0000000000000000 0 FUNC GLOBAL DEFAULT UND strcpy@@GLIBC_2.2.5
68: 0000000000603170 24 OBJECT GLOBAL DEFAULT 24 n33
69: 0000000000000000 0 FUNC GLOBAL DEFAULT UND puts@@GLIBC_2.2.5
70: 0000000000603748 8 OBJECT GLOBAL DEFAULT 25 stdin@@GLIBC_2.2.5
71: 0000000000000000 0 FUNC GLOBAL DEFAULT UND write@@GLIBC_2.2.5
72: 0000000000603740 0 NOTYPE GLOBAL DEFAULT ABS _edata
73: 0000000000603230 24 OBJECT GLOBAL DEFAULT 24 n44
74: 0000000000603290 24 OBJECT GLOBAL DEFAULT 24 n46
75: 0000000000603250 24 OBJECT GLOBAL DEFAULT 24 n42
76: 00000000006032b0 24 OBJECT GLOBAL DEFAULT 24 n48
77: 00000000004022a4 0 FUNC GLOBAL DEFAULT 14 _fini
78: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __stack_chk_fail@@GLIBC_2
79: 0000000000603760 4 OBJECT GLOBAL DEFAULT 25 num_input_strings
80: 0000000000401062 146 FUNC GLOBAL DEFAULT 13 phase_5
81: 00000000004013ba 2 FUNC GLOBAL DEFAULT 13 initialize_bomb_solve
82: 00000000004013bc 61 FUNC GLOBAL DEFAULT 13 blank_line
83: 00000000004017ac 2021 FUNC GLOBAL DEFAULT 13 submitr
84: 0000000000400f43 139 FUNC GLOBAL DEFAULT 13 phase_3
85: 0000000000400ee0 28 FUNC GLOBAL DEFAULT 13 phase_1
86: 00000000004012f6 37 FUNC GLOBAL DEFAULT 13 invalid_phase
87: 0000000000401fb8 469 FUNC GLOBAL DEFAULT 13 init_driver
88: 0000000000000000 0 FUNC GLOBAL DEFAULT UND alarm@@GLIBC_2.2.5
89: 0000000000000000 0 FUNC GLOBAL DEFAULT UND close@@GLIBC_2.2.5
90: 00000000006032f0 16 OBJECT GLOBAL DEFAULT 24 node3
91: 0000000000000000 0 FUNC GLOBAL DEFAULT UND read@@GLIBC_2.2.5
92: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __libc_start_main@@GLIBC_
93: 0000000000000000 0 FUNC GLOBAL DEFAULT UND fgets@@GLIBC_2.2.5
94: 000000000040143a 34 FUNC GLOBAL DEFAULT 13 explode_bomb
95: 00000000006032d0 16 OBJECT GLOBAL DEFAULT 24 node1
96: 00000000006030e0 0 NOTYPE GLOBAL DEFAULT 24 __data_start
97: 0000000000000000 0 FUNC GLOBAL DEFAULT UND signal@@GLIBC_2.2.5
98: 0000000000000000 0 FUNC GLOBAL DEFAULT UND gethostbyname@@GLIBC_2.2.
99: 0000000000603310 16 OBJECT GLOBAL DEFAULT 24 node5
100: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __memmove_chk@@GLIBC_2.3.
101: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __memcpy_chk@@GLIBC_2.3.4
102: 0000000000000000 0 NOTYPE WEAK DEFAULT UND __gmon_start__
103: 0000000000000000 0 FUNC GLOBAL DEFAULT UND strtol@@GLIBC_2.2.5
104: 0000000000401204 62 FUNC GLOBAL DEFAULT 13 fun7
105: 00000000006030e8 0 OBJECT GLOBAL HIDDEN 24 __dso_handle
106: 00000000004022b0 4 OBJECT GLOBAL DEFAULT 15 _IO_stdin_used
107: 0000000000603130 24 OBJECT GLOBAL DEFAULT 24 n22
108: 0000000000603340 1024 OBJECT GLOBAL DEFAULT 24 host_table
109: 0000000000400fce 62 FUNC GLOBAL DEFAULT 13 func4
110: 00000000006030f0 24 OBJECT GLOBAL DEFAULT 24 n1
111: 000000000040131b 29 FUNC GLOBAL DEFAULT 13 string_length
112: 0000000000402210 137 FUNC GLOBAL DEFAULT 13 __libc_csu_init
113: 0000000000000000 0 FUNC GLOBAL DEFAULT UND fflush@@GLIBC_2.2.5
114: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __isoc99_sscanf@@GLIBC_2.
115: 00000000006031b0 24 OBJECT GLOBAL DEFAULT 24 n34
116: 0000000000603150 24 OBJECT GLOBAL DEFAULT 24 n32
117: 0000000000603e10 0 NOTYPE GLOBAL DEFAULT ABS _end
118: 0000000000400c90 0 FUNC GLOBAL DEFAULT 13 _start
119: 0000000000401242 81 FUNC GLOBAL DEFAULT 13 secret_phase
120: 0000000000603768 8 OBJECT GLOBAL DEFAULT 25 infile
121: 0000000000401660 46 FUNC GLOBAL DEFAULT 13 sigalrm_handler
122: 0000000000401f91 39 FUNC GLOBAL DEFAULT 13 init_timeout
123: 0000000000603740 0 NOTYPE GLOBAL DEFAULT ABS __bss_start
124: 0000000000400da0 311 FUNC GLOBAL DEFAULT 13 main
125: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __printf_chk@@GLIBC_2.3.4
126: 0000000000603210 24 OBJECT GLOBAL DEFAULT 24 n47
127: 0000000000603270 24 OBJECT GLOBAL DEFAULT 24 n43
128: 00000000006031f0 24 OBJECT GLOBAL DEFAULT 24 n41
129: 000000000040149e 294 FUNC GLOBAL DEFAULT 13 read_line
130: 00000000006031d0 24 OBJECT GLOBAL DEFAULT 24 n45
131: 0000000000401338 106 FUNC GLOBAL DEFAULT 13 strings_not_equal
132: 000000000040100c 86 FUNC GLOBAL DEFAULT 13 phase_4
133: 0000000000000000 0 FUNC GLOBAL DEFAULT UND fopen@@GLIBC_2.2.5
134: 00000000004010f4 272 FUNC GLOBAL DEFAULT 13 phase_6
135: 0000000000603dc0 80 OBJECT GLOBAL DEFAULT 25 scratch
136: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _Jv_RegisterClasses
137: 000000000040218d 119 FUNC GLOBAL DEFAULT 13 driver_post
138: 0000000000400efc 71 FUNC GLOBAL DEFAULT 13 phase_2
139: 0000000000000000 0 FUNC GLOBAL DEFAULT UND exit@@GLIBC_2.2.5
140: 000000000060375c 4 OBJECT GLOBAL DEFAULT 25 bomb_id
141: 0000000000000000 0 FUNC GLOBAL DEFAULT UND connect@@GLIBC_2.2.5
142: 0000000000603740 0 OBJECT GLOBAL HIDDEN 24 __TMC_END__
143: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __fprintf_chk@@GLIBC_2.3.
144: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _ITM_registerTMCloneTable
145: 00000000006032e0 16 OBJECT GLOBAL DEFAULT 24 node2
146: 0000000000603300 16 OBJECT GLOBAL DEFAULT 24 node4
147: 0000000000000000 0 FUNC GLOBAL DEFAULT UND sleep@@GLIBC_2.2.5
148: 0000000000603320 16 OBJECT GLOBAL DEFAULT 24 node6
149: 0000000000400ac0 0 FUNC GLOBAL DEFAULT 11 _init
150: 000000000040145c 66 FUNC GLOBAL DEFAULT 13 read_six_numbers
151: 0000000000603110 24 OBJECT GLOBAL DEFAULT 24 n21
152: 00000000004013a2 24 FUNC GLOBAL DEFAULT 13 initialize_bomb
153: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __ctype_b_loc@@GLIBC_2.3
154: 0000000000603750 8 OBJECT GLOBAL DEFAULT 25 stderr@@GLIBC_2.2.5
155: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __sprintf_chk@@GLIBC_2.3.
156: 0000000000000000 0 FUNC GLOBAL DEFAULT UND socket@@GLIBC_2.2.5
Histogram for `.gnu.hash‘ bucket list length (total of 3 buckets):
Length Number % of total Coverage
0 1 ( 33.3%)
1 1 ( 33.3%) 33.3%
2 1 ( 33.3%) 100.0%
Version symbols section ‘.gnu.version‘ contains 32 entries:
Addr: 0000000000400736 Offset: 0x000736 Link: 5 (.dynsym)
000: 0 (*local*) 2 (GLIBC_2.2.5) 2 (GLIBC_2.2.5) 2 (GLIBC_2.2.5)
004: 2 (GLIBC_2.2.5) 2 (GLIBC_2.2.5) 3 (GLIBC_2.4) 2 (GLIBC_2.2.5)
008: 2 (GLIBC_2.2.5) 2 (GLIBC_2.2.5) 2 (GLIBC_2.2.5) 2 (GLIBC_2.2.5)
00c: 2 (GLIBC_2.2.5) 2 (GLIBC_2.2.5) 4 (GLIBC_2.3.4) 4 (GLIBC_2.3.4)
010: 0 (*local*) 2 (GLIBC_2.2.5) 2 (GLIBC_2.2.5) 5 (GLIBC_2.7)
014: 4 (GLIBC_2.3.4) 2 (GLIBC_2.2.5) 2 (GLIBC_2.2.5) 2 (GLIBC_2.2.5)
018: 4 (GLIBC_2.3.4) 2 (GLIBC_2.2.5) 6 (GLIBC_2.3) 4 (GLIBC_2.3.4)
01c: 2 (GLIBC_2.2.5) 2 (GLIBC_2.2.5) 2 (GLIBC_2.2.5) 2 (GLIBC_2.2.5)
Version needs section ‘.gnu.version_r‘ contains 1 entries:
Addr: 0x0000000000400778 Offset: 0x000778 Link: 6 (.dynstr)
000000: Version: 1 File: libc.so.6 Cnt: 5
0x0010: Name: GLIBC_2.3 Flags: none Version: 6
0x0020: Name: GLIBC_2.7 Flags: none Version: 5
0x0030: Name: GLIBC_2.3.4 Flags: none Version: 4
0x0040: Name: GLIBC_2.4 Flags: none Version: 3
0x0050: Name: GLIBC_2.2.5 Flags: none Version: 2
Displaying notes found at file offset 0x00000254 with length 0x00000020:
Owner Data size Description
GNU 0x00000010 NT_GNU_ABI_TAG (ABI version tag)
OS: Linux, ABI: 2.6.24
Displaying notes found at file offset 0x00000274 with length 0x00000024:
Owner Data size Description
GNU 0x00000014 NT_GNU_BUILD_ID (unique build ID bitstring)
Build ID: 11c83ac9c51d3036cf2669235060f17e2cd0400b
.text section 里装载了可执行代码;
.data section 里面装载了被初始化的数据;
.bss section 里面装载了未被初始化的数据;
.以 .rel 打头的 sections 里面装载了重定位条目;
.symtab 或者 .dynsym section 里面装载了符号信息;
.strtab 或者 .dynstr section 里面装载了字符串信息;
注意bss中装载了未初始化的数据,在目标文件中这个变量仅仅是一个占位符,不占用实际的磁盘空间。
7、使用过哪些进程间通讯机制,并详细说明 主要介绍一下Linux下面的几种进程通讯方式。
- 管道:管道的名字挺形象的,就一个一个先进先出的队列,一个进程从一端读,另一个进程从另一端写,是一个环形缓冲区。管道有字节缓冲区,因此有大小限制。同时,管道分为命名管道(FIFO)和匿名管道。只有父子之间的经常才可以共享匿名管道,就是受fork限制,而命名管道可以在无亲缘关系的进程间使用,使用mififo函数创建,指定pathname作为路径名。
#include<sys/types.h>
#include<sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);
PS:创建后打开管道,必须读或者写,不能既读又写,属于半双工。
2. 消息队列:消息队列就像一个信箱,有人投递有人取。消息队列具有内核持续性,一个进程往某个队列写入一些消息,终止后,另一个进程可以读取。因此说是一个链表更为合适。注意发送者可以设置优先级,优先级最高的最早消息总是位于队列的头部。
3. 共享内存:共享内存是UNIX提供的进程通讯手段中最快的。前面已经介绍过了。注意一下需要自己提供同步的手段。
4.信号:信号和信号量看起来很像。信号是指signal,用于向一个进程通知发生异步事件的机制,而信号量是一种同步手段,就是PV原语那些东西。信号的传递是通过修改信号所发到的进程的某一个位域完成的。只有一位,无法排队。进程可以选择执行默认行为(如终止),执行一个信号处理函数或者忽略该信号。
简单看一下unix常用的信号:
注意前面32个是传统的unix信号,无法排队,因此可能造成信号的丢失。而后面32是可靠信息,可靠的意思是信息可以排队,信号不丢失。
lijun0914:~/workspace/bomb $ kill -l
1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP
6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1
11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM
16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ
26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR
31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3
38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8
43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7
58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2
63) SIGRTMAX-1 64) SIGRTMAX
5.套接字:socket,上面介绍的通讯手段限制了作用域,套接字编程应用则更为广泛,可用于不同机器之间的通讯。网络的两端都建立一个socket对象,然后通过socket对象进行数据的传输。《unix网络编程卷一》对socket编程有详细的介绍。
8、makefile编写,虽然比较基础,但是会被问到
makefile是unix下,为工程中各个目录下文件制定编译规则的工具。
陈皓的专栏讲解的非常好。
http://blog.csdn.net/haoel/article/details/2886/
9、gdb调试相关的经验,会被问到
相信在Linux下调试C或者C++程序的基本都有gdb的调试经验。
比较基础的命令或者用法就是设置断点,单步运行,查看变量,查看调用栈。
拿一个简单的例子看一下:
#include <iostream>
#include <algorithm>
using namespace std;
void print(string s,int index){
if(index==s.size()){
cout<<s<<endl;
return;
}else{
for(int i=0;i<10;++i){
s[index]=i+‘0‘;
print(s,index+1);
}
}
}
int main(void){
string s="000";
print(s,0);
}
lijun0914:~/workspace $ gdb ./a.out
//设置断点,可使用行断点,函数断点,事件断点,条件断点
(gdb) break 10
Breakpoint 1 at 0x400c12: file 12from1Ton.cc, line 10.
//开始运行
(gdb) run
Starting program: /home/ubuntu/workspace/a.out
Breakpoint 1, print (s="000", index=0) at 12from1Ton.cc:10
10 s[index]=i+‘0‘;
//显示附近代码
(gdb) list
5 if(index==s.size()){
6 cout<<s<<endl;
7 return;
8 }else{
9 for(int i=0;i<10;++i){
10 s[index]=i+‘0‘;
11 print(s,index+1);
12 }
13 }
14 }
//显示变量
(gdb) print s
$1 = "000"
//查看调用栈,即函数调用顺序
(gdb) bt
#0 print (s="000", index=0) at 12from1Ton.cc:10
#1 0x0000000000400ce9 in main () at 12from1Ton.cc:17
//继续从断点处执行
(gdb) continue
Continuing.
Breakpoint 1, print (s="000", index=1) at 12from1Ton.cc:10
10 s[index]=i+‘0‘;
//单步调试,以完整的语句为单位往下执行
(gdb) next
11 print(s,index+1);
(gdb) next
Breakpoint 1, print (s="000", index=2) at 12from1Ton.cc:10
10 s[index]=i+‘0‘;
//查看函数信息,注意这里会显示所有匹配到的函数,C++每一个函数都有自己的函数签名
//在使用模板的时候,如果在模板函数里打断点,则只有一个实例生效,如果想要都打上断点
//可以使用info functions命令,+break void print(std::string, int)即具体函数
(gdb) info functions print
All functions matching regular expression "print":
File ../stdio-common/printf_fphex.c:
int __printf_fphex(_IO_FILE *, const struct printf_info *, const void * const *);
File 12from1Ton.cc:
void print(std::string, int);
static void _GLOBAL__sub_I__Z5printSsi();
File argp-fmtstream.c:
ssize_t __argp_fmtstream_printf(struct argp_fmtstream *, const char *, ...);
.....
(gdb) continue
Continuing.
000
Breakpoint 1, print (s="000", index=2) at 12from1Ton.cc:10
10 s[index]=i+‘0‘;
//使用step命令可以进入隐藏的函数调用中,也可以拿来进入C++的class
//例如使用stl的时候,它会一层层的追溯下去
(gdb) step
11 print(s,index+1);
(gdb) step
print (s="001", index=3) at 12from1Ton.cc:5
5 if(index==s.size()){
//使用finish可以跳出调用栈的不停深入
//这里的例子并不好,可以自己用stl尝试一下,印象更深
(gdb) finish
Run till exit from #0 print (s="001", index=3) at 12from1Ton.cc:7
0x0000000000400c56 in print (s="001", index=2) at 12from1Ton.cc:11
11 print(s,index+1);
//另一个跳出调用的方法是使用临时断点
//注意下面的执行情况,临时断点只生效了一次就会被自动删除
(gdb) tbreak 6
Temporary breakpoint 2 at 0x400be9: file 12from1Ton.cc, line 6.
(gdb) continue
Continuing.
Breakpoint 1, print (s="001", index=2) at 12from1Ton.cc:10
10 s[index]=i+‘0‘;
(gdb) continue
Continuing.
Temporary breakpoint 2, print (s="002", index=3) at 12from1Ton.cc:6
6 cout<<s<<endl;
(gdb) continue
Continuing.
002
Breakpoint 1, print (s="002", index=2) at 12from1Ton.cc:10
10 s[index]=i+‘0‘;
(gdb) continue
Continuing.
003
Breakpoint 1, print (s="003", index=2) at 12from1Ton.cc:10
10 s[index]=i+‘0‘;
(gdb) step
11 print(s,index+1);
(gdb) step
print (s="005", index=3) at 12from1Ton.cc:5
5 if(index==s.size()){
//使用up也可以往高层走
(gdb) up
#1 0x0000000000400c56 in print (s="005", index=2) at 12from1Ton.cc:11
11 print(s,index+1);
//使用断点命令可以在断点处指定一个命令序列,每次到达都执行此序列
//通常可以与condition一起使用
(gdb) command 1
Type commands for breakpoint(s) 1, one per line.
End with a line saying just "end".
>printf "the current value of index is %d\n",index
>continue
>end
(gdb) continue
Continuing.
005
Breakpoint 1, print (s="005", index=2) at 12from1Ton.cc:10
10 s[index]=i+‘0‘;
the current value of index is 2
006
Breakpoint 1, print (s="006", index=2) at 12from1Ton.cc:10
10 s[index]=i+‘0‘;
the current value of index is 2
007
//condition可以跳过其他断点,只在达到我们设定的条件时停下
(gdb) condition 1 s[0]==‘2‘
(gdb) run
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/ubuntu/workspace/a.out
......
194
195
196
197
198
199
Breakpoint 1, print (s="200", index=1) at 12from1Ton.cc:10
10 s[index]=i+‘0‘;
the current value of index is 1
Breakpoint 1, print (s="200", index=2) at 12from1Ton.cc:10
10 s[index]=i+‘0‘;
the current value of index is 2
200
//取消condition
(gdb) condition 1
Breakpoint 1 now unconditional.
(gdb) break 16
Note: breakpoint 4 also set at pc 0x400c98.
Breakpoint 5 at 0x400c98: file 12from1Ton.cc, line 16.
//清除断点
(gdb) delete 1
(gdb) delete 2-3
No breakpoint number 2.
(gdb) delete 3-4
No breakpoint number 3.
(gdb) info breakpoints
Num Type Disp Enb Address What
5 breakpoint keep y 0x0000000000400c98 in main() at 12from1Ton.cc:16
(gdb) delete
Delete all breakpoints? (y or n) y
(gdb) quit
lijun0914:~/workspace $ gdb a.out
(gdb) break 16
Breakpoint 1 at 0x400c98: file 12from1Ton.cc, line 16.
(gdb) run
Starting program: /home/ubuntu/workspace/a.out
Breakpoint 1, main () at 12from1Ton.cc:16
16 string s="000";
//使用watch可以设置观察点,当变量或者表达式变化的时候运行停止
(gdb) watch s
Hardware watchpoint 2: s
(gdb) continue
Continuing.
Hardware watchpoint 2: s
Old value = http://www.mamicode.com/<error reading variable: Cannot access memory at address 0x64>
New value = http://www.mamicode.com/"000"
0x00007ffff7b8ebda in std::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(char const*, std::allocator<char> const&) () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
(gdb) break 10
Breakpoint 3 at 0x400c12: file 12from1Ton.cc, line 10.
(gdb) continue
Continuing.
Breakpoint 3, print (s="000", index=0) at 12from1Ton.cc:10
10 s[index]=i+‘0‘;
(gdb) print i
$1 = 0
//在运行中修改变量
(gdb) set var i=3
(gdb) continue
Continuing.
Breakpoint 3, print (s="300", index=1) at 12from1Ton.cc:10
10 s[index]=i+‘0‘;
(gdb) break 5
Breakpoint 4 at 0x400bcd: file 12from1Ton.cc, line 5.
(gdb) continue
Continuing.
Breakpoint 4, print (s="300", index=2) at 12from1Ton.cc:5
5 if(index==s.size()){
//从当前帧跳到任意一行
(gdb) jump 9
Continuing at 0x400c09.
Breakpoint 3, print (s="300", index=2) at 12from1Ton.cc:10
10 s[index]=i+‘0‘;
//反编译,在查找崩溃问题时很有用
(gdb) disassemble print
Dump of assembler code for function print(std::string, int):
0x0000000000400bbd <+0>: push %rbp
0x0000000000400bbe <+1>: mov %rsp,%rbp
0x0000000000400bc1 <+4>: push %rbx
0x0000000000400bc2 <+5>: sub $0x38,%rsp
0x0000000000400bc6 <+9>: mov %rdi,-0x38(%rbp)
0x0000000000400bca <+13>: mov %esi,-0x3c(%rbp)
0x0000000000400bcd <+16>: mov -0x3c(%rbp),%eax
0x0000000000400bd0 <+19>: movslq %eax,%rbx
0x0000000000400bd3 <+22>: mov -0x38(%rbp),%rax
0x0000000000400bd7 <+26>: mov %rax,%rdi
(gdb) set step 1
(gdb) step
0x00007ffff7b8d3e0 in std::string::operator[](unsigned long) () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
(gdb) disassemble $pc
Dump of assembler code for function _ZNSsixEm:
0x00007ffff7b8d3e0 <+0>: push %rbp
0x00007ffff7b8d3e1 <+1>: push %rbx
0x00007ffff7b8d3e2 <+2>: mov %rsi,%rbx
=> 0x00007ffff7b8d3e5 <+5>: sub $0x8,%rsp
0x00007ffff7b8d3e9 <+9>: mov (%rdi),%rax
0x00007ffff7b8d3ec <+12>: mov -0x8(%rax),%edx
0x00007ffff7b8d3ef <+15>: test %edx,%edx
0x00007ffff7b8d3f1 <+17>: js 0x7ffff7b8d3ff <_ZNSsixEm+31>
0x00007ffff7b8d3f3 <+19>: mov %rdi,%rbp
0x00007ffff7b8d3f6 <+22>: callq 0x7ffff7b2ecd0 <_ZNSs12_M_leak_hardEv@plt>
---Type <return> to continue, or q <return> to quit---
0x00007ffff7b8d3fb <+27>: mov 0x0(%rbp),%rax
0x00007ffff7b8d3ff <+31>: add $0x8,%rsp
0x00007ffff7b8d403 <+35>: add %rbx,%rax
0x00007ffff7b8d406 <+38>: pop %rbx
0x00007ffff7b8d407 <+39>: pop %rbp
0x00007ffff7b8d408 <+40>: retq
End of assembler dump.
(gdb) nexti
0x00007ffff7b8d3e9 in std::string::operator[](unsigned long) () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
这里没有使用call和return,因为这个例子确实不合适,简单来说call是调用函数,return是修改函数返回值。当然还有其他命令没有详细介绍,例如frame,info,where,不熟悉的google之。
需要提一下的是程序出现分段错误,操作系统会生成一个coredump文件,使用gdb coredump core可以查看崩溃堆栈的信息。当然有时候会出现栈越界无法回溯,这里给一个解决方法,通过一个地址根据链表结构回溯,具体见:https://zhuanlan.zhihu.com/p/20642841?refer=jilinxiaohuo
10、如何定位内存泄露?
常见的内存问题有内存泄漏;内存的错误使用,例如无效的读写;缓冲区溢出等等。如果手头有工具的话,我会优先选用可以做内存检测的工具,例如purify和valgrind,这些是专用的内存调试器,通过插装代码等手段跟踪内存。
给给简单的例子(取自《软件调试实战》):
#include <stdio.h>
#include <stdlib.h>
int main(int argc,char *argv[]){
const int size = 100;
int n,sum=0;
int* A = (int*)malloc(sizeof(int)*size);
for(n=size; n>0;n--)
A[n] = n;
for(n=0;n<size;n++)
sum+=A[n];
printf("sum=%d\n",sum);
return 0;
}
lijun0914:~/workspace $ gcc -g testMem.cc
lijun0914:~/workspace $ valgrind --tool=memcheck --leak-check=yes ./a.out
==12784== Memcheck, a memory error detector
==12784== Copyright (C) 2002-2013, and GNU GPL‘d, by Julian Seward et al.
==12784== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
==12784== Command: ./a.out
==12784==
//缓冲区溢出,无效的读
==12784== Invalid write of size 4
==12784== at 0x4005CB: main (testMem.cc:9)
==12784== Address 0x51fc1d0 is 0 bytes after a block of size 400 alloc‘d
==12784== at 0x4C2AB80: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==12784== by 0x4005A6: main (testMem.cc:6)
==12784==
==12784== Conditional jump or move depends on uninitialised value(s)
==12784== at 0x4E8158E: vfprintf (vfprintf.c:1660)
==12784== by 0x4E8B498: printf (printf.c:33)
==12784== by 0x400616: main (testMem.cc:12)
==12784==
//使用了未初始化的变量
==12784== Use of uninitialised value of size 8
==12784== at 0x4E80A4B: _itoa_word (_itoa.c:179)
==12784== by 0x4E846F6: vfprintf (vfprintf.c:1660)
==12784== by 0x4E8B498: printf (printf.c:33)
==12784== by 0x400616: main (testMem.cc:12)
==12784==
==12784== Conditional jump or move depends on uninitialised value(s)
==12784== at 0x4E80A55: _itoa_word (_itoa.c:179)
==12784== by 0x4E846F6: vfprintf (vfprintf.c:1660)
==12784== by 0x4E8B498: printf (printf.c:33)
==12784== by 0x400616: main (testMem.cc:12)
==12784==
==12784== Conditional jump or move depends on uninitialised value(s)
==12784== at 0x4E84742: vfprintf (vfprintf.c:1660)
==12784== by 0x4E8B498: printf (printf.c:33)
==12784== by 0x400616: main (testMem.cc:12)
==12784==
==12784== Conditional jump or move depends on uninitialised value(s)
==12784== at 0x4E81659: vfprintf (vfprintf.c:1660)
==12784== by 0x4E8B498: printf (printf.c:33)
==12784== by 0x400616: main (testMem.cc:12)
==12784==
==12784== Conditional jump or move depends on uninitialised value(s)
==12784== at 0x4E816DC: vfprintf (vfprintf.c:1660)
==12784== by 0x4E8B498: printf (printf.c:33)
==12784== by 0x400616: main (testMem.cc:12)
==12784==
sum=4950
==12784==
//堆的检测,未释放内存
==12784== HEAP SUMMARY:
==12784== in use at exit: 400 bytes in 1 blocks
==12784== total heap usage: 1 allocs, 0 frees, 400 bytes allocated
==12784==
==12784== 400 bytes in 1 blocks are definitely lost in loss record 1 of 1
==12784== at 0x4C2AB80: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==12784== by 0x4005A6: main (testMem.cc:6)
==12784==
==12784== LEAK SUMMARY:
==12784== definitely lost: 400 bytes in 1 blocks
==12784== indirectly lost: 0 bytes in 0 blocks
==12784== possibly lost: 0 bytes in 0 blocks
==12784== still reachable: 0 bytes in 0 blocks
==12784== suppressed: 0 bytes in 0 blocks
==12784==
==12784== For counts of detected and suppressed errors, rerun with: -v
==12784== Use --track-origins=yes to see where uninitialised values come from
==12784== ERROR SUMMARY: 14 errors from 8 contexts (suppressed: 0 from 0)
如果手头没有工具,或者有定制的需要,可以自己实现代码的插桩。例如重载malloc为其添加一层封装,记录所有的分配和释放,使用链表等结构记录节点的增删,最后遍历一下。或者使用链接期垫片,动态链接会优先调用我们定义的同名函数。使用ld -wrap参数。
当然也可以使用调试的手段,直接log记录malloc和free。
参考:windows下的hook方法:
http://www.sxrczx.com/pages/impd.tencent.com/p29.html
陈硕的插装单元测试的方法:
http://blog.csdn.net/Solstice/article/details/6423342
11、动态链接和静态链接的区别
参考《深入理解计算机系统》第七章,链接。
静态链接以一组可重定位目标文件为输入,文件由各种不同的代码和数据节组成,通过符号解析和重定位生成一个完全链接的可以加载和运行的可执行文件。
静态链接有一些明显的缺点,一是如果需要更新一个库,需要重新编译和链接库文件。二是对于一些标准的函数,如果将这些代码复制到每个程序运行的文本段中,会对存储器的资源造成很大的浪费。
共享库就是为解决静态链接而生,共享库是一个目标模块。在运行时,可以加载到任意存储器地址,并和一个在存储器中的程序链接起来。这个过程称为动态链接。共享库在unix下通常使用.so后缀,window下为dll。
共享库使用两种方式共享,一是一个库只有一个so文件。所有引用该库的执行程序共享这个文件的代码和数据。二是一个共享库的.text节的一个副本可以被不同的进程共享。
注意在整个程序的链接过程中,链接器只是拷贝了一些重定位和符号信息。在程序加载(execve)时才会解析so文件中代码和数据的引用。
12、32位系统一个进程最多多少堆内存
32位就是4G的寻址空间,linux将其分为两部分,虚拟地址从0xC0000000到0xffffffff用于内核,为系统空间。较低的3G字节为用户空间。理论上每个进程最多可以使用3G堆内存。而实际上一般限制到2G。
而线程的栈空间大小在linux下可以使用ulimit -s查询,我的环境下默认是8192字节。windows下一说默认1M,一说2M。
13、多线程和多进程的区别(重点 必须从cpu调度,上下文切换,数据共享,多核cup利用率,资源占用,等等各方面回答,然后有一个问题必须会被问到:哪些东西是一个线程私有的?答案中必须包含寄存器,否则悲催)
区别的意思是优缺点吧。
多线程:
- 高效的内存共享,数据共享
- 较轻的上下文切换开销,不用切换地址空间,不用更改CR3寄存器,不用清空TLB。
创建销毁切换比较简单
多进程:
更强的容错性,不会一阻全阻,一个进程崩溃不会整个系统崩溃。
更好的多核伸缩性,进程的使用将许多内核资源(如地址空间,页表,打开的文件)隔离,在多核系统上的可伸缩性强于多线程程序
在多核利用率上,多进程和多线程同样可以提高多核利用率。
其实对于创建和销毁,上下文切换,其实在Linux系统下差别不大,Window下有较大差别。
综上,多进程和多线程的最主要的区别就在资源共享,隔离问题。如果工作使用的内存较大,使用多线程可以避免CPU cache的换入换出,影响性能。线程私有
ID,每个线程都有自己的ID作为进程中唯一的表述。
- 一组寄存器值,用来保存状态
- 各自的堆栈
- 错误返回码,防止错误还未被处理就被其他线程修改。
- 信号屏蔽码,每个线程感兴趣的信号不同。
- 优先级
- 共享的:进程的代码段,公有数据,进程打开的文件描述符,全局内存,进程的栈,堆内存等。
14、写一个c程序辨别系统是64位 or 32位
理论上是不可以使用sizeof加指针判断系统是32或者64位的。sizeof的定义与编译器相关。
#include <stdio.h>
int main(void){
printf("%d\n", __WORDSIZE);
if (1==(1<<32))
printf("32 bit\n");
else
printf("64 bit\n");
return 0;
}
15、写一个c程序辨别系统是大端or小端字节序
int is_big_endian(void)
{
union {
uint32_t i;
char c[4];
} bint = {0x01020304};
return bint.c[0] == 1;
}
if ( htonl(47) == 47 ) {
// Big endian
} else {
// Little endian.
}
16、信号:列出常见的信号,信号怎么处理?
上面已经贴过了,这里给各位lazy boy再粘一次。
17、i++是否原子操作?并解释为什么???????
这个问题网上的解答千篇一律,并且具有误导性。一般32位系统下,都会解释说i++实际上拆分成3条汇编指令,读,加,写回。
我这里给个64位操作系统下的例子和反汇编结果。有兴趣的同学可以运行一下这个程序,看看i会出来什么奇怪的值。
#include <stdio.h>
#include <pthread.h>
int i = 0;
pthread_t thread[2];
void *thread1(){
int num = 0;
while(num<50){
i++;
printf("thread1: i = %d\n",i);
}
pthread_exit(NULL);
}
void *thread2(){
int num = 0;
while(num<50){
i++;
printf("thread2: i = %d\n",i);
}
pthread_exit(NULL);
}
int main(void){
pthread_create(&thread[0],NULL,thread1,NULL);
pthread_create(&thread[1],NULL,thread2,NULL);
printf("the last number of i :%d\n",i);
int test = 0;
test++;
return 0;
}
.....
000000000040069d <thread1>:
40069d: 55 push %rbp
40069e: 48 89 e5 mov %rsp,%rbp
4006a1: 48 83 ec 10 sub $0x10,%rsp
4006a5: c7 45 fc 00 00 00 00 movl $0x0,-0x4(%rbp)
4006ac: eb 26 jmp 4006d4 <thread1+0x37>
4006ae: 8b 05 a0 09 20 00 mov 0x2009a0(%rip),%eax # 601054 <i>
4006b4: 83 c0 01 add $0x1,%eax
4006b7: 89 05 97 09 20 00 mov %eax,0x200997(%rip) # 601054 <i>
4006bd: 8b 05 91 09 20 00 mov 0x200991(%rip),%eax # 601054 <i>
4006c3: 89 c6 mov %eax,%esi
4006c5: bf 14 08 40 00 mov $0x400814,%edi
4006ca: b8 00 00 00 00 mov $0x0,%eax
4006cf: e8 9c fe ff ff callq 400570 <printf@plt>
4006d4: 83 7d fc 31 cmpl $0x31,-0x4(%rbp)
4006d8: 7e d4 jle 4006ae <thread1+0x11>
4006da: bf 00 00 00 00 mov $0x0,%edi
4006df: e8 bc fe ff ff callq 4005a0 <pthread_exit@plt>
000000000040072b <main>:
40072b: 55 push %rbp
40072c: 48 89 e5 mov %rsp,%rbp
40072f: 48 83 ec 10 sub $0x10,%rsp
400733: b9 00 00 00 00 mov $0x0,%ecx
400738: ba 9d 06 40 00 mov $0x40069d,%edx
40073d: be 00 00 00 00 mov $0x0,%esi
400742: bf 60 10 60 00 mov $0x601060,%edi
400747: e8 14 fe ff ff callq 400560 <pthread_create@plt>
40074c: b9 00 00 00 00 mov $0x0,%ecx
400751: ba e4 06 40 00 mov $0x4006e4,%edx
400756: be 00 00 00 00 mov $0x0,%esi
40075b: bf 68 10 60 00 mov $0x601068,%edi
400760: e8 fb fd ff ff callq 400560 <pthread_create@plt>
400765: 8b 05 e9 08 20 00 mov 0x2008e9(%rip),%eax # 601054 <i>
40076b: 89 c6 mov %eax,%esi
40076d: bf 36 08 40 00 mov $0x400836,%edi
400772: b8 00 00 00 00 mov $0x0,%eax
400777: e8 f4 fd ff ff callq 400570 <printf@plt>
40077c: c7 45 fc 00 00 00 00 movl $0x0,-0x4(%rbp)
400783: 83 45 fc 01 addl $0x1,-0x4(%rbp)
400787: b8 00 00 00 00 mov $0x0,%eax
40078c: c9 leaveq
40078d: c3 retq
40078e: 66 90 xchg %ax,%ax
注意对于全局的i++,在线程里的指令为下面三步,这里的三步都是可以被线程打断的:
mov 0x200959(%rip),%eax # 601054 <i>
add $0x1,%eax
mov %eax,0x200950(%rip) # 601054 <i>
对于局部的test++,对应指令为:
addl $0x1,-0x4(%rbp)
难道根据网上说的理论,这里是一条指令,就是原子的么?未必。Imm()的意思是基地址+偏移量的存储器(既内存)操作。这条指令中同时涉及了内存的读写。存在两次内存访问。哪怕是零此或者一次内存访问,只有在数据对齐的情况下才是原子的,不然需要多次load操作。
CPU和内存直接存在多道缓冲,在多核的情况下,无法保证数据一致性。即使在单核的情况下,也可能被DMA或者中断干扰。注意,我强调的是原子操作的判定跟是不是一条汇编指令没有必然联系。
另外,如果不存在资源的共享访问,单论原子操作也是没有意义的。
18、说出你所知道的各类linux系统的各类同步机制(重点),什么是死锁?如何避免死锁(每个技术面试官必问)
前面介绍过linux系统的通讯机制,主要是指进程间通讯,其实通讯就是进程同步的手段。如果问进程间同步,见问题7,这里要说的linux系统的同步机制是讲线程间的同步。
简单总结一下。更多资料参考《unix环境高级编程》、《操作系统》。
互斥量
首先是最基础的加锁原语,互斥量。既确保同一时间只有一个线程访问数据,通过在访问共享资源前对互斥量加锁,阻塞其他试图再次加锁的线程知道互斥锁被释放。互斥的具体实现有多种方法,例如开关中断,使用原子的机器指令。
读写锁
与互斥量类似,不过允许更高的并行性。读写锁有三种状态,读模式的加锁,写模式的加锁,不加锁状态。一次只有一个线程可以占有写模式的读写锁,但是可以多个线程可以同时占用读模式的读写锁。既读模式下可以共享,写模式下互斥。一般一个线程试图以读模式获取锁时,读写锁通常会阻塞随后的读模式锁请求。
条件变量
互斥量是加锁原语,条件变量属于等待原语,用于等待某个条件成立后唤醒阻塞的线程。条件变量与互斥量一起使用,条件本身由互斥量保护。Java Object内置了条件变量wait(),notify(),notifyAll()
。
pthread_cond_wait(),pthread_cond_signal(),pthread_cond_broadcast
(Unix),从函数的命名就可以看出其大致作用。
根据陈硕的总结,条件变量的正确使用方式:
对于wait端:
1.必须与mutex一起使用。
2.在mutex已上锁时才能调用wait()。
3.把判断布尔条件和wait()放到while循环中。
第三个条件主要是为了防止spurious wakeup,既虚假唤醒。因为pthread_conf_wait能被除了pthread_cond_signal(),pthread_cond_broadcast外的其他信号唤醒。需要再wait后再次检查,同时也是为了避免错过一次条件变量后永远的等待下去。
对于signal端:
1.一定不要在mutex已经上锁的情况下调用signal。
2.在signal之前一般要修改布尔表达式。
3.修改布尔表达式通常用mutex保护。
4.注意区分signal和broadcast:“broadcast通常用于表明状态变化,signal通常用于表示资源可用”。
自旋锁
自旋锁与互斥量类似,但它不是通过休眠使进程阻塞,二是在获取锁之前一直处于忙等。既一直占用CPU资源直到锁被释放。
屏障
屏障主要用于多个线程之间的并行工作的协调。屏障允许每个线程等待,直到所有的合作线程都达到某个点,然后从该点继续执行。
信号量
这个在《unix环境高级编程》中没有提及,在《操作系统》中有论述。
信号量可作用与进程间合作,以及多线程的同步。
一个进程可以被迫在某一个位置停止,直到接收到某一个信号。为了发信号,需要使用一个称为信号量的特殊变量,可以看做一个具有整数值得变量。其中只允许信号量取0和1的称为二元信号量。非二元信号量常称为计数信号量或一般信号量。
一般在信号量上定义三个操作:
1.一个信号量可以初始化成非负数。
2.semWait操作使信号量减1。如果值变为负数,则执行semWait的进程或线程被阻塞,否则继续执行。
3.semSignal操作使信号量加1。如果值<=0,则被semWait阻塞的进程被解除阻塞。
信号量需要队列保存阻塞在信号量上等待的进程。至于进程按什么顺序移除,最公平的是先进先出,采用此策略的为强信号量。没有规定顺序的为弱信号量。
互斥量和二元信号量的主要区别在于互斥量的加锁和解锁必须由同一线程分别对应使用,信号量可以由一个线程释放,另一个线程得到。至于用于互斥和用于同步的说法,十分牵强。
陈硕关于信号量的建议是不用。
因为可用条件变量加互斥量完全代替,另外还需要担心计数值需要和自己的数据长度常常保持一致的问题。
死锁
死锁大概已经被讲烂了,我也不想再搬运了。坚持使用Scoped Locking,死锁的时候好检测。
http://blog.csdn.net/joejames/article/details/37960873
19、列举说明linux系统的各类异步机制
老实讲,我不知道这个问题是问什么的。
首先,同步异步只是一种概念问题。例如两个任务A和B,同步是A等待B做完,注意同步并不是同时进行,而是需要协调合作的意思。异步就是A通知B执行后立即返回,自己做其他事情,等B完成后取结果。
兴许这个问题是指各类信号机制,或者是异步IO,proactor模式。我这里就不细讲了。对于这种问题,和问问题的人详细探讨,阐述清楚概念,让他把握一下提问的艺术。
20、exit() _exit()的区别?
exit()是进程终止时调用的函数。exit()会首先调用各终止处理程序(可以使用atexit()为进程注册终止处理程序,一个进程最多登记32个函数),然后关闭所有的打开流,flush输出缓冲的数据。最后调用_exit()或者_Exit()。
简而言之,exit()比_exit()多了可以自己定义的处理函数以及对所有流的关闭。注意,大多Unix系统中,exit(3)是标准C库中的一个函数,_exit(2)则是一个系统调用。
21、如何实现守护进程?
守护进程是在后台运行且不与任何控制终端管理的进程。Unix系统中有很多这样的进程,使用命令ps -axj可以显示此类进程。
在编写守护进程程序的时候需要遵守一些基本的规则和步骤。
1.首先调用umask将文件模式创建屏蔽字设置为一个已知值,通常为0,。通过显示的调用设置权限,防止继承得来的文件模式拒绝了某些权限。
2.调用fork,然后使父进程exit。这样做使得:如果进程是作为shell命令启动的,父进程终止为让shell认为该命令执行完毕,不用挂在终端输入上。另外是为了后续的setsid函数服务,因为调用setsid的进程的先决条件是需要不是进程组组长。
3.调用setsid创建一个新会话。调用成功会使得调用进程成为新会话的首进程,并且成为一个新进程组的组长,且调用进程没有控制终端,如果先前有联系也会切断。
4.将当前工作目录更改为根目录。防止继承来的工作目录在文件系统中无法卸载。
5.关闭不再需要的文件描述符。
6.忽略SIGHUP信号并在此fork。在此fork的原因是防止误操作打开终端。只有会话首进程可以打开终端设备,再fork一次,把父进程退出,子进程继续运行,确保了改进程不是首进程。而这里必须忽略SIGHUP信号,因为会话头进程终止时,其会话的所有进程都会受到SIGHUP信号。注意,这一步是可选的,并不是标准配置。
7.某些守护进程打开dev/null使其某些文件描述符0、1和2。这样,任何一个试图读标准输入、写标准输出或标准错误的例程都不会产生任何效果。
最后,因为守护进程不应该有终端控制,所以在处理出错消息的时候不能简单的写到标准错误上,好在已经有专门的syslogd进程提供了产生日志消息的接口syslog(3)函数。改函数将消息发送至unix域数据报套接字/dev/log。在/etc/syslog.conf文件中可以配置不同种类消息送向何处。
apue中的例子:
#include "apue.h"
#include <syslog.h>
#include <fcntl.h>
#include <sys/resource.h>
void
daemonize(const char *cmd)
{
int i, fd0, fd1, fd2;
pid_t pid;
struct rlimit rl;
struct sigaction sa;
/*
* Clear file creation mask.
*/
umask(0);
/*
* Get maximum number of file descriptors.
*/
if(getrlimit(RLIMIT_NOFILE, &rl) < 0)
err_quit("%s: can‘t get file limit", cmd);
/*
* Become a session leader to lose controlling TTY.
*/
if((pid = fork()) < 0)
err_quit("%s: can‘t fork", cmd);
else if (pid != 0) /* parent */
exit(0);
setsid();
/*
* Ensure future opens won‘t allocate controlling TTYs.
*/
sa.sa_handler = SIG_IGN;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
if(sigaction(SIGHUP, &sa, NULL) < 0)
err_quit("%s: can‘t ignore SIGHUP");
if((pid = fork()) < 0)
err_quit("%s: can‘t fork", cmd);
else if( pid != 0 ) /* parent */
exit(0);
/*
* Change the current working directory to the root so
* we won‘t prevent file system from being unmounted.
*/
if(chdir("/") < 0)
err_quit("%s: can‘t change directory to /");
/*
* Close all open file descriptors.
*/
if(rl.rlim_max = RLIM_INFINITY)
rl.rlim_max = 1024;
for(i = 0; i < rl.rlim_max; i++)
close(i);
/*
* Attach file descriptors 0, 1, and 2 to /dev/null.
*/
fd0 = open("/dev/null", O_RDWR);
fd1 = dup(0);
fd2 = dup(0);
/*
* Initialize the log file.
*/
openlog(cmd, LOG_CONS, LOG_DAEMON);
if(fd0 != 0 || fd1 != 1 || fd2 != 2)
{
syslog(LOG_ERR, "unexpected file descriptors %d %d %d", fd0, fd1, fd2);
exit(1);
}
}
22、linux的内存管理机制是什么?
从进程虚拟内存和内核内存分配两个方面简单的说一下吧。这里涉及的点太多,不一一细讲。
首先看内存分区的策略,是分页还是分段。
分页是将主存划分成各类大小固定的块,分段是将用户进程地址空间划分成若干大小不同的段,每个段有自己完整的逻辑信息。这两种块都不需要连续的位于主存中,可以占用主存的不同区域。
再看是否使用虚拟内存。
进程中的所有存储器访问都是逻辑地址,这些逻辑地址在运行时动态的转化成物理地址。使用逻辑地址访问,使得用户可以突破主存大小的限制,使用分配在磁盘上的虚拟内存。
Linux使用虚拟内存加分页机制。
因为页表本身也需要存储空间,如果只有一张页表的话,对于4G内存,按主流的每页4KB,需要4G/4KB=1M个页,按每个也32B计算,需要32MB大小。为了节省这部分空间,出现了多级页表。04年之后使用的是四级页面,书中多用3级页面讲解,但是思想是一致的。其虚拟地址到物理地址的转化大致如下图:
即通过查找多级表项到最后一级,通过最后一级的地址加上偏移量得到最后的物理地址。
页分配
Linux内核内存分配的基础是用于用户虚拟内存管理的页分配机制。页面分配使用伙伴系统。
在这种分配方式下,内存从一个2的N次幂大的内存块中分配。当内存块比要分配的长度大两倍以上,内存块平均分裂成两块。选中其中一半,重复这个过程(检查长度,满足条件则分裂)直到内存块刚好等于需要的长度。
所有的块信息保存在一个排序过的链表或者二叉树中。当一个块被释放的时候与他的相邻块进行比较。如果他们都被释放,就合并成一个大块放进更大的一个块列表 中。每当分配结束,分配器会从尽量小的块重新开始分配,以避免产生不必要的碎片。
对于一些小块,Linux使用slab分配方案解决内部碎片问题。
页面替换算法
用于处理必须读取一个新页时,应该替换主存中的哪一页。Linux使用时钟算法。其基本策略是把所有页面保存在一个类似时钟面的环形链表中。
当发生缺页中断时,算法首先检查表针指向的页面,如果它的R位是0就淘汰该页面,并把新的页面插入这个位置,然后把表针前移一个位置;如果R位是1就清除R位并把表针前移一个位置,重复这个过程直到找到了一个R位为0的页面为止。
当然还有其他页面算法。
- FIFO,先入先出
- OPT,最佳,无法预测下次访问距当前时间最长页,无法实现
- LRU,最近最少使用,需要给每个页添加一个最后访问的时间标签,较难实现
23、linux的任务调度机制是什么?
一般讨论这个问题时分为单核调度和多核调度。Linux的调度算法换过很多次了,再去照抄书本上的内容就有些不合时宜了。
单核调度
调度器就是为了解决下一个要运行的线程是谁的问题,参考一些指标,例如优先级和使用时间。实时进程的调度就是挑选最高优先级的运行。根据优先级排队FIFO,或者加上时间片。一般使用双向链表实现。这里主要讨论普通进程的调度。
最基础的想法就是按时间片轮转,大家分配一样的时间例如10ms,然后轮流执行,这样会造成交互性非常差,并且任务不分级。可以通过将线程定义为实时线程解决,让某个线程优先解决,但是这样会造成CPU被占满,造成资源浪费。
最新使用的是CFS算法,使用红黑树实现优先队列。给每个调度队列的线程一个vruntime变量,记录线程的运行时间,每次都调度vruntime最小的线程。然后通过nice值作为vruntime流逝的加权,nice大的进程时间流逝快,它占用的时间片就少。
多核调度
多核调度,就是每个CPU有一个运行队列,创建线程时加入到各自队列中,然后对这个队列执行单核的调度算法。
当然还需要注意负载的均衡问题,Linux把不同线程组织到不同的sched_domain中,每个domain包含一组线程,各自有其对应算法。
24、标准库函数和系统调用的区别?
其实不少Linux下的C标准库函数都是对系统调用的包装,也有一些直接内联的汇编。
25、补充一个坑爹坑爹坑爹坑爹的问题:系统如何将一个信号通知到进程?
前面讲过信号了,为什么又问一遍?这个问题有什么特殊的地方么?
内核给进程发送信号,是在进程所在的进程表项的信号域设置对应的信号的位。
后台开发知识点总结(一、Linux和OS)