原创作品,转载请注明出处http://www.cnblogs.com/leo0000/p/5694416.html

  最近因为一个很有意思的段错误学习了一些新的东西。

  当时现象是这样的,程序正在运行,系统升级,此时某些so已经被该程序所使用,现在把这些so文件覆盖了,导致了该程序崩溃。

  调试dump文件可以发现是崩溃在了ld解析函数符号的时候,然后查看libc的源码,发现崩溃的函数checkmatch传入的参数是空指针,所以导致了崩溃。因为受到以前写裸机代码的影响,裸机是这样的,如果前2M stepstorm不够用,那么在stepstorm中的代码就把nandflash中的代码拷贝到内存中,然后跳转到内存中去运行,所以此时就算源文件再怎么被修改也不会受到nandflash中的内容影响。下面先讲两个需要用到的知识点。

  linux下很重要的一点是,一个文件可以被很多应用程序打开,同一时间的确只有一个应用程序可以对该文件读写,但是在不同时刻,所有应用程序对文件的操作都会影响到其他已打开该文件的应用程序,因为在每次读写前,系统调用read和write会对内存中的内容进行有效性判断。

  再讲一个有关mmap或者mmap2的事情,

  void *mmap(void *addr, size_t len, int prot, int flags, int fd, off_t offset);

  addr是要映射到的内存地址,返回值也是被映射到的内存地址,因为一般指定为0,有内核选择一段可用的内存空间。

  len表示要映射的内存大小。

  prot表示这段内存的访问权限。

  flags表示映射后内存的类型,主要是对该内存的写是否会影响到原文件。

  fd表示文件描述符,

  offset表示需要映射的文件内容相对文件头偏移量。

  映射完了之后,对这个内存的访问就是对文件的访问。

  

  下面看栗子:

  源码:

  共享库:

  

#include <stdio.h>

int fun1()
{
printf("fun1\n");
}

  main函数

int fun1();

int main()
{
while(){
sleep();
fun1();
}
}

  这个代码很简单,下面先用strace跟踪下test的运行:

