首页 > 代码库 > 后台开发知识点总结(一、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文件有三种类型:

  1. 可重定位的对象文件(Relocatable file) 由汇编器汇编生成的 .o 文件
  2. 可执行的对象文件(Executable file) 可执行应用程序
  3. 可被共享的对象文件(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下面的几种进程通讯方式。

  1. 管道:管道的名字挺形象的,就一个一个先进先出的队列,一个进程从一端读,另一个进程从另一端写,是一个环形缓冲区。管道有字节缓冲区,因此有大小限制。同时,管道分为命名管道(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、补充一个坑爹坑爹坑爹坑爹的问题:系统如何将一个信号通知到进程?

前面讲过信号了,为什么又问一遍?这个问题有什么特殊的地方么?
内核给进程发送信号,是在进程所在的进程表项的信号域设置对应的信号的位。

<script type="text/javascript"> $(function () { $(‘pre.prettyprint code‘).each(function () { var lines = $(this).text().split(‘\n‘).length; var $numbering = $(‘
    ‘).addClass(‘pre-numbering‘).hide(); $(this).addClass(‘has-numbering‘).parent().append($numbering); for (i = 1; i <= lines; i++) { $numbering.append($(‘
  • ‘).text(i)); }; $numbering.fadeIn(1700); }); }); </script>

    后台开发知识点总结(一、Linux和OS)