前几日,我再看书的时候,仔细的回顾了关于信号的这一章,当时,我想到了如果是在调用系统调用,这个时候给进程发发送一个信号,会产生什么效果,很多人都说了,就算是系统调用,他会很快的执行完成的,也是,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段可能是挨着的,也就是说,你的程序不是在一块连续的内存上的,这也符合了程序的局部性原理!
好了,关于另外两种情况大家自己可以试试!