http://blog.chinaunix.net/uid-72446-id-2060538.html

对于可执行文件来说,段的加载位置是固定的,程序段表中如实反映了段的加载地址.对于共享库来?段的加载位置是浮动的,位置无关的,程序段表反映的是以0作为基准地址的相对加载地址.尽管共享库的连接是不充分的,为了便于测试动态链接器,Linux允许直接加载共享库运行.如果应用程序具有动态链接器的描述段,内核在完成程序段加载后,紧接着加载动态链接器,并且启动动态链接器的

ELF的可执行文件与共享库在结构上非常类似,它们具有一张程序段表,用来描述这些段如何映射到?br>?炭占?
对于可执行文件来说,段的加载位置是固定的,程序段表中如实反映了段的加载地址.对于共享库来?br>?段的加载位置是浮动的,位置无关的,程序段表反映的是以0作为基准地址的相对加载地址.尽管共享库的连接是不充分的,为了便于测试动态链接器,Linux允许直接加载共享库运行.如果应用程序具有动态链接器的描述段,内核在完成程序段加载后,紧接着加载动态链接器,并且启动动态链接器的?br>肟?

typedef struct elf32_hdr{
unsigned char e_ident[EI_NIDENT];
Elf32_Half e_type; /* ET_EXEC ET_DYN 等 */
Elf32_Half e_machine;
Elf32_Word e_version;
Elf32_Addr e_entry; /* Entry point */
Elf32_Off e_phoff; 程序段描述表的位置
Elf32_Off e_shoff; 一般段描述表的位置
Elf32_Word e_flags;
Elf32_Half e_ehsize; ELF头的大小
Elf32_Half e_phentsize; 程序段描述表单元的大小
Elf32_Half e_phnum; 程序段描述表单元的个数
Elf32_Half e_shentsize; 一般段描述表的单元大小
Elf32_Half e_shnum; 一般段描述表单元的个数
Elf32_Half e_shstrndx;
} Elf32_Ehdr;

typedef struct elf32_phdr{
Elf32_Word p_type; PT_INTERP,PT_LOAD,PT_DYNAMIC等
Elf32_Off p_offset; 该程序段在ELF文件中的位置
Elf32_Addr p_vaddr; 该程序段被映射到进程的虚拟地址
Elf32_Addr p_paddr; 
Elf32_Word p_filesz; 该程序段的文件尺寸
Elf32_Word p_memsz; 该程序段的内存尺寸
Elf32_Word p_flags; 该程序段的映射属性
Elf32_Word p_align; 
} Elf32_Phdr;