strace ./test
execve("./test", ["./test"], [/* 22 vars */]) =
brk() = 0x9653000
access("/etc/ld.so.nohwcap", F_OK) = - ENOENT (No such file or directory)
mmap2(NULL, , PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -, ) = 0xb7731000
access("/etc/ld.so.preload", R_OK) = - ENOENT (No such file or directory)
open("tls/i686/sse2/cmov/lib1.so", O_RDONLY|O_CLOEXEC) = - ENOENT (No such file or directory)
open("tls/i686/sse2/lib1.so", O_RDONLY|O_CLOEXEC) = - ENOENT (No such file or directory)
open("tls/i686/cmov/lib1.so", O_RDONLY|O_CLOEXEC) = - ENOENT (No such file or directory)
open("tls/i686/lib1.so", O_RDONLY|O_CLOEXEC) = - ENOENT (No such file or directory)
open("tls/sse2/cmov/lib1.so", O_RDONLY|O_CLOEXEC) = - ENOENT (No such file or directory)
open("tls/sse2/lib1.so", O_RDONLY|O_CLOEXEC) = - ENOENT (No such file or directory)
open("tls/cmov/lib1.so", O_RDONLY|O_CLOEXEC) = - ENOENT (No such file or directory)
open("tls/lib1.so", O_RDONLY|O_CLOEXEC) = - ENOENT (No such file or directory)
open("i686/sse2/cmov/lib1.so", O_RDONLY|O_CLOEXEC) = - ENOENT (No such file or directory)
open("i686/sse2/lib1.so", O_RDONLY|O_CLOEXEC) = - ENOENT (No such file or directory)
open("i686/cmov/lib1.so", O_RDONLY|O_CLOEXEC) = - ENOENT (No such file or directory)
open("i686/lib1.so", O_RDONLY|O_CLOEXEC) = - ENOENT (No such file or directory)
open("sse2/cmov/lib1.so", O_RDONLY|O_CLOEXEC) = - ENOENT (No such file or directory)
open("sse2/lib1.so", O_RDONLY|O_CLOEXEC) = - ENOENT (No such file or directory)
open("cmov/lib1.so", O_RDONLY|O_CLOEXEC) = - ENOENT (No such file or directory)
open("lib1.so", O_RDONLY|O_CLOEXEC) =
read(, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\260\3\0\0004\0\0\0"..., ) =
fstat64(, {st_mode=S_IFREG|, st_size=, ...}) =
getcwd("/home/keda/caozhenhua/test/updateso", ) =
mmap2(NULL, , PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, , ) = 0xb772e000
mmap2(0xb772f000, , PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, , ) = 0xb772f000
close() =
open("tls/i686/sse2/cmov/libc.so.6", O_RDONLY|O_CLOEXEC) = - ENOENT (No such file or directory)
open("tls/i686/sse2/libc.so.6", O_RDONLY|O_CLOEXEC) = - ENOENT (No such file or directory)
open("tls/i686/cmov/libc.so.6", O_RDONLY|O_CLOEXEC) = - ENOENT (No such file or directory)
open("tls/i686/libc.so.6", O_RDONLY|O_CLOEXEC) = - ENOENT (No such file or directory)
open("tls/sse2/cmov/libc.so.6", O_RDONLY|O_CLOEXEC) = - ENOENT (No such file or directory)
open("tls/sse2/libc.so.6", O_RDONLY|O_CLOEXEC) = - ENOENT (No such file or directory)
open("tls/cmov/libc.so.6", O_RDONLY|O_CLOEXEC) = - ENOENT (No such file or directory)
open("tls/libc.so.6", O_RDONLY|O_CLOEXEC) = - ENOENT (No such file or directory)
open("i686/sse2/cmov/libc.so.6", O_RDONLY|O_CLOEXEC) = - ENOENT (No such file or directory)
open("i686/sse2/libc.so.6", O_RDONLY|O_CLOEXEC) = - ENOENT (No such file or directory)
open("i686/cmov/libc.so.6", O_RDONLY|O_CLOEXEC) = - ENOENT (No such file or directory)
open("i686/libc.so.6", O_RDONLY|O_CLOEXEC) = - ENOENT (No such file or directory)
open("sse2/cmov/libc.so.6", O_RDONLY|O_CLOEXEC) = - ENOENT (No such file or directory)
open("sse2/libc.so.6", O_RDONLY|O_CLOEXEC) = - ENOENT (No such file or directory)
open("cmov/libc.so.6", O_RDONLY|O_CLOEXEC) = - ENOENT (No such file or directory)
open("libc.so.6", O_RDONLY|O_CLOEXEC) = - ENOENT (No such file or directory)
open("/home/keda/caozhenhua/test/updateso/tls/i686/sse2/cmov/libc.so.6", O_RDONLY|O_CLOEXEC) = - ENOENT (No such file or directory)
stat64("/home/keda/caozhenhua/test/updateso/tls/i686/sse2/cmov", 0xbf9bd320) = - ENOENT (No such file or directory)
open("/home/keda/caozhenhua/test/updateso/tls/i686/sse2/libc.so.6", O_RDONLY|O_CLOEXEC) = - ENOENT (No such file or directory)
stat64("/home/keda/caozhenhua/test/updateso/tls/i686/sse2", 0xbf9bd320) = - ENOENT (No such file or directory)
open("/home/keda/caozhenhua/test/updateso/tls/i686/cmov/libc.so.6", O_RDONLY|O_CLOEXEC) = - ENOENT (No such file or directory)
stat64("/home/keda/caozhenhua/test/updateso/tls/i686/cmov", 0xbf9bd320) = - ENOENT (No such file or directory)
open("/home/keda/caozhenhua/test/updateso/tls/i686/libc.so.6", O_RDONLY|O_CLOEXEC) = - ENOENT (No such file or directory)
stat64("/home/keda/caozhenhua/test/updateso/tls/i686", 0xbf9bd320) = - ENOENT (No such file or directory)
open("/home/keda/caozhenhua/test/updateso/tls/sse2/cmov/libc.so.6", O_RDONLY|O_CLOEXEC) = - ENOENT (No such file or directory)
stat64("/home/keda/caozhenhua/test/updateso/tls/sse2/cmov", 0xbf9bd320) = - ENOENT (No such file or directory)
open("/home/keda/caozhenhua/test/updateso/tls/sse2/libc.so.6", O_RDONLY|O_CLOEXEC) = - ENOENT (No such file or directory)
stat64("/home/keda/caozhenhua/test/updateso/tls/sse2", 0xbf9bd320) = - ENOENT (No such file or directory)
open("/home/keda/caozhenhua/test/updateso/tls/cmov/libc.so.6", O_RDONLY|O_CLOEXEC) = - ENOENT (No such file or directory)
stat64("/home/keda/caozhenhua/test/updateso/tls/cmov", 0xbf9bd320) = - ENOENT (No such file or directory)
open("/home/keda/caozhenhua/test/updateso/tls/libc.so.6", O_RDONLY|O_CLOEXEC) = - ENOENT (No such file or directory)
stat64("/home/keda/caozhenhua/test/updateso/tls", 0xbf9bd320) = - ENOENT (No such file or directory)
open("/home/keda/caozhenhua/test/updateso/i686/sse2/cmov/libc.so.6", O_RDONLY|O_CLOEXEC) = - ENOENT (No such file or directory)
stat64("/home/keda/caozhenhua/test/updateso/i686/sse2/cmov", 0xbf9bd320) = - ENOENT (No such file or directory)
open("/home/keda/caozhenhua/test/updateso/i686/sse2/libc.so.6", O_RDONLY|O_CLOEXEC) = - ENOENT (No such file or directory)
stat64("/home/keda/caozhenhua/test/updateso/i686/sse2", 0xbf9bd320) = - ENOENT (No such file or directory)
open("/home/keda/caozhenhua/test/updateso/i686/cmov/libc.so.6", O_RDONLY|O_CLOEXEC) = - ENOENT (No such file or directory)
stat64("/home/keda/caozhenhua/test/updateso/i686/cmov", 0xbf9bd320) = - ENOENT (No such file or directory)
open("/home/keda/caozhenhua/test/updateso/i686/libc.so.6", O_RDONLY|O_CLOEXEC) = - ENOENT (No such file or directory)
stat64("/home/keda/caozhenhua/test/updateso/i686", 0xbf9bd320) = - ENOENT (No such file or directory)
open("/home/keda/caozhenhua/test/updateso/sse2/cmov/libc.so.6", O_RDONLY|O_CLOEXEC) = - ENOENT (No such file or directory)
stat64("/home/keda/caozhenhua/test/updateso/sse2/cmov", 0xbf9bd320) = - ENOENT (No such file or directory)
open("/home/keda/caozhenhua/test/updateso/sse2/libc.so.6", O_RDONLY|O_CLOEXEC) = - ENOENT (No such file or directory)
stat64("/home/keda/caozhenhua/test/updateso/sse2", 0xbf9bd320) = - ENOENT (No such file or directory)
open("/home/keda/caozhenhua/test/updateso/cmov/libc.so.6", O_RDONLY|O_CLOEXEC) = - ENOENT (No such file or directory)
stat64("/home/keda/caozhenhua/test/updateso/cmov", 0xbf9bd320) = - ENOENT (No such file or directory)
open("/home/keda/caozhenhua/test/updateso/libc.so.6", O_RDONLY|O_CLOEXEC) = - ENOENT (No such file or directory)
stat64("/home/keda/caozhenhua/test/updateso", {st_mode=S_IFDIR|, st_size=, ...}) =
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) =
fstat64(, {st_mode=S_IFREG|, st_size=, ...}) =
mmap2(NULL, , PROT_READ, MAP_PRIVATE, , ) = 0xb7719000
close() =
access("/etc/ld.so.nohwcap", F_OK) = - ENOENT (No such file or directory)
open("/lib/i386-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) =
read(, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0000\226\1\0004\0\0\0"..., ) =
fstat64(, {st_mode=S_IFREG|, st_size=, ...}) =
mmap2(NULL, , PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, , ) = 0xb7574000
mmap2(0xb7713000, , PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, , 0x19f) = 0xb7713000
mmap2(0xb7716000, , PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -, ) = 0xb7716000
close() =
mmap2(NULL, , PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -, ) = 0xb7573000
mmap2(NULL, , PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -, ) = 0xb7572000
set_thread_area({entry_number:- -> , base_addr:0xb75726c0, limit:, seg_32bit:, contents:, read_exec_only:, limit_in_pages:, seg_not_present:, useable:}) =
mprotect(0xb7713000, , PROT_READ) =
mprotect(0xb772e000, , PROT_READ|PROT_WRITE) =
mprotect(0xb772e000, , PROT_READ|PROT_EXEC) =
mprotect(0xb772f000, , PROT_READ) =
mprotect(0x8049000, , PROT_READ) =
mprotect(0xb7754000, , PROT_READ) =
munmap(0xb7719000, ) =
rt_sigprocmask(SIG_BLOCK, [CHLD], [], ) =
rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], }, ) =
rt_sigprocmask(SIG_SETMASK, [], NULL, ) =
nanosleep({, }, 0xbf9bd958) =
fstat64(, {st_mode=S_IFCHR|, st_rdev=makedev(, ), ...}) =
mmap2(NULL, , PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -, ) = 0xb772d000
write(, "fun1\n", 5fun1
) =
rt_sigprocmask(SIG_BLOCK, [CHLD], [], ) =
rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], }, ) =
rt_sigprocmask(SIG_SETMASK, [], NULL, ) =
nanosleep({, },

  可以看到每打开一个共享库,linux利用的是mmap2,而不是像裸机一样的read和write。

  所以这样的话,比如需要跳转到共享库中的某个函数,如果是第一次的话,那么需要从文件中把内容加载到内存,然后再运行。

  那么如果在程序运行时,出现缺页,那么就需要从内存中重新读取该文件的该段内容,而如果该文件被修改了而且该段是第一次被访问,那么读取到的内容将会导致不可预知的错误。

  

  接下来再对mmap实验。

  源码:

  

#include <sys/mman.h>
#include <fcntl.h> int main()
{
int fd;
int i = ;
char *buf;
fd = open("./libvsipstack.a",O_RDONLY);
buf = mmap(,,PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE,fd,);
while()
{
i++;
sleep();
printf("%c\n",buf[i]);
}
}  

  mmap的映射方式和加载库的方式一致,

  然后在程序运行时我用vi修改了libvsipstack.a,修改之后可以看到libvsipstack.a是有一个备份文件的。此时是可以保证数据是正确的。

-rwxrwxrwx  root root  -- : \
-rwxrwxrwx root root -- : a.out
-rwxrwxrwx root root -- : hello.c
-rwxrwxrwx root root -- : libvsipstack.a
-????????? ? ? ? ? ? libvsipstack.a~
-rwxrwxrwx root root -- : test.c

  而当我们在上面的实验中,用libvsipstack.a覆盖lib1.so,并没有导致lib1.so有一个备份,而是变成了libvsipstack.a一样的文件。所以下一次读取将会和源文件不一致。

-rwxrwxrwx  root root       -- : core
-rwxrwxrwx root root -- : lib1.c
-rwxrwxrwx root root -- : lib1.so
-rwxrwxrwx root root -- : lib2.so
-rwxrwxrwx root root -- : libvsipstack.a
-rwxrwxrwx root root -- : test
-rwxrwxrwx root root -- : test.c

  需要知道当源文件更新时,使用mmap到的内存,系统会去重新读取文件中的内容。

  所以当库被更新时,同时会更新内存中的内容。

  总结一下,

  是否崩在ld中,是由是否是第一次调用该函数决定的,因为只有第一次调用才会需要去解析plt表中的内容。

  首先内存的布局是根据elf文件头来部署的。所以替换前后的文件不是同一个那么就会导致非法指令之类的错误,根本无法确定错误来源。

  但如果替换前后库是一致的,那么差别就在于,重定向表的内容变的无效了。

  重定向表有两种一种是在程序启动时就完成符号解析的,看栗子

Dump of assembler code for function fun1:
0xb7fd846c <+>: push %ebp
0xb7fd846d <+>: e5 mov %esp,%ebp
0xb7fd846f <+>: ec sub $0x18,%esp
0xb7fd8472 <+>: c7 d4 movl $0x4d4,(%esp)
0xb7fd8479 <+>: e8 fc ff ff ff call 0xb7fd847a <fun1+>
0xb7fd847e <+>: c9 leave
0xb7fd847f <+>: c3 ret

  本来+13位置处的代码已经完成了重定向,但是重新读取之后呢,恢复了重定向之前的情况,结果崩溃。

  另一种是懒惰式的加载,是先跳到plt表的情况,看栗子

   0xb7fd845c <+>:     push   %ebp
0xb7fd845d <+>: mov %esp,%ebp
0xb7fd845f <+>: push %ebx
0xb7fd8460 <+>: sub $0x14,%esp
0xb7fd8463 <+>: call 0xb7fd8457 <__i686.get_pc_thunk.bx>
0xb7fd8468 <+>: add $0x1b8c,%ebx
0xb7fd846e <+>: lea -0x1b12(%ebx),%eax
0xb7fd8474 <+>: mov %eax,(%esp)
0xb7fd8477 <+>: call 0xb7fd8380 <puts@plt>
0xb7fd847c <+>: add $0x14,%esp
0xb7fd847f <+>: pop %ebx
0xb7fd8480 <+>: pop %ebp
0xb7fd8481 <+>: ret
End of assembler dump.
(gdb) disassemble 0xb7fd8380
Dump of assembler code for function puts@plt:
0xb7fd8380 <+>: jmp *0x10(%ebx)
0xb7fd8386 <+>: push $0x8
0xb7fd838b <+>: jmp 0xb7fd8360
End of assembler dump.
(gdb) i r
eax 0xb7fd84e2 -
ecx 0xbffff6a8 -
edx 0xb7fbeff4 -
ebx 0xb7fd9ff4 -
esp 0xbffff6ac 0xbffff6ac
ebp 0xbffff6c8 0xbffff6c8
esi 0x0
edi 0x0
eip 0x386 0x386
eflags 0x210292 [ AF SF IF RF ID ]
cs 0x73
ss 0x7b
ds 0x7b
es 0x7b
fs 0x0
gs 0x33
(gdb) disassemble 0x00000386
No function contains specified address.
(gdb) x/x 0xb7fd9ff4+0x10
0xb7fda004: 0x00000386

  可以看到先跳到了plt表,因为是重新读入,相当于还没有重定位,所以跳转到下一句话,可以反汇编看到0x386就是原反汇编没有偏移地址的地址大小。

 <puts@plt>:
: ff a3 jmp *0x10(%ebx)
: push $0x8
38b: e9 d0 ff ff ff jmp <_init+0x3c>

  所以这里面的情况相当于基地址被初始为了0,导致崩溃。

  因为mmap被调用时指明的是private,所以内存的修改不会影响到文件的内容,但是文件的修改会影响到内存的内容,所以导致了崩溃的产生。

  说这个是系统的bug也可以,说是自己的使用不当也可以。

  还有一个是权限问题,除了拥有root权限的用户,是不可以这样随意覆盖文件的。

linux下cp覆盖原so文件时引起的段错误原因确定的更多相关文章

  1. linux下使用gcc/g++编译代码时gets函数有错误

    今天在linux中使用个g++编译一个名为myfirst.cpp的代码的时候,出现如下错误 myfirst.cpp: In function ‘int main()’:myfirst.cpp:11:2 ...

  2. 《Linux下cp XXX1 XXX2的功能》的实现

    <Linux下cp XXX1 XXX2的功能>的实现 一.题目要求 编写MyCP.java 实现类似Linux下cp XXX1 XXX2的功能,要求MyCP支持两个参数: java MyC ...

  3. 编写MyCP.java 实现类似Linux下cp XXX1 XXX2的功能

    题目:编写MyCP.java 实现类似Linux下cp XXX1 XXX2的功能 要求:MyCP支持两个参数: java MyCP -tx XXX1.txt XXX2.bin 用来把文本文件(内容为十 ...

  4. 20175303 Mycp实现Linux下cp xxx1 xxx2的功能

    20175303 Mycp实现Linux下cp xxx1 xxx2的功能 一.题目要求 编写MyCP2.java 实现类似Linux下cp XXX1 XXX2的功能,要求MyCP2支持两个参数: ja ...

  5. 学号20175313 《实现Linux下cp XXX1 XXX2的功能(二)》第九周

    目录 MyCP2 一.题目要求 二.题目理解 三.需求分析 四.设计思路 五.伪代码分析 六.代码链接 七.代码实现过程中遇到的问题 八.运行结果截图 九.心得体会 十.参考资料 MyCP2 一.题目 ...

  6. 学号20175313 《实现Linux下cp XXX1 XXX2的功能(一)》第九周

    目录 MyCP 一.题目要求 二.题目理解 三.需求分析 四.设计思路 五.伪代码分析 六.代码链接 七.代码实现过程中遇到的问题 八.运行结果截图 九.参考资料 MyCP 一.题目要求 编写MyCP ...

  7. 编程实现类似Linux下cp功能

    MyCP的代码实现 一.题目要求: 编写MyCP.java 实现类似Linux下cp XXX1 XXX2的功能,要求MyCP支持两个参数: java MyCP -tx XXX1.txt XXX2.bi ...

  8. linux下shell脚本执行jar文件

    最近在搞一个shell脚本启动jar文件个关闭jar文件的东东.搞得我都蛋疼了.今天晚上终于弄好了 话说,小弟的linux只是刚入门,经过各方查资料终于搞定了.话不多说,下面开始上小弟写的shell脚 ...

  9. linux 下C语言编程库文件处理与Makefile编写

    做开发快3年了,在linux下编译安装软件算是家常便饭了.就拿gcc来说,都有不下10次了,可基本每次都会碰到些奇奇怪怪的问题.看来还是像vs.codeblocks这样的ide把人弄蠢了.便下定决心一 ...

随机推荐

  1. JSCH通过密钥文件进行远程访问

    需求:WEB app 需要使用JSCH来通过密钥文件的方式进行SFTP/SSH访问远程LINUX机器 实现方式:假设远程机器都含有用户名为hadoop的用户,因为密码因为策略的要求密码会随时间发生变化 ...

  2. documentbodyscrollTop的值总为零的解决办法

    有一个功能需要判断返回顶部按钮是否显示. JS代码如下: var sTop = document.body.scrollTop; if(sTop>100){ document.getElemen ...

  3. 怎样用ZBrush对模型进行渲染

    关于如何使用ZBrush®3D图形绘制软件雕刻僵尸模型,Fisker老师用了6个章节共41课时,从人体躯干和骨骼雕刻,到衣服.鞋子制作,再到顶点着色,向大家一一展示了雕刻过程,其中分享了很多ZBrus ...

  4. 第14章 集合框架(1)-List集合的各种类

    1.概述 1.1.Java集合框架的由来 1.2.什么是集合框架? 1.3.为什么需要集合框架 1.4.常用的框架接口规范 2.Vector类 2.1.存储原理 2.2.构造方法 2.3.常用方法 3 ...

  5. bzoj[1087][SCOI2005]互不侵犯King

    Description 在N×N的棋盘里面放K个国王,使他们互不攻击,共有多少种摆放方案.国王能攻击到它上下左右,以及左上左下右上右下八个方向上附近的各一个格子,共8个格子. Input 只有一行,包 ...

  6. 配置Jenkins使用Gitlab的代码库进行构建

    1. 首先确认Jenkins上安装了Git plugin, 以及Subversion plugin Manage Jenkins -> Plugin Manager -> Availabl ...

  7. 代码覆盖工具(gcov、lcov)的使用

    一.安装 gcov:是随gcc一起发布的,并不需要独立安装:lcov:其他博客说是随ltp发布的,结果下载下ltp之后编译了10多分钟,最后也没见lcov,最后到sourceforge下载了lcov单 ...

  8. rpc框架之gRPC 学习 - hello world

    grpc是google在github于2015年开源的一款RPC框架,虽然protobuf很早google就开源了,但是google一直没推出正式的开源框架,导致github上基于protobuf的r ...

  9. js下关于onmouseout、事件冒泡的问题经验小结

    问题是这样的:一个div元素要触发onmouseout事件,同时这个div内部还有子元素,于是当鼠标移动到该div的子元素上时,onmouseout事件也被触发了.在要做浮动层效果的时候会经常遇到这个 ...

  10. yii获取当前url和域名

    <?php //当前域名 echo Yii::app()->request->hostInfo; //除域名外的URL echo Yii::app()->request-> ...