/* * linux/mm/memory.c * * (C) 1991 Linus Torvalds */ /* * demand-loading started 01.12.91 - seems it is high on the list of * things wanted, and it should be easy to implement. - Linus */ /* * Ok, demand-loading was easy, shared pages a little bit tricker. Shared * pages started 02.12.91, seems to work. - Linus. * * Tested sharing by executing about 30 /bin/sh: under the old kernel it * would have taken more than the 6M I have free, but it worked well as * far as I could see. * * Also corrected some "invalidate()"s - I wasn't doing enough of them. */ #include #include #include #include #include #include #include #include volatile void do_exit(long code); static inline volatile void oom(void) { printk("out of memory\n\r"); do_exit(SIGSEGV); } #define invalidate() \ __asm__("movl %%eax,%%cr3"::"a" (0)) /* these are not to be changed without changing head.s etc */ #define LOW_MEM 0x100000 #define PAGING_MEMORY (15*1024*1024) #define PAGING_PAGES (PAGING_MEMORY>>12) #define MAP_NR(addr) (((addr)-LOW_MEM)>>12) #define USED 100 #define CODE_SPACE(addr) ((((addr)+4095)&~4095) < \ current->start_code + current->end_code) static long HIGH_MEMORY = 0; #define copy_page(from,to) \ __asm__("cld ; rep ; movsl"::"S" (from),"D" (to),"c" (1024)) static unsigned char mem_map [ PAGING_PAGES ] = {0,}; /* * Get physical address of first (actually last :-) free page, and mark it * used. If no free pages left, return 0. */ unsigned long get_free_page(void) { register unsigned long __res asm("ax"); __asm__("std ; repne ; scasb\n\t" "jne 1f\n\t" "movb $1,1(%%edi)\n\t" "sall $12,%%ecx\n\t" "addl %2,%%ecx\n\t" "movl %%ecx,%%edx\n\t" "movl $1024,%%ecx\n\t" "leal 4092(%%edx),%%edi\n\t" "rep ; stosl\n\t" "movl %%edx,%%eax\n\t" "1:" "cld\n\t" /* by wyj */ :"=a" (__res) :"0" (0),"i" (LOW_MEM),"c" (PAGING_PAGES), "D" (mem_map+PAGING_PAGES-1) ); return __res; } /* * Free a page of memory at physical address 'addr'. Used by * 'free_page_tables()' */ void free_page(unsigned long addr) { if (addr < LOW_MEM) return; if (addr >= HIGH_MEMORY) panic("trying to free nonexistent page"); addr -= LOW_MEM; addr >>= 12; if (mem_map[addr]--) return; mem_map[addr]=0; panic("trying to free free page"); } /* * This function frees a continuos block of page tables, as needed * by 'exit()'. As does copy_page_tables(), this handles only 4Mb blocks. */ int free_page_tables(unsigned long from,unsigned long size) { unsigned long *pg_table; unsigned long * dir, nr; if (from & 0x3fffff) panic("free_page_tables called with wrong alignment"); if (!from) panic("Trying to free up swapper memory space"); size = (size + 0x3fffff) >> 22; dir = (unsigned long *) ((from>>20) & 0xffc); /* _pg_dir = 0 */ for ( ; size-->0 ; dir++) { if (!(1 & *dir)) continue; pg_table = (unsigned long *) (0xfffff000 & *dir); for (nr=0 ; nr<1024 ; nr++) { if (1 & *pg_table) free_page(0xfffff000 & *pg_table); *pg_table = 0; pg_table++; } free_page(0xfffff000 & *dir); *dir = 0; } invalidate(); return 0; } /* * Well, here is one of the most complicated functions in mm. It * copies a range of linerar addresses by copying only the pages. * Let's hope this is bug-free, 'cause this one I don't want to debug :-) * * Note! We don't copy just any chunks of memory - addresses have to * be divisible by 4Mb (one page-directory entry), as this makes the * function easier. It's used only by fork anyway. * * NOTE 2!! When from==0 we are copying kernel space for the first * fork(). Then we DONT want to copy a full page-directory entry, as * that would lead to some serious memory waste - we just copy the * first 160 pages - 640kB. Even that is more than we need, but it * doesn't take any more memory - we don't copy-on-write in the low * 1 Mb-range, so the pages can be shared with the kernel. Thus the * special case for nr=xxxx. */ int copy_page_tables(unsigned long from,unsigned long to,long size ) { unsigned long * from_page_table; unsigned long * to_page_table; unsigned long this_page; unsigned long * from_dir, * to_dir; unsigned long nr; if ((from&0x3fffff) || (to&0x3fffff)) panic("copy_page_tables called with wrong alignment"); from_dir = (unsigned long *) ((from>>20) & 0xffc); /* _pg_dir = 0 */ to_dir = (unsigned long *) ((to>>20) & 0xffc); size = ((unsigned) (size+0x3fffff)) >> 22; for( ; size-->0 ; from_dir++,to_dir++) { if (1 & *to_dir) panic("copy_page_tables: already exist"); if (!(1 & *from_dir)) continue; from_page_table = (unsigned long *) (0xfffff000 & *from_dir); if (!(to_page_table = (unsigned long *) get_free_page())) return -1; /* Out of memory, see freeing */ *to_dir = ((unsigned long) to_page_table) | 7; nr = (from==0)?0xA0:1024; for ( ; nr-- > 0 ; from_page_table++,to_page_table++) { this_page = *from_page_table; if (!(1 & this_page)) continue; this_page &= ~2; *to_page_table = this_page; if (this_page > LOW_MEM) { *from_page_table = this_page; this_page -= LOW_MEM; this_page >>= 12; mem_map[this_page]++; } } } invalidate(); return 0; } /* * This function puts a page in memory at the wanted address. * It returns the physical address of the page gotten, 0 if * out of memory (either when trying to access page-table or * page.) */ unsigned long put_page(unsigned long page,unsigned long address) { unsigned long tmp, *page_table; /* NOTE !!! This uses the fact that _pg_dir=0 */ if (page < LOW_MEM || page >= HIGH_MEMORY) printk("Trying to put page %p at %p\n",page,address); if (mem_map[(page-LOW_MEM)>>12] != 1) printk("mem_map disagrees with %p at %p\n",page,address); page_table = (unsigned long *) ((address>>20) & 0xffc); if ((*page_table)&1) page_table = (unsigned long *) (0xfffff000 & *page_table); else { if (!(tmp=get_free_page())) return 0; *page_table = tmp|7; page_table = (unsigned long *) tmp; } page_table[(address>>12) & 0x3ff] = page | 7; /* no need for invalidate */ return page; } void un_wp_page(unsigned long * table_entry) { unsigned long old_page,new_page; old_page = 0xfffff000 & *table_entry; if (old_page >= LOW_MEM && mem_map[MAP_NR(old_page)]==1) { *table_entry |= 2; invalidate(); return; } if (!(new_page=get_free_page())) oom(); if (old_page >= LOW_MEM) mem_map[MAP_NR(old_page)]--; *table_entry = new_page | 7; invalidate(); copy_page(old_page,new_page); } /* * This routine handles present pages, when users try to write * to a shared page. It is done by copying the page to a new address * and decrementing the shared-page counter for the old page. * * If it's in code space we exit with a segment error. */ void do_wp_page(unsigned long error_code,unsigned long address) { #if 0 /* we cannot do this yet: the estdio library writes to code space */ /* stupid, stupid. I really want the libc.a from GNU */ if (CODE_SPACE(address)) do_exit(SIGSEGV); #endif un_wp_page((unsigned long *) (((address>>10) & 0xffc) + (0xfffff000 & *((unsigned long *) ((address>>20) &0xffc))))); } void write_verify(unsigned long address) { unsigned long page; if (!( (page = *((unsigned long *) ((address>>20) & 0xffc)) )&1)) return; page &= 0xfffff000; page += ((address>>10) & 0xffc); if ((3 & *(unsigned long *) page) == 1) /* non-writeable, present */ un_wp_page((unsigned long *) page); return; } void get_empty_page(unsigned long address) { unsigned long tmp; if (!(tmp=get_free_page()) || !put_page(tmp,address)) { free_page(tmp); /* 0 is ok - ignored */ oom(); } } /* * try_to_share() checks the page at address "address" in the task "p", * to see if it exists, and if it is clean. If so, share it with the current * task. * * NOTE! This assumes we have checked that p != current, and that they * share the same executable. */ static int try_to_share(unsigned long address, struct task_struct * p) { unsigned long from; unsigned long to; unsigned long from_page; unsigned long to_page; unsigned long phys_addr; from_page = to_page = ((address>>20) & 0xffc); from_page += ((p->start_code>>20) & 0xffc); to_page += ((current->start_code>>20) & 0xffc); /* is there a page-directory at from? */ from = *(unsigned long *) from_page; if (!(from & 1)) return 0; from &= 0xfffff000; from_page = from + ((address>>10) & 0xffc); phys_addr = *(unsigned long *) from_page; /* is the page clean and present? */ if ((phys_addr & 0x41) != 0x01) return 0; phys_addr &= 0xfffff000; if (phys_addr >= HIGH_MEMORY || phys_addr < LOW_MEM) return 0; to = *(unsigned long *) to_page; if (!(to & 1)) if (to = get_free_page()) *(unsigned long *) to_page = to | 7; else oom(); to &= 0xfffff000; to_page = to + ((address>>10) & 0xffc); if (1 & *(unsigned long *) to_page) panic("try_to_share: to_page already exists"); /* share them: write-protect */ *(unsigned long *) from_page &= ~2; *(unsigned long *) to_page = *(unsigned long *) from_page; invalidate(); phys_addr -= LOW_MEM; phys_addr >>= 12; mem_map[phys_addr]++; return 1; } /* * share_page() tries to find a process that could share a page with * the current one. Address is the address of the wanted page relative * to the current data space. * * We first check if it is at all feasible by checking executable->i_count. * It should be >1 if there are other tasks sharing this inode. */ static int share_page(unsigned long address) { struct task_struct ** p; if (!current->executable) return 0; if (current->executable->i_count < 2) return 0; for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) { if (!*p) continue; if (current == *p) continue; if ((*p)->executable != current->executable) continue; if (try_to_share(address,*p)) return 1; } return 0; } void do_no_page(unsigned long error_code,unsigned long address) { int nr[4]; unsigned long tmp; unsigned long page; int block,i; /* if (current->pid > 5) printk(" --do_no_page: address=%x, pid=%d\n", address, current->pid); */ address &= 0xfffff000; tmp = address - current->start_code; if (!current->executable || tmp >= current->end_data) { get_empty_page(address); return; } if (share_page(tmp)) return; if (!(page = get_free_page())) oom(); /* remember that 1 block is used for header */ block = 1 + tmp/BLOCK_SIZE; for (i=0 ; i<4 ; block++,i++) nr[i] = bmap(current->executable,block); bread_page(page,current->executable->i_dev,nr); i = tmp + 4096 - current->end_data; tmp = page + 4096; while (i-- > 0) { tmp--; *(char *)tmp = 0; } if (put_page(page,address)) return; free_page(page); oom(); } void mem_init(long start_mem, long end_mem) { int i; HIGH_MEMORY = end_mem; for (i=0 ; i>= 12; while (end_mem-->0) mem_map[i++]=0; } int do_execve2(unsigned long * eip,long tmp,char * filename, char ** argv, char ** envp) { int ans=0; if((ans=do_execve(eip,tmp,filename,argv,envp))) return ans; int nr[4]; unsigned long temp; unsigned long page; int block,i; unsigned long address=current->start_code; //this part comes from do_no_page as achievement of immediate loading of cs and ds while(address<=current->brk+current->start_code) { address &= 0xfffff000; temp = address - current->start_code; if (share_page(temp)) return -1; if (!(page = get_free_page())) oom(); block = 1 + temp/BLOCK_SIZE; for (i=0 ; i<4 ; block++,i++) nr[i] = bmap(current->executable,block); bread_page(page,current->executable->i_dev,nr); if (!put_page(page,address)) { free_page(page); oom(); } address+=PAGE_SIZE; } return 0; } void calc_mem(void) { int i,j,k,free=0; long * pg_tbl; for(i=0 ; i>20) & 0xffc); from_page += ((current->start_code>>20) & 0xffc); to_page += ((p->start_code>>20) & 0xffc); //从页表项获取页地址 from = *(unsigned long *) from_page; from &= 0xfffff000; from_page = from + ((address>>10) & 0xffc); phys_addr = *(unsigned long *) from_page; to = *(unsigned long *) to_page; if (!(to & 1)) //是否存在 if (to = get_free_page()) *(unsigned long *) to_page = to | 7; //权限修改 else return 0; to &= 0xfffff000; to_page = to + ((address>>10) & 0xffc); if (1 & *(unsigned long *) to_page) return 0; /* share them: write-protect */ *(unsigned long *) to_page = *(unsigned long *) from_page; //复制到p进程 phys_addr -= LOW_MEM; phys_addr >>= 12; mem_map[phys_addr]++; //物理页映射数组,记录该物理页的使用者数量 return 1; } // rememeber to sync when munmap exit manual long sys_mmap(void * start,size_t len,unsigned long * argv)// int prot,int flags,int fd,off_t off { unsigned long a,b,c,d; a=get_fs_long(argv); b=get_fs_long(argv+1); c=get_fs_long(argv+2); d=get_fs_long(argv+3); //printk("a,b,c,d:%ld,%ld,%ld,%ld\n",a,b,c,d); int prot=a,flags=b,fd=c; off_t off=d; int block; size_t l=len; struct buffer_head * bh; char *temp,*p1,*p2; void * buf=start; int tempprot=0; //因为new.h和namei.c定义不一样,要修改 if(prot &PROT_READ) tempprot |=4; if(prot &PROT_EXEC) tempprot |=1; if(prot &PROT_WRITE) tempprot |=2; //特殊量len>0 off%4096==0 prot至少有read权限 if( (flags & MAP_SHARED)&& (flags &MAP_PRIVATE )) { printk("error with private and shared request.\n"); return -1; } if(len<=0 || off<0 || (off%PAGE_SIZE)) { if(len<=0) printk("the len of the wanting bytes must be positive.\n"); else printk("offset must be the mul of 4096.\n"); return MAP_FAILED; } if(!(prot& PROT_READ)) return MAP_FAILED; //permissions of the file ,参考函数namei.c::permissions struct m_inode *inode=current->filp[fd]->f_inode; int mode = inode->i_mode; if (inode->i_dev && !inode->i_nlinks) return MAP_FAILED; else if (current->euid==inode->i_uid) mode >>= 6; else if (current->egid==inode->i_gid) mode >>= 3; if (!(((mode & tempprot & 0007) == tempprot) || suser())) { printk("error permission."); return MAP_FAILED; } //end of permission check //设置用户空间的虚拟地址,初始为32MB处,这样写在brk增长至32MB时会有bug,但是这种情况不会出现, //因为物理页一共16MB,linux-011没有页替换策略,即linux011内核默认虚拟内存利用不会超过16MB //同时,当栈向下增长到一定程度时,也会和当前地址冲突,这种情况也不可能出现 if(start<=current->brk || start>=current->start_stack-0x8000 || start==NULL) { if(!current->mmap_tail) buf=0x2000000; else buf=current->mmap_tail->endaddr; } //申请mmap struct mmap_struct *mmap=(struct mmap_struct *)malloc(sizeof(struct mmap_struct)); mmap->address=buf; mmap->size=len; mmap->endaddr=(unsigned long)buf+(len + 0xfff)&0xfffff000; mmap->rw=prot; mmap->ps=flags; mmap->filenode=(struct m_inode*)malloc(sizeof(struct m_inode)); memcpy(mmap->filenode,inode,sizeof(struct m_inode)); if(!mmap->filenode->i_count)mmap->filenode->i_count=1; mmap->off=off; mmap->next=NULL; //加入current->mmap if(!current->mmap) {current->mmap=current->mmap_tail=mmap;} else { current->mmap_tail->next=mmap; current->mmap_tail=mmap; } //分配物理页 unsigned long from,to,from_page,to_page,phys_addr,tpage,address=mmap->address; while(addressendaddr) { from_page = ((address>>20) & 0xffc); from_page += ((current->start_code>>20) & 0xffc);//页目录 if(!(*(unsigned long *)from_page&1)) //页表项不存在,申请页面 if(!(tpage=get_free_page())) { printk("out of memory.\n"); return MAP_FAILED; } else { *(unsigned long *)from_page= tpage | 7; mem_map[(tpage-LOW_MEM)>>12]++; } from=*(unsigned long *)from_page; //页表项 from &= 0xfffff000; from_page = from + ((address>>10) & 0xffc); if(!(*(unsigned long *)from_page & 1)) //物理页不存在 if(!(tpage=get_free_page())) { printk("out of memory.\n"); return MAP_FAILED; } else { *(unsigned long *)from_page= tpage | 7; mem_map[(tpage-LOW_MEM)>>12]++; } *(unsigned long *)from_page &= (prot&PROT_WRITE)?0xffffffff:0xfffffffd; //分配读写权限 //设置物理页共享 if(flags & MAP_SHARED) { //printk("i am doing sharing."); struct task_struct ** p; for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) { if (!*p) continue; if (current == *p) continue; //在这一部分,我将current->address处的物理页复制给 //所有进程的该区域 if(!my_share(address,*p)) printk("share failed at task:pid=%d",(*p)->pid); } } address+=PAGE_SIZE; } //将文件内容写入共享内存 if (!(block = inode->i_zone[0])) return NULL; if (!(bh = bread(inode->i_dev,block))) return NULL; temp = (char *) bh->b_data; p1=temp+off;p2=buf; while(l--) put_fs_byte(*(p1++),p2++); return buf; } static int my_sync(off_t pos, struct m_inode * inode, char * buf, int count) { int block,c; struct buffer_head * bh; char * p; int i=0; while (ii_dev,block))) break; c = pos % BLOCK_SIZE;//从偏移里那个开始写 p = c + bh->b_data; bh->b_dirt = 1; c = BLOCK_SIZE-c;//本块要写的数量 if (c > count-i) c = count-i; pos += c;//已经读了c字节,更新偏移量 if (pos > inode->i_size) { inode->i_size = pos; inode->i_dirt = 1; } i += c; while (c-->0) //暂存到p里面,即bh里 *(p++) = get_fs_byte(buf++); brelse(bh);//释放bh同时,同步到磁盘 } inode->i_mtime = CURRENT_TIME;//文件最后修改时间 inode->i_ctime = CURRENT_TIME;//i节点修改时间 return (i?i:-1); } int sys_munmap(void * start ,size_t size) { struct mmap_struct *temp=current->mmap; struct mmap_struct *pre=NULL; while(temp!=NULL) //遍历链表 { if(temp->address==(unsigned long)start) break; else temp=temp->next; pre=temp; } if(!temp) //未找到 { printk("invalid address.\n"); return -1; } if(!pre) //为头节点 { if(current->mmap_tail==current->mmap)current->mmap_tail=NULL; current->mmap=current->mmap->next; } else if(temp->next=NULL) //中间节点 { current->mmap_tail=pre; pre->next=NULL; } else{ //尾节点 pre->next=temp->next; } unsigned long from,to,from_page,to_page,phys_addr,tpage,address=temp->address; //printk("%lx\n",address); if(!(temp->ps & MAP_SHARED)) goto mytag; //若为共享,执行下面的代码 while (address < temp ->endaddr) { //printk("addr: %lx\n",address); from_page = ((address>>20) & 0xffc); from_page += ((current->start_code>>20) & 0xffc);//页目录 from=*(unsigned long *)from_page; //页表项 from &= 0xfffff000; from_page = from + ((address>>10) & 0xffc); phys_addr = (*(unsigned long *)from_page & 0xfffff000); //printk("addr: %lx",*(unsigned long *)from_page); if(!(*(unsigned long *)from_page & 1))//当前进程必须有该映射区物理页 { printk("error of deleting page at %x.\n",*(unsigned long *)from_page); printk("current address:%x\n",address); return -1; } if(temp->ps & MAP_SHARED) { struct task_struct ** p; for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) { //遍历所有进程 if (!*p) //空进程 continue; if (current == *p) //排除当前进程 continue; to_page = ((address>>20) & 0xffc)+(((*p)->start_code>>20) & 0xffc); to = *(unsigned long *) to_page; if(!(to & 1)) //不存在,肯定未共享过 continue; to &= 0xfffff000; to_page = to + ((address>>10) & 0xffc); if(!(*(unsigned long *)to_page &1)) //不存在,肯定未共享过 continue; to = (*(unsigned long *)to_page & 0xfffff000); if(phys_addr == to) { *(unsigned long *)to_page &= ~1; //删除页 mem_map[(phys_addr-LOW_MEM)>>12]--; //物理页使用者-- } } } address+=PAGE_SIZE; } if(temp->ps & MAP_SHARED) { if(my_sync(temp->off,temp->filenode,(char*)(temp->address),temp->size)==-1) return -1; } mytag: //取消删去当前进程映射区物理页 address=temp->address; while (address < temp ->endaddr) { //printk("%lx\n",address); from_page = ((address>>20) & 0xffc); from_page += ((current->start_code>>20) & 0xffc);//页目录 from=*(unsigned long *)from_page; //页表项 from &= 0xfffff000; from_page = from + ((address>>10) & 0xffc); phys_addr = (*(unsigned long *)from_page & 0xfffff000); //printk("addr: %lx",*(unsigned long *)from_page); if(!(*(unsigned long *)from_page & 1)) //存在 { printk("error of deleting page at %x.\n",*(unsigned long *)from_page); printk("current address:%x\n",address); return -1; } else{ *(unsigned long *)from_page &= ~1; //删除物理页 } address+=PAGE_SIZE; } free(temp->filenode); //清空节点 mmap->filename free(temp); //清空 mmap return 0; } /* 直接让esp(子进程)指向另一个进程(当前进程)的地址; 修改eip对应的指令,改为ret 0xec8353c3 ps:应该只有0xc3有用 由于eip只执行一条指令,我们将eip指向栈,将ret压入栈即可; 在这里我们只实现了int (*fn)(void *) 一种函数类型,并且要求fn内以exit结尾 于是我们只需修改八字节即可; 下面写的不对,不能指向另一个进程的地址,只能修改物理页了。 */ /* int do_clone(int nr,long tmp,int (*fn)(void *), void *child_stack, int flag) { //设置压栈表,方便操作; long stack[1024]; unsigned long len=0; task[nr]->tss.esp=(unsigned long) child_stack - 8; stack[0] = (unsigned long) fn; task[nr]->tss.eip = (unsigned long) child_stack - 4; stack[1]= 0xec8353c3; //栈区赋值; len = 8; char *p1=stack ,*p2=(char *)((unsigned long)child_stack-len); while(len--) put_fs_byte(*(p1++),p2++); return 0; } */ /* child_stack-4 00 00 00 00 */ /* child_stack-8 00 00 00 00 */ /* child_stack-12 arg */ /* child_stack-16 fn */ //esp /* child_stack-20 00 00 00 00 */ /* child_stack-24 00 00 00 00 */ /* child_stack-28 00 00 00 00 */ /* child_stack-32 0x000000c3 */ /* int do_clone(int nr,long tmp,int (*fn)(void *), void *child_stack, int flag) { void * argv =NULL; //共享物理页:写法有缺陷,可能child——stack跨页 //子进程task[nr]的页表项获取,目的是更新成child——stacK的物理页 unsigned long nrfrom_page,nrfrom,from_page,from,tpage,address; address = task[nr]->tss.esp; nrfrom_page = ((address>>20) & 0xffc); nrfrom_page += ((task[nr]->start_code>>20) & 0xffc);//页目录 nrfrom=*(unsigned long *)nrfrom_page; //页表项 if(!(nrfrom&1)) //无页表项分配一个 { if(!(tpage=get_free_page())) { printk("out of memory.\n"); return -1; } else { *(unsigned long *)nrfrom_page= tpage | 7; mem_map[(tpage-LOW_MEM)>>12]++; } } nrfrom &= 0xfffff000; nrfrom_page = nrfrom + ((address>>10) & 0xffc); //获取child_stack的物理页,并将其复制给task[nr]; address=(unsigned long) child_stack; from_page = ((address>>20) & 0xffc); from_page += ((current->start_code>>20) & 0xffc);//页目录 if(!(*(unsigned long *)from_page&1)) //页表项不存在,申请页面 if(!(tpage=get_free_page())) { printk("out of memory.\n"); return -1; } else { *(unsigned long *)from_page= tpage | 7; mem_map[(tpage-LOW_MEM)>>12]++; } from=*(unsigned long *)from_page; //页表项 from &= 0xfffff000; from_page = from + ((address>>10) & 0xffc); if(!(*(unsigned long *)from_page & 1)) //物理页不存在 if(!(tpage=get_free_page())) { printk("out of memory.\n"); return -1; } else { *(unsigned long *)from_page= tpage | 7; mem_map[(tpage-LOW_MEM)>>12]++; } //复制给nr *(unsigned long *)nrfrom_page = *(unsigned long *)from_page; unsigned long stack[128]; unsigned long off=(unsigned long)child_stack & 0xfff; if(off < 32) { printk("地址跨页,我也没办法.\n"); return -1; } //更新esp,让其指向child_stack同样偏移的地点; //注意:这里child_stack 指向的是高地址,要向下面push. task[nr]->tss.esp &=0xfffff000; task[nr]->tss.esp |=off - 16; //eip 只有一条ret //所以传进来的fn必须以exit结尾,不能return , //因为我不会return这个玩意push ebp 又pop 啥的 task[nr]->tss.eip &=0xfffff000; task[nr]->tss.eip |=off - 32; //ret eip stack[0]=0x000000c3; stack[4]=(unsigned long)fn; stack[5]=(unsigned long)argv; char * p1=stack, *p2=(unsigned long) child_stack - 32,len=32; while(len--) put_fs_byte(*(p1++),p2++); printk("nr:%d::fn:%x\n",nr,(unsigned long )fn); return 0; } */ int copy_page_tables_clone(unsigned long from,unsigned long to,long size,int flag) { unsigned long * from_page_table; unsigned long * to_page_table; unsigned long this_page; unsigned long * from_dir, * to_dir; unsigned long nr; //获取栈区域的物理页 /* child_stack= child_stack?child_stack:(0x2000000-4096); printk("chid:%lx\n",(unsigned long ) child_stack); unsigned long sfrom,sfrom_page,tpage; sfrom_page = ((child_stack>>20) & 0xffc); sfrom_page += ((current->start_code>>20) & 0xffc);//页目录 printk("11111222222222222221."); if(!(*(unsigned long *)sfrom_page&1)) //页表项不存在,申请页面 if(!(tpage=get_free_page())) { printk("out of memory.\n"); return -1; } else { *(unsigned long *)sfrom_page= tpage | 7; mem_map[(tpage-LOW_MEM)>>12]++; printk("111555555555511."); } printk("111111111111111111."); */ /* sfrom=*(unsigned long *)sfrom_page; //页表项 sfrom &= 0xfffff000; sfrom_page = sfrom + ((child_stack>>10) & 0xffc); printk("1111133333333."); if(!(*(unsigned long *)sfrom_page & 1)) //物理页不存在 if(!(tpage=get_free_page())) { printk("out of memory.\n"); return -1; } else { *(unsigned long *)sfrom_page= tpage | 7; printk("111114444466666664111."); mem_map[(tpage-LOW_MEM)>>12]++; printk("11111444444444444111."); } printk("11111111133333333333311."); */ //遍历进程size个页目录,复制到子进程中 if ((from&0x3fffff) || (to&0x3fffff)) panic("copy_page_tables called with wrong alignment"); from_dir = (unsigned long *) ((from>>20) & 0xffc); /* _pg_dir = 0 */ to_dir = (unsigned long *) ((to>>20) & 0xffc); size = ((unsigned) (size+0x3fffff)) >> 22; for( ; size-->0 ; from_dir++,to_dir++) { if (1 & *to_dir) panic("copy_page_tables: already exist"); if (!(1 & *from_dir)) continue; from_page_table = (unsigned long *) (0xfffff000 & *from_dir); if (!(to_page_table = (unsigned long *) get_free_page())) return -1; /* Out of memory, see freeing */ *to_dir = ((unsigned long) to_page_table) | 7; nr = (from==0)?0xA0:1024; for ( ; nr-- > 0 ; from_page_table++,to_page_table++) { this_page = *from_page_table; if (!(1 & this_page)) continue; //如果flag==0 表示创建进程 而非线程 //如果当前物理页是栈区物理页,父子进程都有写权限 if(!flag)// && !(this_page == *(unsigned long *)sfrom_page)) this_page &= ~2; *to_page_table = this_page; if (this_page > LOW_MEM) { *from_page_table = this_page; this_page -= LOW_MEM; this_page >>= 12; mem_map[this_page]++; } } } invalidate(); return 0; }