struct linux_binprm{
char buf[BINPRM_BUF_SIZE]; 预先读入的ELF文件头
struct page *page[MAX_ARG_PAGES];
unsigned long p; /* current top of mem */
int sh_bang;
struct file * file;
int e_uid, e_gid;
kernel_cap_t cap_inheritable, cap_permitted, cap_effective;
int argc, envc;
char * filename; /* Name of binary */
unsigned long loader, exec;
};
static int load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs)
{
struct file *interpreter = NULL; /* to shut gcc up */
unsigned long load_addr = 0, load_bias;
int load_addr_set = 0;
char * elf_interpreter = NULL;
unsigned int interpreter_type = INTERPRETER_NONE;
unsigned char ibcs2_interpreter = 0;
mm_segment_t old_fs;
unsigned long error;
struct elf_phdr * elf_ppnt, *elf_phdata;
unsigned long elf_bss, k, elf_brk;
int elf_exec_fileno;
int retval, size, i;
unsigned long elf_entry, interp_load_addr = 0;
unsigned long start_code, end_code, start_data, end_data;
struct elfhdr elf_ex;
struct elfhdr interp_elf_ex;
struct exec interp_ex;
char passed_fileno[6];

/* Get the exec-header */
elf_ex = *((struct elfhdr *) bprm->buf);

retval = -ENOEXEC;
/* First of all, some simple consistency checks */
if (memcmp(elf_ex.e_ident, ELFMAG, SELFMAG) != 0)
goto out; 文件头标记是否匹配

if (elf_ex.e_type != ET_EXEC && elf_ex.e_type != ET_DYN)
goto out; 文件类型是否为可执行文件或共享库
if (!elf_check_arch(&elf_ex))
goto out; 
if (!bprm->file->f_op||!bprm->file->f_op->mmap)
goto out; 所在的文件系统是否具有文件映射功能

/* Now read in all of the header information */

retval = -ENOMEM;
size = elf_ex.e_phentsize * elf_ex.e_phnum; 求程序段表总长度
if (size > 65536)
goto out;
elf_phdata = (struct elf_phdr *) kmalloc(size, GFP_KERNEL); 分配程序段表空间
if (!elf_phdata)
goto out;

; 读入程序段表
retval = kernel_read(bprm->file, elf_ex.e_phoff, (char *) elf_phdata, size);
if (retval < 0) 
goto out_free_ph;

retval = get_unused_fd(); 取可用进程文件表的自由槽位
if (retval < 0)
goto out_free_ph;
get_file(bprm->file);
fd_install(elf_exec_fileno = retval, bprm->file); 将打开的文件安装到进程文件表

elf_ppnt = elf_phdata; 指向程序段表
elf_bss = 0; bss段的起始地址
elf_brk = 0; bss段的终止地址

start_code = ~0UL; 代码段的开始
end_code = 0; 代码段的终止
start_data = 0; 数据段的开始
end_data = 0; 数据段的终止
; 扫描ELF程序段表,搜寻动态链接器定义
for (i = 0; i < elf_ex.e_phnum; i++) {
if (elf_ppnt->p_type == PT_INTERP) {
retval = -EINVAL;
if (elf_interpreter)
goto out_free_dentry; 如果包含多个动态链接器描述项

/* This is the program interpreter used for
* shared libraries - for now assume that this
* is an a.out format binary
*/

retval = -ENOMEM; 为动态链接器名称字符串分配空间
elf_interpreter = (char *) kmalloc(elf_ppnt->p_filesz,
GFP_KERNEL);
if (!elf_interpreter)
goto out_free_file;
; 将动态链接器的文件名读入内存
retval = kernel_read(bprm->file, elf_ppnt->p_offset,
elf_interpreter,
elf_ppnt->p_filesz);
if (retval < 0)
goto out_free_interp;
/* If the program interpreter is one of these two,
* then assume an iBCS2 image. Otherwise assume
* a native linux image.
*/
if (strcmp(elf_interpreter,"/usr/lib/libc.so.1") == 0 ||
strcmp(elf_interpreter,"/usr/lib/ld.so.1") == 0)
ibcs2_interpreter = 1; 说明应用程序是IBCS2仿真代码

interpreter = open_exec(elf_interpreter); 打开动态链接器文件
retval = PTR_ERR(interpreter);
if (IS_ERR(interpreter))
goto out_free_interp;
retval = kernel_read(interpreter, 0, bprm->buf, BINPRM_BUF_SIZE);
; 读入动态链接器文件头
if (retval < 0) 
goto out_free_dentry;

/* Get the exec headers */
interp_ex = *((struct exec *) bprm->buf); 假定为aout格式的文件头结构
interp_elf_ex = *((struct elfhdr *) bprm->buf); 假定为ELF文件头结构
}
elf_ppnt++; 下一片段目录项
}

/* Some simple consistency checks for the interpreter */
if (elf_interpreter) {
; 如果定义了动态链接器,分析其格式类型
interpreter_type = INTERPRETER_ELF | INTERPRETER_AOUT;

/* Now figure out which format our binary is */
if ((N_MAGIC(interp_ex) != OMAGIC) &&
(N_MAGIC(interp_ex) != ZMAGIC) &&
(N_MAGIC(interp_ex) != QMAGIC))
interpreter_type = INTERPRETER_ELF; 如果不是AOUT标识

if (memcmp(interp_elf_ex.e_ident, ELFMAG, SELFMAG) != 0)
interpreter_type &= ~INTERPRETER_ELF; 如果没有ELF标识

retval = -ELIBBAD;
if (!interpreter_type) 不能识别动态链接器类型
goto out_free_dentry;

/* Make sure only one type was selected */
if ((interpreter_type & INTERPRETER_ELF) &&
interpreter_type != INTERPRETER_ELF) {
printk(KERN_WARNING "ELF: Ambiguous type, using ELF ");
interpreter_type = INTERPRETER_ELF;
}
}

/* OK, we are done with that, now set up the arg stuff,
and then start this sucker up */

if (!bprm->sh_bang) {
char * passed_p;

if (interpreter_type == INTERPRETER_AOUT) {
sprintf(passed_fileno, "%d", elf_exec_fileno);
passed_p = passed_fileno;

if (elf_interpreter) {
retval = copy_strings_kernel(1,&passed_p,bprm);
; 将程序的文件描述符压入参数堆栈,准备传递给aout格式的动态链接器
if (retval)
goto out_free_dentry; 
bprm->argc++; bprm->page[]中参数的数目
}
}
}

/* Flush all traces of the currently running executable */
retval = flush_old_exec(bprm);
if (retval)
goto out_free_dentry;

/* OK, This is the point of no return */
current->mm->start_data = 0;
current->mm->end_data = 0;
current->mm->end_code = 0;
current->mm->mmap = NULL;
current->flags &= ~PF_FORKNOEXEC;
elf_entry = (unsigned long) elf_ex.e_entry; 应用程序的入口地址

/* Do this immediately, since STACK_TOP as used in setup_arg_pages
may depend on the personality. */
SET_PERSONALITY(elf_ex, ibcs2_interpreter);
; 如果是ibcs2_interpreter非0,置PER_SVR4代码个性,否则置PER_LINUX代码个性

/* Do this so that we can load the interpreter, if need be. We will
change some of these later */
current->mm->rss = 0;
setup_arg_pages(bprm); /* XXX: check error */
; 建立描述堆栈参数页的初始虚存范围结构
current->mm->start_stack = bprm->p;

/* Try and get dynamic programs out of the way of the default mmap
base, as well as whatever program they might try to exec. This
is because the brk will follow the loader, and is not movable. */

load_bias = ELF_PAGESTART(elf_ex.e_type==ET_DYN ? ELF_ET_DYN_BASE : 0);
; 如果需要加载的是共享库,则设置共享库的加载偏置为ELF_ET_DYN_BASE
/* Now we do a little grungy work by mmaping the ELF image into
the correct location in memory. At this point, we assume that
the image should be loaded at fixed address, not at a variable
address. */

old_fs = get_fs();
set_fs(get_ds());
; 再次扫描程序段描述表,映射其中的程序段到进程空间
for(i = 0, elf_ppnt = elf_phdata; i < elf_ex.e_phnum; i++, elf_ppnt++) {
int elf_prot = 0, elf_flags;
unsigned long vaddr;

if (elf_ppnt->p_type != PT_LOAD) 
continue; 如果程序段不可加载

if (elf_ppnt->p_flags & PF_R) elf_prot |= PROT_READ;
if (elf_ppnt->p_flags & PF_W) elf_prot |= PROT_WRITE;
if (elf_ppnt->p_flags & PF_X) elf_prot |= PROT_EXEC;

elf_flags = MAP_PRIVATE|MAP_DENYWRITE|MAP_EXECUTABLE;
; 根据程序段描述设置相应的mmap参数

vaddr = elf_ppnt->p_vaddr; 段起始地址
if (elf_ex.e_type == ET_EXEC || load_addr_set) {
; 对于可执行程序,使用固定映射
elf_flags |= MAP_FIXED;
}

error = elf_map(bprm->file, load_bias + vaddr, elf_ppnt, elf_prot, elf_flags);
; 将该程序段[eppnt->p_offset,eppnt->p_filesz]映射到虚存(load_bias+vaddr)开始的区域

if (!load_addr_set) {
load_addr_set = 1;
load_addr = (elf_ppnt->p_vaddr - elf_ppnt->p_offset);
; 求出该ELF文件在用户虚存中的起始地址
if (elf_ex.e_type == ET_DYN) {
load_bias += error -  
ELF_PAGESTART(load_bias + vaddr);
load_addr += error;
}
}

k = elf_ppnt->p_vaddr;
if (k < start_code) start_code = k; 取最小的段地址作为代码段起始
if (start_data < k) start_data = k; 取最大的段地址作为数据段起始

k = elf_ppnt->p_vaddr + elf_ppnt->p_filesz; 这时k指向文件段尾

if (k > elf_bss)
elf_bss = k; 取最大文件段尾作为BSS段起始
if ((elf_ppnt->p_flags & PF_X) && end_code < k)
end_code = k; 取最大可执行的文件段尾作为可执行段的终止
if (end_data < k)
end_data = k; 取最大的文件段尾作为数据段的终止
k = elf_ppnt->p_vaddr + elf_ppnt->p_memsz; 这时k指向内存段尾
if (k > elf_brk)
elf_brk = k; 取最大的内存段尾作为BSS段的终止
}
set_fs(old_fs);
elf_entry += load_bias;
elf_bss += load_bias;
elf_brk += load_bias;
start_code += load_bias;
end_code += load_bias;
start_data += load_bias;
end_data += load_bias;

if (elf_interpreter) {
if (interpreter_type == INTERPRETER_AOUT)
elf_entry = load_aout_interp(&interp_ex,
interpreter);
else
elf_entry = load_elf_interp(&interp_elf_ex, 动态链接器的文件头
interpreter, 动态链接器打开的文件结构
&interp_load_addr); 输出链接器的加载地址
; 可执行程序的入口变为动态链接器的入口
allow_write_access(interpreter);
fput(interpreter);
kfree(elf_interpreter);

if (elf_entry == ~0UL) {
printk(KERN_ERR "Unable to load interpreter ");
kfree(elf_phdata);
send_sig(SIGSEGV, current, 0);
return 0;
}
}

kfree(elf_phdata); 释放程序段表

if (interpreter_type != INTERPRETER_AOUT)
sys_close(elf_exec_fileno);

set_binfmt(&elf_format); 增加ELF内核模块的引用计数

compute_creds(bprm);
current->flags &= ~PF_FORKNOEXEC;
bprm->p = (unsigned long) 建立入口函数参数表
create_elf_tables((char *)bprm->p,
bprm->argc,
bprm->envc,
(interpreter_type == INTERPRETER_ELF ? &elf_ex : NULL),
load_addr, load_bias,
interp_load_addr,
(interpreter_type == INTERPRETER_AOUT ? 0 : 1));
/* N.B. passed_fileno might not be initialized? */
if (interpreter_type == INTERPRETER_AOUT)
current->mm->arg_start += strlen(passed_fileno) + 1;
current->mm->start_brk = current->mm->brk = elf_brk;
current->mm->end_code = end_code;
current->mm->start_code = start_code;
current->mm->start_data = start_data;
current->mm->end_data = end_data;
current->mm->start_stack = bprm->p;

/* Calling set_brk effectively mmaps the pages that we need
* for the bss and break sections
*/
set_brk(elf_bss, elf_brk); 建立bss的虚存映射,elf_bss是bss的开始,elf_brk是bss的结束

padzero(elf_bss); 如果bss不起始于页连界上,说明与data段有重叠,则将该页bss区域清0

#if 0
printk("(start_brk) %lx " , (long) current->mm->start_brk);
printk("(end_code) %lx " , (long) current->mm->end_code);
printk("(start_code) %lx " , (long) current->mm->start_code);
printk("(start_data) %lx " , (long) current->mm->start_data);
printk("(end_data) %lx " , (long) current->mm->end_data);
printk("(start_stack) %lx " , (long) current->mm->start_stack);
printk("(brk) %lx " , (long) current->mm->brk);
#endif

if ( current->personality == PER_SVR4 )
{
/* Why this, you ask??? Well SVr4 maps page 0 as read-only,
and some applications "depend" upon this behavior.
Since we do not have the power to recompile these, we
emulate the SVr4 behavior. Sigh. */
/* N.B. Shouldn't the size here be PAGE_SIZE?? */
down(¤t->mm->mmap_sem);
error = do_mmap(NULL, 0, 4096, PROT_READ | PROT_EXEC,
MAP_FIXED | MAP_PRIVATE, 0);
up(¤t->mm->mmap_sem);
}

#ifdef ELF_PLAT_INIT
/*
* The ABI may specify that certain registers be set up in special
* ways (on i386 %edx is the address of a T_FINI function, for
* example. This macro performs whatever initialization to
* the regs structure is required.
*/
ELF_PLAT_INIT(regs);
#endif

start_thread(regs, elf_entry, bprm->p); 将返回的eip设为elf_entry,esp设为bprm->p
if (current->ptrace & PT_PTRACED)
send_sig(SIGTRAP, current, 0); 如果进程处于跟踪状态,则生成SIGTRAP信号
retval = 0;
out:
return retval;

/* error cleanup */
out_free_dentry:
allow_write_access(interpreter);
fput(interpreter);
out_free_interp:
if (elf_interpreter)
kfree(elf_interpreter);
out_free_file:
sys_close(elf_exec_fileno);
out_free_ph:
kfree(elf_phdata);
goto out;
}
static unsigned long load_elf_interp(struct elfhdr * interp_elf_ex,
struct file * interpreter,
unsigned long *interp_load_addr)
{
struct elf_phdr *elf_phdata;
struct elf_phdr *eppnt;
unsigned long load_addr = 0;
int load_addr_set = 0;
unsigned long last_bss = 0, elf_bss = 0;
unsigned long error = ~0UL;
int retval, i, size;

/* First of all, some simple consistency checks */
if (interp_elf_ex->e_type != ET_EXEC &&
interp_elf_ex->e_type != ET_DYN)
goto out;
if (!elf_check_arch(interp_elf_ex))
goto out;
if (!interpreter->f_op || !interpreter->f_op->mmap)
goto out;

/*
* If the size of this structure has changed, then punt, since
* we will be doing the wrong thing.
*/
if (interp_elf_ex->e_phentsize != sizeof(struct elf_phdr))
goto out;

/* Now read in all of the header information */

size = sizeof(struct elf_phdr) * interp_elf_ex->e_phnum;
if (size > ELF_MIN_ALIGN)
goto out; 如果动态链接器的程序段表的尺寸大于1个页面
elf_phdata = (struct elf_phdr *) kmalloc(size, GFP_KERNEL);
if (!elf_phdata)
goto out;

retval = kernel_read(interpreter,interp_elf_ex->e_phoff,(char *)elf_phdata,size);
error = retval;
if (retval < 0)
goto out_close;

eppnt = elf_phdata;
for (i=0; ie_phnum; i++, eppnt++) {
if (eppnt->p_type == PT_LOAD) {
int elf_type = MAP_PRIVATE | MAP_DENYWRITE;
int elf_prot = 0;
unsigned long vaddr = 0;
unsigned long k, map_addr;

if (eppnt->p_flags & PF_R) elf_prot = PROT_READ;
if (eppnt->p_flags & PF_W) elf_prot |= PROT_WRITE;
if (eppnt->p_flags & PF_X) elf_prot |= PROT_EXEC;
vaddr = eppnt->p_vaddr;
if (interp_elf_ex->e_type == ET_EXEC || load_addr_set)
elf_type |= MAP_FIXED;

map_addr = elf_map(interpreter, load_addr + vaddr, eppnt, elf_prot, elf_type);

if (!load_addr_set && interp_elf_ex->e_type == ET_DYN) {
load_addr = map_addr - ELF_PAGESTART(vaddr);
load_addr_set = 1;
}

/*
* Find the end of the file mapping for this phdr, and keep
* track of the largest address we see for this.
*/
k = load_addr + eppnt->p_vaddr + eppnt->p_filesz;
if (k > elf_bss)
elf_bss = k;

/*
* Do the same thing for the memory mapping - between
* elf_bss and last_bss is the bss section.
*/
k = load_addr + eppnt->p_memsz + eppnt->p_vaddr;
if (k > last_bss)
last_bss = k;
}
}

/* Now use mmap to map the library into memory. */

/*
* Now fill out the bss section. First pad the last page up
* to the page boundary, and then perform a mmap to make sure
* that there are zero-mapped pages up to and including the 
* last bss page.
*/
padzero(elf_bss);
elf_bss = ELF_PAGESTART(elf_bss + ELF_MIN_ALIGN - 1); /* What we have mapped so far*/

/* Map the last of the bss segment */
if (last_bss > elf_bss)
do_brk(elf_bss, last_bss - elf_bss);

*interp_load_addr = load_addr;
error = ((unsigned long) interp_elf_ex->e_entry) + load_addr;

out_close:
kfree(elf_phdata);
out:
return error;
}
static inline unsigned long
elf_map (struct file *filep, unsigned long addr, struct elf_phdr *eppnt, int prot, inttype)
{
unsigned long map_addr;

down(¤t->mm->mmap_sem);
map_addr = do_mmap(filep, ELF_PAGESTART(addr),
eppnt->p_filesz + ELF_PAGEOFFSET(eppnt->p_vaddr), prot, type,
eppnt->p_offset - ELF_PAGEOFFSET(eppnt->p_vaddr));

up(¤t->mm->mmap_sem);
return(map_addr);
}
void set_binfmt(struct linux_binfmt *new)
{
struct linux_binfmt *old = current->binfmt;
if (new && new->module)
__MOD_INC_USE_COUNT(new->module);
current->binfmt = new;
if (old && old->module)
__MOD_DEC_USE_COUNT(old->module);
}
static void set_brk(unsigned long start, unsigned long end)
{
start = ELF_PAGEALIGN(start);
end = ELF_PAGEALIGN(end);
if (end <= start)
return;
do_brk(start, end - start);
}
static void padzero(unsigned long elf_bss)
{
unsigned long nbyte;

nbyte = ELF_PAGEOFFSET(elf_bss);
if (nbyte) {
nbyte = ELF_MIN_ALIGN - nbyte;
clear_user((void *) elf_bss, nbyte);
}
}

