pltosos opt post no schedaplausos 的意思

后台开发知识点总结(一、Linux和OS)_其它编程-织梦者
当前位置:&>&&>& > 后台开发知识点总结(一、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 --------
------ Semaphore Arrays --------
------ Message Queues --------
used-bytes
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
chmod -R u+r directory
递归地给directory目录下所有文件和子目录的属主分配读的权限
chmod 4755
设置用ID,给属主分配读、写和执行权限,给组和其他用户分配读、执行的权限。
观察运行中的进程状态可以使用静态的ps和动态的top以及top的增强版htop。这些命令可以统计各个进程的CPU和内存MEM使用率。当然还有专门针对cpu,内存,io做监控的各个命令,mpstat,vmstat,iostat。
这里会有一些进程优先级的概念,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:/articles/9070.html
  awk:.tw/bookRead/skns00004_read.pdf
http://wanggen.myweb.hinet.net/ach3/ach3.html?MywebPageId=5373791#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:
7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
2's complement, little endian
1 (current)
UNIX - System V
ABI Version:
EXEC (Executable file)
Advanced Micro Devices X86-64
Entry point address:
Start of program headers:
64 (bytes into file)
Start of section headers:
18616 (bytes into file)
Size of this header:
64 (bytes)
Size of program headers:
56 (bytes)
Number of program headers:
Size of section headers:
64 (bytes)
Number of section headers:
Section header string table index: 33
Section Headers:
[ 1] .interp
[ 2] .note.ABI-tag
[ 3] .note.gnu.build-i NOTE
[ 4] .gnu.hash
[ 5] .dynsym
[ 6] .dynstr
[ 7] .gnu.version
[ 8] .gnu.version_r
[ 9] .rela.dyn
[10] .rela.plt
[11] .init
[13] .text
[14] .fini
[15] .rodata
[16] .eh_frame_hdr
[17] .eh_frame
[18] .init_array
INIT_ARRAY
[19] .fini_array
FINI_ARRAY
[21] .dynamic
[23] .got.plt
[24] .data
[26] .comment
[27] .debug_aranges
[28] .debug_info
[29] .debug_abbrev
[30] .debug_line
[31] .debug_str
[32] .debug_loc
[33] .shstrtab
[34] .symtab
[35] .strtab
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:
0x0 0x0040
0x01f8 0x01f8
0x8 0x0238
0x001c 0x001c
[Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
0x0 0x0000
0x2cf4 0x2cf4
0x2df8 0x2df8 0x2df8
0x2e10 0x2e10 0x2e10
0x01d0 0x01d0
0x4 0x0254
GNU_EH_FRAME
0x8 0x2798
0x0 0x0000
0x2df8 0x2df8 0x2df8
Section to Segment mapping:
Segment Sections...
.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
.init_array .fini_array .jcr .dynamic .got .got.plt .data .bss
.note.ABI-tag .note.gnu.build-id
.eh_frame_hdr
.init_array .fini_array .jcr .dynamic .got
Dynamic section at offset 0x2e10 contains 24 entries:
Name/Value
0x0001 (NEEDED)
Shared library: [libc.so.6]
0x000c (INIT)
0x000d (FINI)
0x0019 (INIT_ARRAY)
0x001b (INIT_ARRAYSZ)
0x001a (FINI_ARRAY)
0x001c (FINI_ARRAYSZ)
0xffffef5 (GNU_HASH)
0x0005 (STRTAB)
0x0006 (SYMTAB)
0x000a (STRSZ)
365 (bytes)
0x000b (SYMENT)
24 (bytes)
0x0015 (DEBUG)
0x0003 (PLTGOT)
0x0002 (PLTRELSZ)
648 (bytes)
0x0014 (PLTREL)
0x0017 (JMPREL)
0x0007 (RELA)
0x0008 (RELASZ)
96 (bytes)
0x0009 (RELAENT)
24 (bytes)
0xffffffe (VERNEED)
0xfffffff (VERNEEDNUM)
0xffffff0 (VERSYM)
0x0000 (NULL)
Relocation section '.rela.dyn' at offset 0x7d8 contains 4 entries:
Sym. Value
Sym. Name + Addend
R_X86_64_GLOB_DAT 0000 __gmon_start__ + 0
001d R_X86_64_COPY
3740 stdout + 0
001e R_X86_64_COPY
3748 stdin + 0
001f R_X86_64_COPY
3750 stderr + 0
Relocation section '.rela.plt' at offset 0x838 contains 27 entries:
Sym. Value
Sym. Name + Addend
R_X86_64_JUMP_SLO 0000 getenv + 0
R_X86_64_JUMP_SLO 0000 __errno_location + 0
R_X86_64_JUMP_SLO 0000 strcpy + 0
R_X86_64_JUMP_SLO 0000 puts + 0
R_X86_64_JUMP_SLO 0000 write + 0
R_X86_64_JUMP_SLO 0000 __stack_chk_fail + 0
R_X86_64_JUMP_SLO 0000 alarm + 0
R_X86_64_JUMP_SLO 0000 close + 0
R_X86_64_JUMP_SLO 0000 read + 0
000a R_X86_64_JUMP_SLO 0000 __libc_start_main + 0
000b R_X86_64_JUMP_SLO 0000 fgets + 0
000c R_X86_64_JUMP_SLO 0000 signal + 0
000d R_X86_64_JUMP_SLO 0000 gethostbyname + 0
000e R_X86_64_JUMP_SLO 0000 __memmove_chk + 0
000f R_X86_64_JUMP_SLO 0000 __memcpy_chk + 0
R_X86_64_JUMP_SLO 0000 strtol + 0
R_X86_64_JUMP_SLO 0000 fflush + 0
R_X86_64_JUMP_SLO 0000 __isoc99_sscanf + 0
R_X86_64_JUMP_SLO 0000 __printf_chk + 0
R_X86_64_JUMP_SLO 0000 fopen + 0
R_X86_64_JUMP_SLO 0000 exit + 0
R_X86_64_JUMP_SLO 0000 connect + 0
R_X86_64_JUMP_SLO 0000 __fprintf_chk + 0
R_X86_64_JUMP_SLO 0000 sleep + 0
001a R_X86_64_JUMP_SLO 0000 __ctype_b_loc + 0
001b R_X86_64_JUMP_SLO 0000 __sprintf_chk + 0
001c R_X86_64_JUMP_SLO 0000 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:
GLOBAL DEFAULT
UND getenv@GLIBC_2.2.5 (2)
GLOBAL DEFAULT
UND __errno_location@GLIBC_2.2.5 (2)
GLOBAL DEFAULT
UND strcpy@GLIBC_2.2.5 (2)
GLOBAL DEFAULT
UND puts@GLIBC_2.2.5 (2)
GLOBAL DEFAULT
UND write@GLIBC_2.2.5 (2)
GLOBAL DEFAULT
UND __stack_chk_fail@GLIBC_2.4 (3)
GLOBAL DEFAULT
UND alarm@GLIBC_2.2.5 (2)
GLOBAL DEFAULT
UND close@GLIBC_2.2.5 (2)
GLOBAL DEFAULT
UND read@GLIBC_2.2.5 (2)
GLOBAL DEFAULT
UND __libc_start_main@GLIBC_2.2.5 (2)
GLOBAL DEFAULT
UND fgets@GLIBC_2.2.5 (2)
GLOBAL DEFAULT
UND signal@GLIBC_2.2.5 (2)
GLOBAL DEFAULT
UND gethostbyname@GLIBC_2.2.5 (2)
GLOBAL DEFAULT
UND __memmove_chk@GLIBC_2.3.4 (4)
GLOBAL DEFAULT
UND __memcpy_chk@GLIBC_2.3.4 (4)
UND __gmon_start__
GLOBAL DEFAULT
UND strtol@GLIBC_2.2.5 (2)
GLOBAL DEFAULT
UND fflush@GLIBC_2.2.5 (2)
GLOBAL DEFAULT
UND __isoc99_sscanf@GLIBC_2.7 (5)
GLOBAL DEFAULT
UND __printf_chk@GLIBC_2.3.4 (4)
GLOBAL DEFAULT
UND fopen@GLIBC_2.2.5 (2)
GLOBAL DEFAULT
UND exit@GLIBC_2.2.5 (2)
GLOBAL DEFAULT
UND connect@GLIBC_2.2.5 (2)
GLOBAL DEFAULT
UND __fprintf_chk@GLIBC_2.3.4 (4)
GLOBAL DEFAULT
UND sleep@GLIBC_2.2.5 (2)
GLOBAL DEFAULT
UND __ctype_b_loc@GLIBC_2.3 (6)
GLOBAL DEFAULT
UND __sprintf_chk@GLIBC_2.3.4 (4)
GLOBAL DEFAULT
UND socket@GLIBC_2.2.5 (2)
GLOBAL DEFAULT
25 stdout@GLIBC_2.2.5 (2)
GLOBAL DEFAULT
25 stdin@GLIBC_2.2.5 (2)
GLOBAL DEFAULT
25 stderr@GLIBC_2.2.5 (2)
Symbol table '.symtab' contains 157 entries:
0 SECTION LOCAL
0 SECTION LOCAL
0 SECTION LOCAL
0 SECTION LOCAL
0 SECTION LOCAL
0 SECTION LOCAL
0 SECTION LOCAL
0 SECTION LOCAL
0 SECTION LOCAL
0 SECTION LOCAL
0 SECTION LOCAL
0 SECTION LOCAL
0 SECTION LOCAL
0 SECTION LOCAL
0 SECTION LOCAL
0 SECTION LOCAL
0 SECTION LOCAL
0 SECTION LOCAL
0 SECTION LOCAL
0 SECTION LOCAL
0 SECTION LOCAL
0 SECTION LOCAL
0 SECTION LOCAL
0 SECTION LOCAL
0 SECTION LOCAL
0 SECTION LOCAL
0 SECTION LOCAL
0 SECTION LOCAL
0 SECTION LOCAL
0 SECTION LOCAL
0 SECTION LOCAL
0 SECTION LOCAL
13 call_gmon_start
ABS crtstuff.c
20 __JCR_LIST__
13 deregister_tm_clones
13 register_tm_clones
13 __do_global_dtors_aux
25 completed.6976
19 __do_global_dtors_aux_fin
13 frame_dummy
18 __frame_dummy_init_array_
ABS bomb.c
ABS phases.c
15 array.3449
ABS support.c
13 sig_handler
ABS driverlib.c
13 rio_readlineb
ABS crtstuff.c
17 __FRAME_END__
20 __JCR_END__
18 __init_array_end
21 _DYNAMIC
18 __init_array_start
23 _GLOBAL_OFFSET_TABLE_
GLOBAL DEFAULT
13 __libc_csu_fini
GLOBAL DEFAULT
GLOBAL DEFAULT
UND getenv@@GLIBC_2.2.5
GLOBAL DEFAULT
13 phase_defused
GLOBAL DEFAULT
GLOBAL DEFAULT
UND __errno_location@@GLIBC_2
UND _ITM_deregisterTMCloneTab
GLOBAL DEFAULT
25 stdout@@GLIBC_2.2.5
24 data_start
GLOBAL DEFAULT
25 input_strings
GLOBAL DEFAULT
UND strcpy@@GLIBC_2.2.5
GLOBAL DEFAULT
GLOBAL DEFAULT
UND puts@@GLIBC_2.2.5
GLOBAL DEFAULT
25 stdin@@GLIBC_2.2.5
GLOBAL DEFAULT
UND write@@GLIBC_2.2.5
GLOBAL DEFAULT
ABS _edata
GLOBAL DEFAULT
GLOBAL DEFAULT
GLOBAL DEFAULT
GLOBAL DEFAULT
GLOBAL DEFAULT
GLOBAL DEFAULT
UND __stack_chk_fail@@GLIBC_2
GLOBAL DEFAULT
25 num_input_strings
GLOBAL DEFAULT
13 phase_5
GLOBAL DEFAULT
13 initialize_bomb_solve
GLOBAL DEFAULT
13 blank_line
GLOBAL DEFAULT
13 submitr
GLOBAL DEFAULT
13 phase_3
GLOBAL DEFAULT
13 phase_1
GLOBAL DEFAULT
13 invalid_phase
GLOBAL DEFAULT
13 init_driver
GLOBAL DEFAULT
UND alarm@@GLIBC_2.2.5
GLOBAL DEFAULT
UND close@@GLIBC_2.2.5
GLOBAL DEFAULT
GLOBAL DEFAULT
UND read@@GLIBC_2.2.5
GLOBAL DEFAULT
UND __libc_start_main@@GLIBC_
GLOBAL DEFAULT
UND fgets@@GLIBC_2.2.5
GLOBAL DEFAULT
13 explode_bomb
GLOBAL DEFAULT
GLOBAL DEFAULT
24 __data_start
GLOBAL DEFAULT
UND signal@@GLIBC_2.2.5
GLOBAL DEFAULT
UND gethostbyname@@GLIBC_2.2.
GLOBAL DEFAULT
GLOBAL DEFAULT
UND __memmove_chk@@GLIBC_2.3.
GLOBAL DEFAULT
UND __memcpy_chk@@GLIBC_2.3.4
UND __gmon_start__
GLOBAL DEFAULT
UND strtol@@GLIBC_2.2.5
GLOBAL DEFAULT
GLOBAL HIDDEN
24 __dso_handle
GLOBAL DEFAULT
15 _IO_stdin_used
GLOBAL DEFAULT
GLOBAL DEFAULT
24 host_table
GLOBAL DEFAULT
GLOBAL DEFAULT
GLOBAL DEFAULT
13 string_length
GLOBAL DEFAULT
13 __libc_csu_init
GLOBAL DEFAULT
UND fflush@@GLIBC_2.2.5
GLOBAL DEFAULT
UND __isoc99_sscanf@@GLIBC_2.
GLOBAL DEFAULT
GLOBAL DEFAULT
GLOBAL DEFAULT
GLOBAL DEFAULT
GLOBAL DEFAULT
13 secret_phase
GLOBAL DEFAULT
GLOBAL DEFAULT
13 sigalrm_handler
GLOBAL DEFAULT
13 init_timeout
GLOBAL DEFAULT
ABS __bss_start
GLOBAL DEFAULT
GLOBAL DEFAULT
UND __printf_chk@@GLIBC_2.3.4
GLOBAL DEFAULT
GLOBAL DEFAULT
GLOBAL DEFAULT
GLOBAL DEFAULT
13 read_line
GLOBAL DEFAULT
GLOBAL DEFAULT
13 strings_not_equal
GLOBAL DEFAULT
13 phase_4
GLOBAL DEFAULT
UND fopen@@GLIBC_2.2.5
GLOBAL DEFAULT
13 phase_6
GLOBAL DEFAULT
25 scratch
UND _Jv_RegisterClasses
GLOBAL DEFAULT
13 driver_post
GLOBAL DEFAULT
13 phase_2
GLOBAL DEFAULT
UND exit@@GLIBC_2.2.5
GLOBAL DEFAULT
25 bomb_id
GLOBAL DEFAULT
UND connect@@GLIBC_2.2.5
GLOBAL HIDDEN
24 __TMC_END__
GLOBAL DEFAULT
UND __fprintf_chk@@GLIBC_2.3.
UND _ITM_registerTMCloneTable
GLOBAL DEFAULT
GLOBAL DEFAULT
GLOBAL DEFAULT
UND sleep@@GLIBC_2.2.5
GLOBAL DEFAULT
GLOBAL DEFAULT
GLOBAL DEFAULT
13 read_six_numbers
GLOBAL DEFAULT
GLOBAL DEFAULT
13 initialize_bomb
GLOBAL DEFAULT
UND __ctype_b_loc@@GLIBC_2.3
GLOBAL DEFAULT
25 stderr@@GLIBC_2.2.5
GLOBAL DEFAULT
UND __sprintf_chk@@GLIBC_2.3.
GLOBAL DEFAULT
UND socket@@GLIBC_2.2.5
Histogram for `.gnu.hash' bucket list length (total of 3 buckets):
% of total
Version symbols section '.gnu.version' contains 32 entries:
Addr: 0736
Offset: 0x000736
Link: 5 (.dynsym)
0 (*local*)
2 (GLIBC_2.2.5)
2 (GLIBC_2.2.5)
2 (GLIBC_2.2.5)
2 (GLIBC_2.2.5)
2 (GLIBC_2.2.5)
3 (GLIBC_2.4)
2 (GLIBC_2.2.5)
2 (GLIBC_2.2.5)
2 (GLIBC_2.2.5)
2 (GLIBC_2.2.5)
2 (GLIBC_2.2.5)
2 (GLIBC_2.2.5)
2 (GLIBC_2.2.5)
4 (GLIBC_2.3.4)
4 (GLIBC_2.3.4)
0 (*local*)
2 (GLIBC_2.2.5)
2 (GLIBC_2.2.5)
5 (GLIBC_2.7)
4 (GLIBC_2.3.4)
2 (GLIBC_2.2.5)
2 (GLIBC_2.2.5)
2 (GLIBC_2.2.5)
4 (GLIBC_2.3.4)
2 (GLIBC_2.2.5)
6 (GLIBC_2.3)
4 (GLIBC_2.3.4)
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: 0x0778
Offset: 0x000778
Link: 6 (.dynstr)
000000: Version: 1
File: libc.so.6
Name: GLIBC_2.3
Flags: none
Version: 6
Name: GLIBC_2.7
Flags: none
Version: 5
Name: GLIBC_2.3.4
Flags: none
Version: 4
Name: GLIBC_2.4
Flags: none
Version: 3
Name: GLIBC_2.2.5
Flags: none
Version: 2
Displaying notes found at file offset 0x with length 0x:
Description
NT_GNU_ABI_TAG (ABI version tag)
OS: Linux, ABI: 2.6.24
Displaying notes found at file offset 0x with length 0x:
Description
NT_GNU_BUILD_ID (unique build ID bitstring)
Build ID: 11c83ac9c51dcd0400b
.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
3) SIGQUIT
5) SIGTRAP
6) SIGABRT
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
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&
void print(string s,int index){
if(index==s.size()){
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.
//开始运行
Starting program: /home/ubuntu/workspace/a.out
Breakpoint 1, print (s="000", index=0) at 12from1Ton.cc:10
s[index]=i+'0';
//显示附近代码
(gdb) list
if(index==s.size()){
for(int i=0;i&10;++i){
s[index]=i+'0';
print(s,index+1);
//显示变量
(gdb) print s
$1 = "000"
//查看调用栈,即函数调用顺序
print (s="000", index=0) at 12from1Ton.cc:10
0x0ce9 in main () at 12from1Ton.cc:17
//继续从断点处执行
(gdb) continue
Continuing.
Breakpoint 1, print (s="000", index=1) at 12from1Ton.cc:10
s[index]=i+'0';
//单步调试,以完整的语句为单位往下执行
(gdb) next
print(s,index+1);
(gdb) next
Breakpoint 1, print (s="000", index=2) at 12from1Ton.cc: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.
Breakpoint 1, print (s="000", index=2) at 12from1Ton.cc:10
s[index]=i+'0';
//使用step命令可以进入隐藏的函数调用中,也可以拿来进入C++的class
//例如使用stl的时候,它会一层层的追溯下去
(gdb) step
print(s,index+1);
(gdb) step
print (s="001", index=3) at 12from1Ton.cc:5
if(index==s.size()){
//使用finish可以跳出调用栈的不停深入
//这里的例子并不好,可以自己用stl尝试一下,印象更深
(gdb) finish
Run till exit from #0
print (s="001", index=3) at 12from1Ton.cc:7
0x0c56 in print (s="001", index=2) at 12from1Ton.cc: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
s[index]=i+'0';
(gdb) continue
Continuing.
Temporary breakpoint 2, print (s="002", index=3) at 12from1Ton.cc:6
(gdb) continue
Continuing.
Breakpoint 1, print (s="002", index=2) at 12from1Ton.cc:10
s[index]=i+'0';
(gdb) continue
Continuing.
Breakpoint 1, print (s="003", index=2) at 12from1Ton.cc:10
s[index]=i+'0';
(gdb) step
print(s,index+1);
(gdb) step
print (s="005", index=3) at 12from1Ton.cc:5
if(index==s.size()){
//使用up也可以往高层走
0x0c56 in print (s="005", index=2) at 12from1Ton.cc: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
(gdb) continue
Continuing.
Breakpoint 1, print (s="005", index=2) at 12from1Ton.cc:10
s[index]=i+'0';
the current value of index is 2
Breakpoint 1, print (s="006", index=2) at 12from1Ton.cc:10
s[index]=i+'0';
the current value of index is 2
//condition可以跳过其他断点,只在达到我们设定的条件时停下
(gdb) condition 1 s[0]=='2'
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/ubuntu/workspace/a.out
Breakpoint 1, print (s="200", index=1) at 12from1Ton.cc:10
s[index]=i+'0';
the current value of index is 1
Breakpoint 1, print (s="200", index=2) at 12from1Ton.cc:10
s[index]=i+'0';
the current value of index is 2
//取消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
Disp Enb Address
breakpoint
0x0c98 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.
Starting program: /home/ubuntu/workspace/a.out
Breakpoint 1, main () at 12from1Ton.cc:16
string s="000";
//使用watch可以设置观察点,当变量或者表达式变化的时候运行停止
(gdb) watch s
Hardware watchpoint 2: s
(gdb) continue
Continuing.
Hardware watchpoint 2: s
Old value = &error reading variable: Cannot access memory at address 0x64&
New value = "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
s[index]=i+'0';
(gdb) print i
//在运行中修改变量
(gdb) set var i=3
(gdb) continue
Continuing.
Breakpoint 3, print (s="300", index=1) at 12from1Ton.cc: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
if(index==s.size()){
//从当前帧跳到任意一行
(gdb) jump 9
Continuing at 0x400c09.
Breakpoint 3, print (s="300", index=2) at 12from1Ton.cc:10
s[index]=i+'0';
//反编译,在查找崩溃问题时很有用
(gdb) disassemble print
Dump of assembler code for function print(std::string, int):
0x0bbd &+0&:
0x0bbe &+1&:
0x0bc1 &+4&:
0x0bc2 &+5&:
$0x38,%rsp
0x0bc6 &+9&:
%rdi,-0x38(%rbp)
0x0bca &+13&:
%esi,-0x3c(%rbp)
0x0bcd &+16&:
-0x3c(%rbp),%eax
0x0bd0 &+19&:
movslq %eax,%rbx
0x0bd3 &+22&:
-0x38(%rbp),%rax
0x0bd7 &+26&:
(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&:
0x00007ffff7b8d3e1 &+1&:
0x00007ffff7b8d3e2 &+2&:
=& 0x00007ffff7b8d3e5 &+5&:
0x00007ffff7b8d3e9 &+9&:
(%rdi),%rax
0x00007ffff7b8d3ec &+12&:
-0x8(%rax),%edx
0x00007ffff7b8d3ef &+15&:
0x00007ffff7b8d3f1 &+17&:
0x7ffff7b8d3ff &_ZNSsixEm+31&
0x00007ffff7b8d3f3 &+19&:
0x00007ffff7b8d3f6 &+22&:
0x7ffff7b2ecd0 &_ZNSs12_M_leak_hardEv@plt&
---Type &return& to continue, or q &return& to quit---
0x00007ffff7b8d3fb &+27&:
0x0(%rbp),%rax
0x00007ffff7b8d3ff &+31&:
0x00007ffff7b8d403 &+35&:
0x00007ffff7b8d406 &+38&:
0x00007ffff7b8d407 &+39&:
0x00007ffff7b8d408 &+40&:
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可以查看崩溃堆栈的信息。当然有时候会出现栈越界无法回溯,这里给一个解决方法,通过一个地址根据链表结构回溯,具体见:/p/?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= n&0;n--)
for(n=0;n&n++)
sum+=A[n];
printf("sum=%d\n",sum);
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) , 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== Invalid write of size 4
at 0x4005CB: main (testMem.cc:9)
Address 0x51fc1d0 is 0 bytes after a block of size 400 alloc'd
at 0x4C2AB80: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
by 0x4005A6: main (testMem.cc:6)
==12784== Conditional jump or move depends on uninitialised value(s)
at 0x4E8158E: vfprintf (vfprintf.c:1660)
by 0x4E8B498: printf (printf.c:33)
by 0x400616: main (testMem.cc:12)
//使用了未初始化的变量
==12784== Use of uninitialised value of size 8
at 0x4E80A4B: _itoa_word (_itoa.c:179)
by 0x4E846F6: vfprintf (vfprintf.c:1660)
by 0x4E8B498: printf (printf.c:33)
by 0x400616: main (testMem.cc:12)
==12784== Conditional jump or move depends on uninitialised value(s)
at 0x4E80A55: _itoa_word (_itoa.c:179)
by 0x4E846F6: vfprintf (vfprintf.c:1660)
by 0x4E8B498: printf (printf.c:33)
by 0x400616: main (testMem.cc:12)
==12784== Conditional jump or move depends on uninitialised value(s)
at 0x4E84742: vfprintf (vfprintf.c:1660)
by 0x4E8B498: printf (printf.c:33)
by 0x400616: main (testMem.cc:12)
==12784== Conditional jump or move depends on uninitialised value(s)
at 0x4E81659: vfprintf (vfprintf.c:1660)
by 0x4E8B498: printf (printf.c:33)
by 0x400616: main (testMem.cc:12)
==12784== Conditional jump or move depends on uninitialised value(s)
at 0x4E816DC: vfprintf (vfprintf.c:1660)
by 0x4E8B498: printf (printf.c:33)
by 0x400616: main (testMem.cc:12)
//堆的检测,未释放内存
==12784== HEAP SUMMARY:
in use at exit: 400 bytes in 1 blocks
total heap usage: 1 allocs, 0 frees, 400 bytes allocated
==12784== 400 bytes in 1 blocks are definitely lost in loss record 1 of 1
at 0x4C2AB80: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
by 0x4005A6: main (testMem.cc:6)
==12784== LEAK SUMMARY:
definitely lost: 400 bytes in 1 blocks
indirectly lost: 0 bytes in 0 blocks
possibly lost: 0 bytes in 0 blocks
still reachable: 0 bytes in 0 blocks
suppressed: 0 bytes in 0 blocks
==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。
  参考:下的hook方法:
  /pages//p29.html
  陈硕的插装单元测试的方法:
  http://blog.csdn.net/Solstice/article/details/6423342
11、动态链接和静态链接的区别
  参考《深入理解计算机系统》第七章,链接。
  静态链接以一组可重定位目标文件为输入,文件由各种不同的代码和数据节组成,通过符号解析和重定位生成一个完全链接的可以加载和运行的可执行文件。
  静态链接有一些明显的缺点,一是如果需要更新一个库,需要重新编译和链接库文件。二是对于一些标准的函数,如果将这些代码复制到每个程序运行的文本段中,会对存储器的资源造成很大的浪费。
  共享库就是为解决静态链接而生,共享库是一个目标模块。在运行时,可以加载到任意存储器地址,并和一个在存储器中的程序链接起来。这个过程称为动态链接。共享库在unix下通常使用.so后缀,window下为dll。
  共享库使用两种方式共享,一是一个库只有一个so文件。所有引用该库的执行程序共享这个文件的代码和数据。二是一个共享库的.text节的一个副本可以被不同的进程共享。
  注意在整个程序的链接过程中,链接器只是拷贝了一些重定位和符号信息。在程序加载(execve)时才会解析so文件中代码和数据的引用。
12、32位系统一个进程最多多少堆内存
  32位就是4G的寻址空间,linux将其分为两部分,虚拟地址从0xCxffffffff用于内核,为系统空间。较低的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");
printf("64 bit\n");
15、写一个c程序辨别系统是大端or小端字节序
int is_big_endian(void)
char c[4];
} bint = {0x};
return bint.c[0] == 1;
if ( htonl(47) == 47 ) {
// Big endian
// 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){
printf("thread1: i = %d\n",i);
pthread_exit(NULL);
void *thread2(){
int num = 0;
while(num&50){
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;
069d &thread1&:
48 83 ec 10
$0x10,%rsp
c7 45 fc 00 00 00 00
$0x0,-0x4(%rbp)
4006d4 &thread1+0x37&
8b 05 a0 09 20 00
0x2009a0(%rip),%eax
# 601054 &i&
89 05 97 09 20 00
%eax,0x200997(%rip)
# 601054 &i&
8b 05 91 09 20 00
0x200991(%rip),%eax
# 601054 &i&
bf 14 08 40 00
$0x400814,%edi
b8 00 00 00 00
e8 9c fe ff ff
400570 &printf@plt&
83 7d fc 31
$0x31,-0x4(%rbp)
4006ae &thread1+0x11&
bf 00 00 00 00
e8 bc fe ff ff
4005a0 &pthread_exit@plt&
072b &main&:
48 83 ec 10
$0x10,%rsp
b9 00 00 00 00
ba 9d 06 40 00
$0x40069d,%edx
be 00 00 00 00
bf 60 10 60 00
$0x601060,%edi
e8 14 fe ff ff
400560 &pthread_create@plt&
b9 00 00 00 00
ba e4 06 40 00
$0x4006e4,%edx
be 00 00 00 00
bf 68 10 60 00
$0x601068,%edi
e8 fb fd ff ff
400560 &pthread_create@plt&
8b 05 e9 08 20 00
0x2008e9(%rip),%eax
# 601054 &i&
bf 36 08 40 00
$0x400836,%edi
b8 00 00 00 00
e8 f4 fd ff ff
400570 &printf@plt&
c7 45 fc 00 00 00 00
$0x0,-0x4(%rbp)
83 45 fc 01
$0x1,-0x4(%rbp)
b8 00 00 00 00
注意对于全局的i++,在线程里的指令为下面三步,这里的三步都是可以被线程打断的:
0x200959(%rip),%eax
# 601054 &i&
%eax,0x200950(%rip)
# 601054 &i&
对于局部的test++,对应指令为:
$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/
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&
daemonize(const char *cmd)
i, fd0, fd1, fd2;
* Clear file creation mask.
* 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 */
* 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 */
* 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_ 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);
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)的全文介绍,希望对您学习和使用程序编程有所帮助.
这些内容可能对你也有帮助
更多可查看其它编程列表页。
猜您也会喜欢这些文章

我要回帖

更多关于 plt.rcparams什么意思 的文章

 

随机推荐