前几日,我再看书的时候,仔细的回顾了关于信号的这一章,当时,我想到了如果是在调用系统调用,这个时候给进程发发送一个信号,会产生什么效果,很多人都说了,就算是系统调用,他会很快的执行完成的,也是,Linux信号有一种机制就是延时投递,比如这时,你的进程正在malloc分配内存,或者执行其他的原子操作,但是会很快的执行完成,这就会触发这种机制!
但是,如果我们是阻塞方式的socket套接字调用read,或者recv,recvfrom等,这会一直阻塞,难道非得等到有数据来到,我们才能给他信号?显然Linux内核人员避免了这种情况,还有,如果程序就是在sleep,成为了睡眠状态,这时候给他一个信号,会产生什么效果?
我是根据一个程序是在sleep的时候,接受到了一个信号想到了许多东西,下面看一下这个程序:
#include <stdio.h> #include <signal.h> void handler (int signum) { printf ("signal called\n"); } int main () { struct sigaction sigact; sigact.sa_handler = handler; //sigact.sa_flags = SA_RESTART; sigaction (SIGALRM, &sigact, NULL); alarm (2); sleep (50); printf ("done\n"); return 0; }
这个程序是很简单的,当你运行的时候就知道了结果是什么了,当然,我并没有满足这点内容,下面,我又用了strace -o log.txt ./a.out运行来查看这个程序运行了什么系统调用(内核层面),当你执行完后,目录下会多了一个log.txt文件,打开它,你会看到如下:
1 execve("./a.out", ["./a.out"], [/* 41 vars */]) = 0 2 brk(0) = 0x9ddd000 3 access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory) 4 mmap2(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb77ea000 5 access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory) 6 open("/etc/ld.so.cache", O_RDONLY) = 3 7 fstat64(3, {st_mode=S_IFREG|0644, st_size=55715, ...}) = 0 8 mmap2(NULL, 55715, PROT_READ, MAP_PRIVATE, 3, 0) = 0xb77dc000 9 close(3) = 0 10 access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory) 11 open("/lib/libc.so.6", O_RDONLY) = 3 12 read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0@n\1\0004\0\0\0"..., 512) = 512 13 fstat64(3, {st_mode=S_IFREG|0755, st_size=1421892, ...}) = 0 14 mmap2(NULL, 1427880, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7a6000 15 mmap2(0x8fd000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x157) = 0x8fd000 16 mmap2(0x900000, 10664, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x900000 17 close(3) = 0 18 mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb77db000 19 set_thread_area({entry_number:-1 -> 6, base_addr:0xb77db6c0, limit:1048575, seg_32bit:1, contents:0, read_exec_only:0, limit_in_pages:1, seg_not_present:0, useable:1}) = 0 20 mprotect(0x8fd000, 8192, PROT_READ) = 0 21 mprotect(0x8049000, 4096, PROT_READ) = 0 22 mprotect(0x24e000, 4096, PROT_READ) = 0 23 munmap(0xb77dc000, 55715) = 0 24 rt_sigaction(SIGALRM, {0x8048454, ~[HUP QUIT ILL TRAP ABRT BUS SEGV TERM STOP TSTP TTIN URG XCPU XFSZ VTALRM PROF WINCH IO PWR SYS RTMIN RT_1 RT_2 RT_4 RT_5 RT_10 RT_11 RT_12 RT_13 RT_14 RT_18 RT_19 RT_22 RT_23 RT_31], SA_STACK|SA_NOCLDSTOP|SA_NOCLDWAIT|0x484e8}, NULL, 8) = 0 25 alarm(2) = 0 26 rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0 27 rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0 28 rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0 29 nanosleep({50, 0}, {48, 195199}) = ? ERESTART_RESTARTBLOCK (To be restarted) 30 --- SIGALRM (Alarm clock) @ 0 (0) --- 31 fstat64(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 0), ...}) = 0 32 mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb77e9000 33 write(1, "signal called\n", 14) = 14 34 sigreturn() = ? (mask now []) 35 write(1, "done\n", 5) = 5 36 exit_group(0) = ?
对于我们来说,这个程序运行所调用的函数,会比你想的要多,其中,有很多的mmap2函数执行,我们再看第11行,这个是关于加载动态库的,也就是在关闭这个3文件描述符,都是在加载一些动态库,这些mmap2加载的地址勾起了我的兴趣,我们知道程序有什么堆栈,什么代码段,文本段,关于有什么段,《深入理解计算机系统》有详解,于是,我又修改了一下这个程序:
#include <stdio.h> #include <signal.h> #include <stdlib.h> int gi1 = 1; int gi2; void handler (int signum) { printf ("signal called\n"); printf ("handler function address is [%p]\n", handler); return; } int main () { int i1 = 1; int i2; int *mi = (int *) malloc (10); struct sigaction sigact; sigact.sa_handler = handler; //sigact.sa_flags = SA_RESTART; sigaction (SIGALRM, &sigact, NULL); alarm (2); sleep (50); printf ("done\n"); printf ("main function is [%p]\n", main); printf ("gi1 is [%p]\n", &gi1); printf ("gi2 is [%p]\n", &gi2); printf ("i1 is [%p]\n", &i1); printf ("i2 is [%p]\n", &i2); printf ("mi is [%p]\n", mi); free (mi); return 0; }
接着执行strace -o log2.txt ./a.out, 然后在打开看看这个文件:
1 execve("./a.out", ["./a.out"], [/* 41 vars */]) = 0 2 brk(0) = 0x9bae000 3 access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory) 4 mmap2(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7734000 5 access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory) 6 open("/etc/ld.so.cache", O_RDONLY) = 3 7 fstat64(3, {st_mode=S_IFREG|0644, st_size=55715, ...}) = 0 8 mmap2(NULL, 55715, PROT_READ, MAP_PRIVATE, 3, 0) = 0xb7726000 9 close(3) = 0 10 access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory) 11 open("/lib/libc.so.6", O_RDONLY) = 3 12 read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0@n\1\0004\0\0\0"..., 512) = 512 13 fstat64(3, {st_mode=S_IFREG|0755, st_size=1421892, ...}) = 0 14 mmap2(NULL, 1427880, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x426000 15 mmap2(0x57d000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x157) = 0x57d000 16 mmap2(0x580000, 10664, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x580000 17 close(3) = 0 18 mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7725000 19 set_thread_area({entry_number:-1 -> 6, base_addr:0xb77256c0, limit:1048575, seg_32bit:1, contents:0, read_exec_only:0, limit_in_pages:1, seg_not_present:0, useable:1}) = 0 20 mprotect(0x57d000, 8192, PROT_READ) = 0 21 mprotect(0x8049000, 4096, PROT_READ) = 0 22 mprotect(0xfeb000, 4096, PROT_READ) = 0 23 munmap(0xb7726000, 55715) = 0 24 brk(0) = 0x9bae000 25 brk(0x9bcf000) = 0x9bcf000 26 rt_sigaction(SIGALRM, {0x8048620, [QUIT ILL TRAP BUS FPE KILL STKFLT STOP WINCH RT_1], SA_SIGINFO|0x57f320}, NULL, 8) = 0 27 alarm(2) = 0 28 rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0 29 rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0 30 rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0 31 nanosleep({50, 0}, {48, 188320}) = ? ERESTART_RESTARTBLOCK (To be restarted) 32 --- SIGALRM (Alarm clock) @ 0 (0) --- 33 fstat64(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 0), ...}) = 0 34 mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7733000 35 write(1, "signal called\n", 14) = 14 36 write(1, "handler function address is [0x8"..., 40) = 40 37 rt_sigreturn(0xbfd456d4) = -1 EINTR (Interrupted system call) 38 write(1, "done\n", 5) = 5 39 write(1, "main function is [0x80484e0]\n", 29) = 29 40 write(1, "gi1 is [0x804a028]\n", 19) = 19 41 write(1, "gi2 is [0x804a034]\n", 19) = 19 42 write(1, "i1 is [0xbfd4579c]\n", 19) = 19 43 write(1, "i2 is [0xbfd45798]\n", 19) = 19 44 write(1, "mi is [0x9bae008]\n", 18) = 18 45 exit_group(0) = ?
前面的还是加载动态库,我们不用关心,当然,在第25行,程序执行了是brk函数,这个就是库向内核申请内存,但是内核给库函数最少就是一页(4096字节),如果用户申请少于这个,那就是有C 库分配给用户进程了!而在第35和36是程序信号输出,我们看到了一个地址开头就是8,我们就可已放心的认定,Linux的信号是执行在用户地址空间的栈上的,我们都知道,在IA-32体系下的计算机,linux内核虚拟地址空间是从0到0xC0000000,每个用户都有3G可用,而内存映射的地址是起始于0x40000000,这就说堆只有一个G可以用,继续增长就会到了mmap区域,然后Linux内核2.6.7版本开发的时候是将mmap地址上移动,这种解决方式就是《计算机程序设计艺术》中第一卷,第二章的顺序分配中的一个解决方法,程序划分各个段的目的,也就是说A
程序和B 程序的text段可能是挨着的,也就是说,你的程序不是在一块连续的内存上的,这也符合了程序的局部性原理!
好了,关于另外两种情况大家自己可以试试!