⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 exec.c

📁 带中文注释的 linux 0.11 源代码0.11,很好的
💻 C
📖 第 1 页 / 共 2 页
字号:
/** linux/fs/exec.c** (C) 1991 Linus Torvalds*//** #!-checking implemented by tytso.*//** #!开始的程序检测部分是由tytso 实现的。*//** Demand-loading implemented 01.12.91 - no need to read anything but* the header into memory. The inode of the executable is put into* "current->executable", and page faults do the actual loading. Clean.** Once more I can proudly say that linux stood up to being changed: it* was less than 2 hours work to get demand-loading completely implemented.*//** 需求时加载是于1991.12.1 实现的 - 只需将执行文件头部分读进内存而无须* 将整个执行文件都加载进内存。执行文件的i 节点被放在当前进程的可执行字段中* ("current->executable"),而页异常会进行执行文件的实际加载操作以及清理工作。** 我可以再一次自豪地说,linux 经得起修改:只用了不到2 小时的工作时间就完全* 实现了需求加载处理。*/#include <errno.h>		// 错误号头文件。包含系统中各种出错号。(Linus 从minix 中引进的)。#include <string.h>		// 字符串头文件。主要定义了一些有关字符串操作的嵌入函数。#include <sys/stat.h>		// 文件状态头文件。含有文件或文件系统状态结构stat{}和常量。#include <a.out.h>		// a.out 头文件。定义了a.out 执行文件格式和一些宏。#include <linux/fs.h>		// 文件系统头文件。定义文件表结构(file,buffer_head,m_inode 等)。#include <linux/sched.h>	// 调度程序头文件,定义了任务结构task_struct、初始任务0 的数据,// 还有一些有关描述符参数设置和获取的嵌入式汇编函数宏语句。#include <linux/kernel.h>	// 内核头文件。含有一些内核常用函数的原形定义。#include <linux/mm.h>		// 内存管理头文件。含有页面大小定义和一些页面释放函数原型。#include <asm/segment.h>	// 段操作头文件。定义了有关段寄存器操作的嵌入式汇编函数。extern int sys_exit (int exit_code);	// 程序退出系统调用。extern int sys_close (int fd);	// 文件关闭系统调用。/** MAX_ARG_PAGES defines the number of pages allocated for arguments* and envelope for the new program. 32 should suffice, this gives* a maximum env+arg of 128kB !*//** MAX_ARG_PAGES 定义了新程序分配给参数和环境变量使用的内存最大页数。* 32 页内存应该足够了,这使得环境和参数(env+arg)空间的总合达到128kB!*/#define MAX_ARG_PAGES 32/** create_tables() parses the env- and arg-strings in new user* memory and creates the pointer tables from them, and puts their* addresses on the "stack", returning the new stack pointer value.*//** create_tables()函数在新用户内存中解析环境变量和参数字符串,由此* 创建指针表,并将它们的地址放到"堆栈"上,然后返回新栈的指针值。*///// 在新用户堆栈中创建环境和参数变量指针表。// 参数:p - 以数据段为起点的参数和环境信息偏移指针;argc - 参数个数;envc -环境变量数。// 返回:堆栈指针。static unsigned long *create_tables (char *p, int argc, int envc){  unsigned long *argv, *envp;  unsigned long *sp;// 堆栈指针是以4 字节(1 节)为边界寻址的,因此这里让sp 为4 的整数倍。  sp = (unsigned long *) (0xfffffffc & (unsigned long) p);// sp 向下移动,空出环境参数占用的空间个数,并让环境参数指针envp 指向该处。  sp -= envc + 1;  envp = sp;// sp 向下移动,空出命令行参数指针占用的空间个数,并让argv 指针指向该处。// 下面指针加1,sp 将递增指针宽度字节值。  sp -= argc + 1;  argv = sp;// 将环境参数指针envp 和命令行参数指针以及命令行参数个数压入堆栈。  put_fs_long ((unsigned long) envp, --sp);  put_fs_long ((unsigned long) argv, --sp);  put_fs_long ((unsigned long) argc, --sp);// 将命令行各参数指针放入前面空出来的相应地方,最后放置一个NULL 指针。  while (argc-- > 0)    {      put_fs_long ((unsigned long) p, argv++);      while (get_fs_byte (p++)) /* nothing */ ;	// p 指针前移4 字节。    }  put_fs_long (0, argv);// 将环境变量各指针放入前面空出来的相应地方,最后放置一个NULL 指针。  while (envc-- > 0)    {      put_fs_long ((unsigned long) p, envp++);      while (get_fs_byte (p++)) /* nothing */ ;    }  put_fs_long (0, envp);  return sp;			// 返回构造的当前新堆栈指针。}/** count() counts the number of arguments/envelopes*//** count()函数计算命令行参数/环境变量的个数。*///// 计算参数个数。// 参数:argv - 参数指针数组,最后一个指针项是NULL。// 返回:参数个数。static intcount (char **argv){  int i = 0;  char **tmp;  if (tmp = argv)    while (get_fs_long ((unsigned long *) (tmp++)))      i++;  return i;}/** 'copy_string()' copies argument/envelope strings from user* memory to free pages in kernel mem. These are in a format ready* to be put directly into the top of new user memory.** Modified by TYT, 11/24/91 to add the from_kmem argument, which specifies* whether the string and the string array are from user or kernel segments:** from_kmem argv * argv *** 0 user space user space* 1 kernel space user space* 2 kernel space kernel space** We do this by playing games with the fs segment register. Since it* it is expensive to load a segment register, we try to avoid calling* set_fs() unless we absolutely have to.*//** 'copy_string()'函数从用户内存空间拷贝参数和环境字符串到内核空闲页面内存中。* 这些已具有直接放到新用户内存中的格式。** 由TYT(Tytso)于1991.12.24 日修改,增加了from_kmem 参数,该参数指明了字符串或* 字符串数组是来自用户段还是内核段。** from_kmem argv * argv *** 0 用户空间 用户空间* 1 内核空间 用户空间* 2 内核空间 内核空间** 我们是通过巧妙处理fs 段寄存器来操作的。由于加载一个段寄存器代价太大,所以* 我们尽量避免调用set_fs(),除非实在必要。*///// 复制指定个数的参数字符串到参数和环境空间。// 参数:argc - 欲添加的参数个数;argv - 参数指针数组;page - 参数和环境空间页面指针数组。// p -在参数表空间中的偏移指针,始终指向已复制串的头部;from_kmem - 字符串来源标志。// 在do_execve()函数中,p 初始化为指向参数表(128kB)空间的最后一个长字处,参数字符串// 是以堆栈操作方式逆向往其中复制存放的,因此p 指针会始终指向参数字符串的头部。// 返回:参数和环境空间当前头部指针。static unsigned longcopy_strings (int argc, char **argv, unsigned long *page,	      unsigned long p, int from_kmem){  char *tmp, *pag;  int len, offset = 0;  unsigned long old_fs, new_fs;  if (!p)    return 0;			/* bullet-proofing *//* 偏移指针验证 */// 取ds 寄存器值到new_fs,并保存原fs 寄存器值到old_fs。  new_fs = get_ds ();  old_fs = get_fs ();// 如果字符串和字符串数组来自内核空间,则设置fs 段寄存器指向内核数据段(ds)。  if (from_kmem == 2)    set_fs (new_fs);// 循环处理各个参数,从最后一个参数逆向开始复制,复制到指定偏移地址处。  while (argc-- > 0)    {// 如果字符串在用户空间而字符串数组在内核空间,则设置fs 段寄存器指向内核数据段(ds)。      if (from_kmem == 1)	set_fs (new_fs);// 从最后一个参数开始逆向操作,取fs 段中最后一参数指针到tmp,如果为空,则出错死机。      if (!(tmp = (char *) get_fs_long (((unsigned long *) argv) + argc)))	panic ("argc is wrong");// 如果字符串在用户空间而字符串数组在内核空间,则恢复fs 段寄存器原值。      if (from_kmem == 1)	set_fs (old_fs);// 计算该参数字符串长度len,并使tmp 指向该参数字符串末端。      len = 0;			/* remember zero-padding */      do	{			/* 我们知道串是以NULL 字节结尾的 */	  len++;	}      while (get_fs_byte (tmp++));// 如果该字符串长度超过此时参数和环境空间中还剩余的空闲长度,则恢复fs 段寄存器并返回0。      if (p - len < 0)	{			/* this shouldn't happen - 128kB */	  set_fs (old_fs);	/* 不会发生-因为有128kB 的空间 */	  return 0;	}// 复制fs 段中当前指定的参数字符串,是从该字符串尾逆向开始复制。      while (len)	{	  --p;	  --tmp;	  --len;// 函数刚开始执行时,偏移变量offset 被初始化为0,因此若offset-1<0,说明是首次复制字符串,// 则令其等于p 指针在页面内的偏移值,并申请空闲页面。	  if (--offset < 0)	    {	      offset = p % PAGE_SIZE;// 如果字符串和字符串数组在内核空间,则恢复fs 段寄存器原值。	      if (from_kmem == 2)		set_fs (old_fs);// 如果当前偏移值p 所在的串空间页面指针数组项page[p/PAGE_SIZE]==0,表示相应页面还不存在,// 则需申请新的内存空闲页面,将该页面指针填入指针数组,并且也使pag 指向该新页面,若申请不// 到空闲页面则返回0。	      if (!(pag = (char *) page[p / PAGE_SIZE]) &&		  !(pag = (char *) page[p / PAGE_SIZE] =		    (unsigned long *) get_free_page ()))		return 0;// 如果字符串和字符串数组来自内核空间,则设置fs 段寄存器指向内核数据段(ds)。	      if (from_kmem == 2)		set_fs (new_fs);	    }// 从fs 段中复制参数字符串中一字节到pag+offset 处。	  *(pag + offset) = get_fs_byte (tmp);	}    }// 如果字符串和字符串数组在内核空间,则恢复fs 段寄存器原值。  if (from_kmem == 2)    set_fs (old_fs);// 最后,返回参数和环境空间中已复制参数信息的头部偏移值。  return p;}//// 修改局部描述符表中的描述符基址和段限长,并将参数和环境空间页面放置在数据段末端。// 参数:text_size - 执行文件头部中a_text 字段给出的代码段长度值;// page - 参数和环境空间页面指针数组。// 返回:数据段限长值(64MB)。static unsigned longchange_ldt (unsigned long text_size, unsigned long *page){  unsigned long code_limit, data_limit, code_base, data_base;  int i;// 根据执行文件头部a_text 值,计算以页面长度为边界的代码段限长。并设置数据段长度为64MB。  code_limit = text_size + PAGE_SIZE - 1;  code_limit &= 0xFFFFF000;  data_limit = 0x4000000;// 取当前进程中局部描述符表代码段描述符中代码段基址,代码段基址与数据段基址相同。  code_base = get_base (current->ldt[1]);  data_base = code_base;// 重新设置局部表中代码段和数据段描述符的基址和段限长。  set_base (current->ldt[1], code_base);  set_limit (current->ldt[1], code_limit);  set_base (current->ldt[2], data_base);  set_limit (current->ldt[2], data_limit);/* make sure fs points to the NEW data segment *//* 要确信fs 段寄存器已指向新的数据段 */// fs 段寄存器中放入局部表数据段描述符的选择符(0x17)。  __asm__ ("pushl $0x17\n\tpop %%fs"::);// 将参数和环境空间已存放数据的页面(共可有MAX_ARG_PAGES 页,128kB)放到数据段线性地址的// 末端。是调用函数put_page()进行操作的(mm/memory.c, 197)。  data_base += data_limit;  for (i = MAX_ARG_PAGES - 1; i >= 0; i--)    {

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -