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. 定时器详解和应用、js加载阻塞、css加载阻塞

    1.setTimeout().setInterval()详解和应用 1.1 详解: setTimeout.setInterval执行时机 1.2 存在问题: setInterval重复定时器可能存在的 ...

  2. 常见错误及处理-jsp及Servlet

    1.servlet输入出页面时,中文字符乱码 解决方法: 在Writer之前设置请求头: response.setHeader("Content-type", "text ...

  3. Cogs 465. 挤牛奶

    465. 挤牛奶 ★   输入文件:milk2.in   输出文件:milk2.out   简单对比时间限制:1 s   内存限制:128 MB [问题描述] 三个农民每天清晨5点起床,然后去牛棚给3 ...

  4. asddf

    https://docs.saltstack.com/en/getstarted/fundamentals/index.html https://pypi.org/simple/cherrypy/ 安 ...

  5. POJ1028 Web Navigation

    题目来源:http://poj.org/problem?id=1028 题目大意: 模拟实现一个浏览器的“前进”和“回退”功能.由一个forward stack和一个backward stack实现. ...

  6. linux开机出现Give root password for maintenance (or type Control-D to continue):解决办法

    修改rc.local后导致 linux开机出现Give root password for maintenance,而且很多系统文件无法修改,之前的rc.local也不能修改了,单用户模式也无法进入 ...

  7. Chapter12

    package scalaimport java.awt.event.{ActionEvent, ActionListener}import javax.swing.JButton import sc ...

  8. springcloud中常用的注解@

    @SpringBootApplication是springboot启动类,包括三个注解,他们的作用分别是: @Configuration:表示将该类作用springboot配置文件类 @EnableA ...

  9. Murano中的角色

    Application Publisher - An individual or company that publishes an application to an application cat ...

  10. map 常用方法

    map遍历: Map map = new HashMap(); Iterator it = map.entrySet().iterator(); while(it.hasNext()) { Map.E ...