现在的位置: 首页 > 综合 > 正文

我关于一个小程序所想到的

2013年10月09日 ⁄ 综合 ⁄ 共 6281字 ⁄ 字号 评论关闭

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

好了,关于另外两种情况大家自己可以试试!

抱歉!评论已关闭.