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

📄 exec.c

📁 linux0.11原码
💻 C
📖 第 1 页 / 共 2 页
字号:
/* passed *  linux/fs/exec.c * *  (C) 1991  Linus Torvalds */#include <set_seg.h>
/* * #!开始的程序检测部分是由tytso 实现的。 *//* * 需求时加载是于1991.12.1 实现的- 只需将执行文件头部分读进内存而无须
 * 将整个执行文件都加载进内存。执行文件的i 节点被放在当前进程的可执行字段中
 * ("current-.h>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 定义了新程序分配给参数和环境变量使用的内存最大页数。
 * 32 页内存应该足够了,这使得环境和参数(env+arg)空间的总合达到128kB! */#define MAX_ARG_PAGES 32/* * 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()函数计算命令行参数/环境变量的个数。 */
//// 计算参数个数。
// 参数:argv - 参数指针数组,最后一个指针项是NULL。
// 返回:参数个数。static int count(char ** argv){	int i=0;	char ** tmp;	if (tmp = argv)		while (get_fs_long((unsigned long *) (tmp++)))			i++;	return i;}/* * '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 long copy_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;	/* 偏移指针验证 */
// 取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;		/* 我们知道串是以NULL 字节结尾的 */		do {			len++;		} while (get_fs_byte(tmp++));
// 如果该字符串长度超过此时参数和环境空间中还剩余的空闲长度,则恢复fs 段寄存器并返回0。		if (p-len < 0) {	/* 不会发生-因为有128kB 的空间 */			set_fs(old_fs);			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] = 				      (char *) 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 long change_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);/* 要确信fs 段寄存器已指向新的数据段 */
// fs 段寄存器中放入局部表数据段描述符的选择符(0x17)。//	__asm__("pushl $0x17\n\tpop %%fs"::);
	_asm {
		push 0x17
		pop fs
	}
// 将参数和环境空间已存放数据的页面(共可有MAX_ARG_PAGES 页,128kB)放到数据段线性地址的
// 末端。是调用函数put_page()进行操作的(mm/memory.c, 197)。	data_base += data_limit;	for (i=MAX_ARG_PAGES-1 ; i>=0 ; i--) {		data_base -= PAGE_SIZE;		if (page[i])						// 如果该页面存在,			put_page(page[i],data_base);	// 就放置该页面。	}	return data_limit;		// 最后返回数据段限长(64MB)。}/*

⌨️ 快捷键说明

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