分析ELF的加载过程的更多相关文章

  1. 第42天学习打卡(Class类 Class类的常用方法 内存分析 类的加载过程 类加载器 反射操作泛型 反射操作注解)

    Class类 对象照镜子后得到的信息:某个类的属性.方法和构造器.某个类到底实现了哪些接口.对于每个类而言,JRE都为其保留一个不变的Class类型的对象.一个Class对象包含了特定某个结构(cla ...

  2. Dubbo源码分析之ExtensionLoader加载过程解析

    ExtensionLoader加载机制阅读: Dubbo的类加载机制是模仿jdk的spi加载机制:  Jdk的SPI扩展加载机制:约定是当服务的提供者每增加一个接口的实现类时,需要在jar包的META ...

  3. Linux gadget驱动分析1------驱动加载过程

    为了解决一个问题,简单看了一遍linux gadget驱动的加载流程.做一下记录. 使用的内核为linux 2.6.35 硬件为芯唐NUC950. gadget是在UDC驱动上面的一层,如果要编写ga ...

  4. 重温.NET下Assembly的加载过程

    最近在工作中牵涉到了.NET下的一个古老的问题:Assembly的加载过程.虽然网上有很多文章介绍这部分内容,很多文章也是很久以前就已经出现了,但阅读之后发现,并没能解决我的问题,有些点写的不是特别详 ...

  5. 重温.NET下Assembly的加载过程 ASP.NET Core Web API下事件驱动型架构的实现(三):基于RabbitMQ的事件总线

    重温.NET下Assembly的加载过程   最近在工作中牵涉到了.NET下的一个古老的问题:Assembly的加载过程.虽然网上有很多文章介绍这部分内容,很多文章也是很久以前就已经出现了,但阅读之后 ...

  6. NET下Assembly的加载过程

    NET下Assembly的加载过程 最近在工作中牵涉到了.NET下的一个古老的问题:Assembly的加载过程.虽然网上有很多文章介绍这部分内容,很多文章也是很久以前就已经出现了,但阅读之后发现,并没 ...

  7. ELF文件的加载过程(load_elf_binary函数详解)--Linux进程的管理与调度(十三)

    加载和动态链接 从编译/链接和运行的角度看,应用程序和库程序的连接有两种方式. 一种是固定的.静态的连接,就是把需要用到的库函数的目标代码(二进制)代码从程序库中抽取出来,链接进应用软件的目标映像中: ...

  8. insmod模块加载过程代码分析1【转】

    转自:http://blog.chinaunix.net/uid-27717694-id-3966290.html 一.概述模块是作为ELF对象文件存放在文件系统中的,并通过执行insmod程序链接到 ...

  9. springmvc源码分析——入门看springmvc的加载过程

    本文将分析springmvc是如何在容器启动的时候将各个模块加载完成容器的创建的. 我知道在web.xml文件中我们是这样配置springmvc的: 可以看到,springmvc的核心控制器就是Dis ...

随机推荐

  1. webpack@3.6.0(1) -- 快速开始

    本篇内容 前言 配置入口和输出 热更新 loader配置 js代码压缩 html的打包与发布 前言 //全局安装 npm install -g webpack(3.6.0) npm init //安装 ...

  2. 洛谷P1171 售货员的难题

    P1171 售货员的难题 题目背景 数据有更改 题目描述 某乡有n个村庄(1<n<20),有一个售货员,他要到各个村庄去售货,各村庄之间的路程s(0<s<1000)是已知的,且 ...

  3. 洛谷P1098 字符串的展开

    P1098 字符串的展开 题目描述 在初赛普及组的“阅读程序写结果”的问题中,我们曾给出一个字符串展开的例子:如果在输入的字符串中,含有类似于“d-h”或者“4-8”的字串,我们就把它当作一种简写,输 ...

  4. JDK源码-java.lang.String

    1.开篇明志 本文来看看String的源码. 2.Java7 API String介绍 String 类代表字符串.Java 程序中的所有字符串字面值(如 “abc” )都作为此类的实例实现. 字符串 ...

  5. Oracle 11g 数据类型

    1.     字符类型 数据类型 长度 说明 CHAR(n BYTE/CHAR) 默认1字节,n值最大为2000 末尾填充空格以达到指定长度,超过最大长度报错.默认指定长度为字节数,字符长度可以从1字 ...

  6. 关于递归函数返回值为null的问题

    public function gettopcateid($cate_id){ $pid=db('cate')->where('cate_id',$cate_id)->find(); if ...

  7. ie 9 position:fixed 无效的两种情况

    第一种情况: 运行发现在Google Chrome,FireFox都可以的,但是在IE9就不行了很是郁闷,因为IE6以上的版本都是支持fixed的属性的:上网上找了好久没找到,因为不知道关键字该怎么搜 ...

  8. Vue中的指令(听博主说总结的很好)

    指令[重点] 作用:简化Dom操作 参考:https://cn.vuejs.org/v2/api/#%E6%8C%87%E4%BB%A4 特点: 1.都是以v-开头 2.除了插值表达式,其它都写在标签 ...

  9. sweetAlert()参数配置

    alertTypes = ['error', 'warning', 'info', 'success'], defaultParams = { title: '', text: '', type: n ...

  10. BestCoder Round #86 1003

    链接:http://acm.hdu.edu.cn/showproblem.php?pid=5806 题意:有多少个区间里的第 k 大的数不小于 m 解法:尺取法,首先我们用dp[i]保存到i的位置有多 ...