原创作品,转载请注明出处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. shell parameter expansitions

    type testtype -a test math calculate:echo $((1+2*3)) parameter expansition:bash-4 introduced feature ...

  2. 用javascript插入样式

    一.用javascript插入<style>样式 有时候我们需要利用js来动态生成页面上style标签中的css代码,方法很直接,就是直接创建一个style元素,然后设置style元素里面 ...

  3. MMORPG大型游戏设计与开发(服务器 AI 概述)

    游戏世界中我们拥有许多对象,常见的就是角色自身以及怪物和NPC,我们可以见到怪物和NPC拥有许多的行为,比如说怪物常常见到敌对的玩家就会攻击一样,又如一些NPC来游戏世界中走来走去,又有些怪物和NPC ...

  4. TJ2016 CTF Write up

    No zuo no die. Use markdown to write writeup in the future......

  5. Oracle基本数据类型

    一 字符串类型 字符串数据类型还可以依据存储空间分为固定长度类型(CHAR/NCHAR) 和可变长度类型(VARCHAR2/NVARCHAR2)两种. 所谓固定长度:是指虽然输入的字段值小于该字段的限 ...

  6. [No000054] Windows 下Python3.5, NoteBook增强版安装

    接着上周继续,没看的童鞋.请移步: http://www.cnblogs.com/Chary/p/No00004B.html 这里,假设你已经能够看到这个画面了: 接下来,我们继续 给药 : 安装no ...

  7. [bzoj1911][Apio2010特别行动队] (动态规划+斜率优化)

    Description Input Output Sample Input - - Sample Output HINT Solution 斜率优化动态规划 首先易得出这样的一个朴素状态转移方程 f[ ...

  8. Linux用户管理.md

    用户与组的概念 linux多用户,多任务的特性 Linux是一个真实的.完整的多用户多任务操作系统,多用户多任务就是可以在系统上建立多个用户,而多个用户可以在同一时间内登录同一个系统执行各自不同的任务 ...

  9. fMRI: spatial smoothing

    Source: Brain voyager support Theoretical Background Spatial smoothing means that data points are av ...

  10. bzoj1800[Ahoi2009]fly 飞行棋 暴力枚举

    找了道bzoj的水题,千年难得一遇. 建议初学者做做,然而我个蒟蒻初学时应该A不了..... < http://www.lydsy.com/JudgeOnline/problem.php?id= ...