LFYOS |
我们安装的开发环境是Redhat 7.1,内核版本linux-2.4.2,下面以该版本为例来讨论LINUX操作系统安装和加载问题。在Redhat 7.1Linux操作系统中,当重构内核时,首先进入目录/usr/src/ linux-2.4.2,执行make命令,该命令根据文件/usr/src/ linux-2.4.2.Makefile中的规则,执行规则指定的各个命令,其执行过程是首先生成一个内核图象,然后再把该内核图象压缩后,安装到系统之中。生成内核图象的make规则为:
vmlinux: $(CONFIGURATION) init/main.o init/version.o linuxsubdirs
$(LD) $(LINKFLAGS) $(HEAD) init/main.o init/version.o \
--start-group \
$(CORE_FILES) \
$(DRIVERS) \
$(NETWORKS) \
$(LIBS) \
--end-group \
-o vmlinux
我们实现虚拟地址空间基于文件操作系统的安装和加载的方法是:修改该规则,使得做make重构内核时,不再根据生成linux内核的目标文件生成内核图象,而是根据生成虚拟地址空间基于文件操作系统的目标文件生成内核图象,为此把上诉规则修改为:
vmlinux : $(LFYOS)
$(LD) $(LINKFLAGS) $(LFYOS) -o vmlinux
变量$(LFYOS)中存放的是生成虚拟地址空间基于文件操作系统内核需要的目标文件,这样当我们重新做make重构内核时,就会根据变量$(LFYOS)中指定的目标文件生成一个内核图象,并把该内核图象安装到系统之中,而不再根据生成Linux的内核文件生成和安装LINUX内核图象。当重新启动系统时,就会把我们实现的内核启动起来,而不会启动一个LINUX内核。
安装和加载的详细步骤如下:
·i.以超级用户root注册。安装操作系统的内核当然必须是超级用户root。
·ii.检查系统是否安装了内核开发的软件包,是否安装了lilo,如果没有安装安装之,关于如何安装请参考安装LINUX的详细资料,此处略。
·iii.重构一遍linux内核,关于如何重构linux内核请参考安装LINUX的详细资料,此处略。
·iv. 进入目录/usr/src/linux-X.X.X(其中的X.X.X为内核版本号),复制一个Makefile,也就是执行命令:
cp Makefile lfy_makefile
·v.编辑文件lfy_makefile,确定其中的规则:
vmlinux: $(CONFIGURATION) init/main.o init/version.o linuxsubdirs
$(LD) $(LINKFLAGS) $(HEAD) init/main.o init/version.o \
--start-group \
$(CORE_FILES) \
$(DRIVERS) \
$(NETWORKS) \
$(LIBS) \
--end-group \
-o vmlinux
把该规则修改为:
vmlinux : $(LFYOS)
$(LD) $(LINKFLAGS) $(LFYOS) -o vmlinux
·vi.进入/etc目录,编辑lilo的配置文件/etc/lilo.conf。该文件中的内容类似于:
boot=/dev/hda
map=/boot/map
install=/boot/boot.b
prompt
timeout=180
message=/boot/message
linear
default=dos
image=/boot/vmlinuz-2.4.2-2
label=linux
read-only
root=/dev/hda7
other=/dev/hda1
optional
label=dos
在该文件中开始部分是安装各个操作系统总的配置信息,后面是各个操作系统的配置信息。修改该文件为:
boot=/dev/hda
map=/boot/map
install=/boot/boot.b
prompt
timeout=180
message=/boot/message
linear
default=dos
image=/boot/vmlinuz-2.4.2-2
label=linux
read-only
root=/dev/hda7
image=/boot/vmlinuz.new
label=lfyos
read-only
root=/dev/hda7
other=/dev/hda1
optional
label=dos
中间部分为加入的内容,也就是虚拟地址空间基于文件操作系统的启动信息,操作系统的启动文件为/boot/vmlinux.new,名称为lfyos,只读安装,安装设备为/dev/hda7。不同的系统安装设备可能不同,只需要把安装设备设置成和linux操作系统相同安装设备即可。
·vii.确定安装虚拟地址空间基于文件操作系统源文件和目标文件的目录,进入该目录(在我们的实现中,源文件和目标文件安装在目录/root下的os目录中),因此利用命令cd /root进入目录/root,把虚拟地址空间基于文件操作系统源文件的压缩文件os.tar拷贝到该目录下。
·viii.执行命令tar –xvf os.tar ,把源文件的压缩文件解压,该命令将在/root目录下生成一个子目录os,生成虚拟地址空间基于文件操作系统的源文件将全部解压到该子目录下。
·ix.进入/root/os目录,编辑该目录下的makefile文件,在该文件的第一行为
VERSION=linux-2.4.2
变量VERSION 中存放LINUX内核的版本号,如果你安装的LINUX内核的版本不是2.4.2,修改为相应的版本号。
·x.执行命令make。该命令执行完毕后,虚拟地址空间基于文件操作系统就已经成功的安装到你的系统。
·xi.重新启动系统,选择启动的操作系统,将发现可以选择的操作系统中包括lfyos,这就是安装的虚拟地址空间基于文件操作系统。选择并启动该操作系统,虚拟地址空间基于文件操作系统即开始运行。
#define DEFAULT_CS (0x23)
#define DEFAULT_DS (0x2b)
#define DEFAULT_ES DEFAULT_DS
#define DEFAULT_FS DEFAULT_DS
#define DEFAULT_GS DEFAULT_DS
#define DEFAULT_SS DEFAULT_DS
#define DEFAULT_SP (0xc0001ffc)
#define DEFAULT_SP_0 (0xc0000ffc)
typedef struct{
void (*ip)(void );
int cs;
int flag;
char *sp;
int ss;
int ds,es,fs,gs;
char *sp_0;
int (*function)(void );
int cr2[CR2_BUF_NUMBER];
}run_point;
#endif
2、文件kernel/capability.h
#ifndef OS_KERNEL_CAPABILITY
#define OS_KERNEL_CAPABILITY
struct capability {
int c1,c2,c3,c4;
};
#endif
3、文件kernel/process.h
#ifndef OS_PROCESS_STRUCT
#define OS_PROCESS_STRUCT
struct thread_physical_block {
int stack_memory_id,stack_block_id;
int memory_id,block_id;
};
struct network_address{
int a;
};
struct file{
struct{
struct network_address network_node;
int server_processor,server_process,file_handler;
}file;
struct{
int processor,process;
}proxy;
int memory_process,open_window_id;
int window_base_1,window_base_2,window_length,right;
struct capability capability;
int read_in_flag,swap_out_flag;
};
union file_system_operation_parameter{
struct {
int begin_rw,end_rw;
}read_write;
};
struct file_system_call_parameter {
int command,sleep_semaphore;
struct file file;
struct thread_physical_block block;
union file_system_operation_parameter parameter;
};
#define OPEN_FILE 0
#define CLOSE_FILE 1
#define READ_FILE 2
#define WRITE_FILE 3
#define OPERATE_FILE 4
struct process{
run_point start_point;
int (*driver)(struct file_system_call_parameter *par);
int priority;
int semaphore;
int enter_thread_number,id;
int max_thread_number,thread_number,thread_ring;
int max_semaphore_number,semaphore_number,semaphore_ring;
struct user_file_information file[USER_FILE_NUMBER];
struct capability capability;
};
#endif
4、文件kernel/kernel_struct.h
#ifndef OS_KERNEL_STRUCT
#define OS_KERNEL_STRUCT
struct semaphore{
int value;
int process;
int thread_id,thread_ring;
int front,back;
struct capability capability;
int heap,v_value;
struct kernel_time first,step;
};
struct thread ;
struct return_stack ;
struct thread_environment{
int system_call,system_call_arg1; /*ax,bx*/
int system_call_arg2,system_call_arg3; /*cx,dx*/
int system_call_arg4,system_call_arg5; /*si,di*/
int system_call_arg6,system_call_arg7; /*bp,r7*/
run_point point; /*ip,cs,flag,sp,ss */
};
struct exception_item{
int flag;
};
struct return_stack{
struct thread_environment environment;
int current_process,process_id,process_p_flag;
struct exception_item exception;
struct thread_physical_block physical_block;
};
struct thread{
enum {SLEEP,RUN,READY} state;
int priority;
int set_v_operation_result_flag;
int heap,process,pro_front,pro_back;
int semaphore,sleep_semaphore,sem_front,sem_back;
int cpu_id;
struct return_stack return_stack[RETURN_BLOCK_NUMBER];
int return_stack_top;
struct user_file_information file[USER_FILE_NUMBER];
};
struct thread_heap{
int thread;
};
#endif
5、文件kernel/os_struct.h
#ifndef OS_OS_STRUCT
#define OS_OS_STRUCT
struct cpu_information{
int thread_id;
struct{
int thread_id,process_id;
struct thread_physical_block physical_block;
}last;
};
struct os_data{
struct{
struct cpu_information used_cpu[VIRTUAL_CPU_NUMBER];
struct{
int cpu_id;
}free_cpu[VIRTUAL_CPU_NUMBER];
int free_cpu_top;
}virtual_cpu;
struct process process[PROCESS_NUMBER];
struct thread thread[THREAD_NUMBER];
struct{
struct thread_heap run_heap[VIRTUAL_CPU_NUMBER];
struct thread_heap ready_heap[THREAD_NUMBER];
int run_thread_number,ready_thread_number;
}thread_heap;
struct semaphore semaphore[SEMAPHORE_NUMBER];
struct{
int semaphore_id;
}semaphore_heap[SEMAPHORE_NUMBER];
struct kernel_time current_time;
struct system_file_information system_file[MEMORY_BODY_NUMBER];
struct capability system_capability;
};
struct current_information {
struct thread_environment *env;
struct return_stack *rt;
struct thread *t;
struct cpu_information *cpu_info;
union system_call_parameter *par;
int thread_id,cpu_id,return_stack_top;
};
extern struct current_information current;
#endif
#ifndef OS_MEMORY_STRUCT
#define OS_MEMORY_STRUCT
struct file_window{
enum{
FILE_OPEN,
FILE_CLOSE
} state;
struct file file;
int block_ring;
int file_front,file_back;
int lock_number;
};
struct memory_sleep_semaphore{
int processor,semaphore;
};
struct physical_block{
enum{
FREE_BLOCK,
READING_BLOCK,
NOT_MODIFIED_BLOCK,
MODIFIED_BLOCK,
WRITING_BLOCK
}state;
int lock_number;
int file_window,logic_block_number;
int physical_number;
int file_front,file_back;
int buf_front,buf_back;
int hash_front,hash_back;
struct memory_sleep_semaphore sleep_semaphore;
};
struct memory_resource{
int process_number;
int file_number,max_file_number;
int max_block_number,trigger_block_number;
int block_number,read_block_number,write_block_number;
struct capability capability;
};
struct memory_process{
int file_number,max_file_number,file_ring;
int max_block_number,trigger_block_number;
int block_number,read_block_number,write_block_number;
int block_ring,read_block_ring,write_block_ring;
struct capability capability;
};
struct memory_hash{
int hash_ring;
};
struct memory_body_struct{
int *free_physical_block,*free_file;
int *block_number,*free_block_number;
int *file_number,*process_number,*hash_number;
struct physical_block *physical_block;
struct file_window *file_window;
struct memory_process *memory_process;
struct memory_hash *hash;
int (*hash_function)(int file,int logic_block);
int my_processor,my_memory_body;
struct memory_sleep_semaphore *wait_block;
};
struct install_memory_body_parameter{
char *base;
int (*hash_function)(int file,int logic_block);
int my_processor,my_memory_body;
int block_number,file_number;
int process_number,hash_number;
int first_block;
struct capability capability;
};
#endif
2、文件memory/call_memory.h
#ifndef OS_MEMORY_MEMORY_CALL
#define OS_MEMORY_MEMORY_CALL
union memory_call_parameter{
struct{
struct install_memory_body_parameter
memory_body_parameter;
struct capability capability;
int set_stack_flag;
}setup;
struct open_file_window{
int file_window_id;
struct file_window file_window;
struct capability process_capability;
}open_file_window;
struct close_file_window{
int file_window_id;
struct capability file_capability;
}close_file_window;
struct file_attribute{
int file_window_id;
struct file file;
struct capability capability;
}file_attribute;
struct memory_map_deal{
int file_window_id;
int begin_logic_address,end_logic_address;
struct capability file_capability;
}memory_map_deal;
struct flush_process_memory{
int give_up_flag;
int process_number;
struct capability process_capability;
}flush_process_memory;
struct flush_file_window{
int give_up_flag;
int file_window_id;
struct capability file_capability;
}flush_file_window;
struct mark_modify{
int file_window_id;
int begin_logic_address,end_logic_address;
struct capability file_capability;
}mark_modifed;
struct memory_resource memory_resource;
struct control_file_system{
union file_system_operation_parameter parameter;
struct capability capability;
}control_file_system;
};
#endif
在虚拟地址空间基于文件操作系统中,采用按优先级调度方式,调度各个就绪线程占用处理机。执行线程调度时,总是选择优先数最小,也就是优先级最高的线程占用处理机。内核利用堆数据结构选择优先数最小的线程。下面首先介绍堆数据结构,然后讨论选择优先数最小线程的算法。
假设r[0],r[1],… r[n-1]是一序列元素,如果对于任意r[i],同时满足条件r[i].key≤r[2* i+1].key,r[i].key≤r[2*i+2].key,则称该序列是一个堆,也称为最小化堆,如果把不等式中的小于号≤换成大于号≥,称为最大化堆。在下面的讨论中,所有的堆指的是最小化堆。
可以把序列r[0],r[1],… r[n-1]看成是按数组方式存储的一棵满二叉树,元素r[0]为树根,其它元素为树中的中间结点或叶子结点。考虑到在一棵按数组方式存储的满二叉树中,元素r[2*i+1]和元素r[2*i+2]是元素r[i]的左孩子和右孩子,因此在一个堆中,对于任一元素r[i],其键值r[i].key一定小于等于其左右两个孩子的键值r[2* i+1].key和r[2*i+2].key。
根据堆的定义,可以得出堆的一个重要性质:如果一个按数组存放的满二叉树是堆,那么堆顶元素一定是堆中所有元素中键值最小的元素。
该性质可以根据堆二叉树的层数利用数学归纳法证明:
当堆二叉树的层数为1时,堆二叉树中只有一个元素r[0],因此堆顶元素是堆中所有元素中键值最小的元素。
假设对于所有的层数小于n的堆二叉树,堆顶元素的键值是堆中所有元素中键值最小的元素。当堆二叉树的层树为n时,堆顶元素r[0]的左右子树一定是层数小于n的堆二叉树,左子树的堆顶元素是r[1],右子树的堆顶元素是r[2]。根据假设可知r[1]是左子树中键值最小的元素,r[2]是右子树中键值最小的元素;再根据堆的定义:对于任意元素r[i]满足r[i].key≤r[2* i+1].key,r[i].key≤r[2*i+2].key,可知r[0].key≤r[1].key,r[0].key≤r[2].key,因此,堆顶元素r[0]一定是堆中所有元素中键值最小的元素。
堆的性质使得堆很适合从一组元素中选择最值元素(最大值或最小值)的场合,只要把所有可供选择的元素组织成一个堆,堆顶元素即为需要选择的最值元素。这也是我们采用堆数据结构选择优先数最小线程的原因。在LFYOS中,把所有处于就绪状态的线程根据线程的优先数组织成一个堆,堆顶元素即为优先级最高的线程,执行线程切换时,内核总是选择处于堆顶的线程占用处理机。
元素的健值发生变化时对堆的调整
假设在堆中存在n个元素r[0],r[1],… r[n-1],这些元素之间全部满足条件r[i].key≤r[2* i+1].key和r[i].key≤r[2*i+2].key,由于某种原因,其中某个元素r[k]的键值r[k].key改变,破坏了构成堆的条件,需要对元素序列进行相应的调整,把元素序列恢复为一个堆。
元素r[k]的键值r[k].key改变有两种可能:键值r[k].key减小或键值r[k].key增大。在堆中,元素r[k]的键值r[k].key大于等于其双亲的键值r[INT((k-1)/2)].key,小于等于左右孩子的键值r[2* i+1].key和r[2*i+2].key,如果键值r[k].key减小,其键值仍然小于等于左右孩子的键值,但可能由于小于其双亲的键值而破坏了构成堆的条件。通过在序列中把r[k]和其双亲结点相互交换,在二叉树中把r[k]向上移动,即可把序列调整为一个堆。
下面讨论如果某个元素r[k]其键值r[k].key增大后,如何对堆进行调整。
假设在堆中,元素r[k]的键值r[k].key大于等于其双亲的键值r[INT((k-1)/2)].key,小于等于左右孩子的键值r[2* i+1].key和r[2*i+2].key,如果键值r[k].key增大,其键值仍然大于其双亲接点的键值,但可能由于大于其左右孩子的键值而破坏了构成堆的条件。通过在序列中把r[k]和左右孩子结点中键值较小的结点相互交换,在二叉树中把r[k]向下移动,即可把序列调整为一个堆。
堆调整算法的时间复杂度
无论某个堆中元素的键值是增大还是减小,堆调整算法执行结点交换的次数都是很少的。假设在一个堆中存在n个元素,则堆二叉树的层数不超过1+Log 2n,当堆中某一元素的键值增大时,该元素在堆中向下移动;当堆中某一元素的键值减小时,该元素在堆中向上移动。由于堆二叉树的层数不超过1+Log2n,因此,执行堆调整算法时,结点交换的次数不超过Log2n,也就是说,堆调整算法的时间复杂度仅仅为O(Log2n)。显然,堆调整算法的时间复杂度很低,这也是我们选择堆数据结构来选择优先级最高线程的原因。
向堆中加入一个元素,从堆中删除一个元素
向堆中加入一个元素时,把元素放在序列的最后,然后执行键值减小的上移调整;从堆中删除一个元素时,首先把被删除的元素和最后一个元素交换,把元素从序列的最后删除,再根据删除元素的键值和交换元素的键值大小执行上移或下移调整。如果删除元素的键值小于交换元素的键值,执行下移调整;如果删除元素的键值大于交换元素的键值,执行上移调整。
在系统中设置一空闲处理机栈,所有空闲的处理机记录在空闲处理机栈中,当某一处理机空闲时,把处理机号压入栈中,如果有线程需要运行,从栈中弹出一个处理机分配给线程使用。当就绪线程堆非空时,所有处理机都已分配给了线程,空闲处理机栈一定是空的。
在系统中设置一处理机状态数组,每个处理机对应数组中的一个元素。在数组中记录着各个处理机需要切换的线程,在大多数情况下,当前运行的线程和需要切换的线程二者相同,表示不需要执行处理机切换,如果二者不同,需要申请处理机中断,请求处理机执行处理机切换,切换另一线程占用处理机。
运行线程堆和就绪线程堆
在虚拟地址基于文件的操作系统中,设置了两个堆数据结构:运行线程堆和就绪线程堆。所有正在处理机上运行的线程记录在运行线程堆中;所有等待到处理机上去运行的线程记录在就绪线程堆中。运行线程堆中线程的个数最多不超过系统支持的处理机数。
系统按照各个线程的优先数对两个堆实施管理。在就绪线程堆中,堆顶元素是优先数最小优先级最高的线程,如果需要选择一个就绪线程占用处理机,选择堆顶元素对应的线程;在运行线程堆中,堆顶元素是优先数最大优先级最低的线程,如果需要选择一个运行线程放弃处理机,也选择堆顶元素对应的线程。由于线程优先数越小,优先极越高,优先数越大,优先极越低,因此,运行线程堆是一个最大化堆。
由于系统选择优先级最高的线程占用处理机运行,一般情况下,就绪线程堆堆顶元素对应线程的优先数大于运行线程堆堆顶元素对应线程的优先数。如果由于某些原因线程的优先数发生了变化(例如,线程改变了优先数、处于运行线程堆中线程睡眠、线程终止退出等),就绪线程堆堆顶元素对应线程的优先数,可能小于运行线程堆堆顶元素对应线程的优先数,表示系统中某个处于就绪态线程的优先级高于另一个处于运行态线程的优先级,需要系统执行处理机切换,使运行线程堆堆顶元素对应线程放弃处理机,就绪线程堆堆顶元素对应线程占用处理机。
向运行线程堆或就绪线程堆插入线程的算法
如果存在空闲处理机,从空闲处理机栈弹出一个处理机号分配给线程,把分配的处理机号记录在线程的控制块中,在处理机状态数组中记录该处理机需要切换的线程,直接把线程加入到运行线程堆中,并请求处理机执行处理机切换。
如果不存在空闲处理机,比较需要插入的线程优先级和运行线程堆堆顶元素对应的线程的优先级,如果需要插入线程的优先级不高于运行线程堆堆顶元素对应线程的优先级,直接把新创建的线程加入到就绪线程堆中。
如果需要插入线程的优先级高于运行线程堆堆顶元素对应线程的优先级,把需要插入的线程和运行线程堆堆顶元素相互交换,对运行线程堆执行下移调整,把原运行线程堆堆顶元素对应的线程加入到就绪线程堆中;同时,在处理机状态数组中对应处理机元素中,把需要切换线程域设置为新插入线程的线程标识符,请求处理机执行处理机切换。
从运行线程堆或就绪线程堆删除线程的算法
如果线程处于就绪线程堆中,直接从就绪线程堆中删除。
如果线程处于运行线程堆中,就绪线程堆非空,把删除的线程和就绪线程堆堆顶元素对应的线程执行交换,对运行线程堆执行下移调整,再从就绪线程堆中把线程删除;同时,在处理机状态数组对应处理机的元素中,把需要切换线程域设置为原就绪线程堆堆顶元素对应的线程,并请求处理机执行处理机切换。
如果线程处于运行线程堆中,就绪线程堆为空,直接从运行线程堆中删除;同时,在处理机状态数组对应处理机元素中,把需要切换线程域设置为空。把处理机号压入空闲处理机栈中,并请求处理机执行处理机切换,处理机切换为空闲状态。
向运行线程堆或就绪线程堆中插入线程:线程对信号量执行V操作时,可能需要唤醒线程,调用本功能向运行线程堆或就绪线程堆插入线程。
从运行线程堆或就绪线程堆删除线程:线程对信号量执行P操作时,可能需要使线程睡眠,调用本功能从运行线程堆或就绪线程堆删除线程。
线程优先数调整:改变线程优先数时调用本功能,首先从运行线程堆或就绪线程堆删除线程,修改线程优先数后再插入到运行线程堆或就绪线程堆中。
处理机切换响应:处理机管理部件仅仅选择哪些线程应该在处理机上运行,如果需要某个处理机执行处理机切换,通过中断网络向处理机申请中断,处理机响应中断,触发中断服务程序执行处理机切换的操作,并调用本功能通知处理机管理部件已完成处理机切换。
信号量(semaphore)是E. W. Dijkstra在1965年提出的一种用于实现进程之间同步和互斥的方法。一个信号量的值可以为0,表示没有积累下来的唤醒操作,也没有在信号量上睡眠的进程;或者为正值,表示有一个或多个被积累下来的唤醒操作;或者为负值,其绝对值为在信号量上睡眠的进程数。
信号量管理部件提供四个功能调用:
·申请一个信号量
·释放一个信号量
·对信号量执行P操作
·对信号量执行V操作
在LFYOS中,线程和进程的关系是一种临时性的关系,线程并非永久性地属于某一个进程,通过线程迁移调用一个线程可以从一个进程进入另一个进程,执行被调用进程相应的功能,在被调用进程中执行完毕后,通过线程返回调用线程退回到原进程中运行。通过线程迁移调用和线程返回调用,线程实现了在不同进程之间的迁移。
通过线程迁移调用,一个线程进入另一个进程时,在新进程中,只能从进程初始执行点开始执行。通过在进程初始执行点处放置一定的检查程序,可以防止恶意的线程对进程的破坏,保证线程调用的安全性。
同样,当一个线程从一个进程返回到原来的进程时,系统也要保证线程返回到正确的执行点,防止恶意的特洛伊木马造成的破坏,为此,在线程控制块中专门设置了线程返回控制块栈。当一个线程执行线程迁移调用,从一个进程进入另一个进程时,系统保存线程返回调用时应该恢复的返回地址、进程标识符等信息;当线程执行线程返回调用从一个进程返回到原来的进程时,根据以前保存的线程返回地址,恢复线程在原进程环境中的运行。通过保存线程返回地址,防止服务进程对调用进程恶意的破坏,保证线程返回的安全性。
线程迁移部件提供的功能调用及实现
线程迁移部件提供了两个功能调用:线程迁移调用和线程返回调用。
线程迁移部件分下面三步执行线程迁移调用:
·判断目标进程是否是一个合法的进程,如果非法直接返回出错;否则继续向下执行。
·如果需要把当前应该保存的信息压入线程返回控制块栈中,执行压栈操作,如果压栈操作溢出,直接返回出错信息;如果不需要执行压栈操作或需要执行压栈操作且压栈操作没有溢出,继续向下执行。
·最后把线程的当前所属进程设置为目标进程,根据目标进程的初始执行点,在目标进程中开始执行。
线程迁移部件分下面步执行线程返回调用:
从线程返回控制块栈中弹出应该恢复的信息。
如果不能弹出应该恢复的信息,线程终止,否则根据弹出的信息恢复线程在原进程中的运行。
中断服务程序是和硬件平台密切相关的,对于外部设备中断,中断服务程序根据中断对应的信号量执行对信号量的V操作;对于处理机之间的中断,中断服务程序调用处理机切换部件提供的功能,执行处理机切换操作。
处理机切换部件首先通过处理机切换响应调用,通知就绪线程管理部件执行了处理机切换,然后调用硬件处理机切换部件执行具体的切换操作。
线程获得处理机后,首先判断是否需要执行强制返回,如果需要,执行线程返回调用返回上一级进程,否则恢复线程在当前进程中的运行。
在内核中设置一进程控制块数组,数据中每一个元素对应一个进程,存放着对应进程的各项信息,包括以下信息:
·进程的初始执行点
·创建线程的最高优先级
·进程的Capability校验
·操作该进程的互斥信号量
·允许进程申请的最大信号量数和当前进程申请的信号量数
·允许进程创建的线程数和当前进程创建的线程数
·记录进程创建线程的线程环链首指针
·记录进程申请信号量的信号量环链首指针
一个进程创建的所有线程链成一个线程环链,并把链首指针存放在进程控制块中。创建线程时,向线程环链中插入一个线程;终止线程时,从线程环链中把相应的线程删除。根据线程环链可以确定一个进程创建的所有线程。
一个进程申请的所有信号量也链成一个信号量环链,并把链首指针存放在进程控制块中。申请信号量时,向信号量环链中插入一个信号量;释放信号量时,从信号量环链中把相应的信号量删除。根据信号量环链可以确定一个进程申请的所有信号量。
线程控制块数组
为了管理线程,系统设置一线程控制块数组,数组中每个元素对应一个线程,线程控制块中存放着以下信息:
·线程的状态(睡眠态、就绪态、运行态)以及线程睡眠的信号量(睡眠态)、在就绪线程堆中的位置(就绪态)或在运行线程堆中的位置(运行态)
·线程的优先数
·线程当前所在的进程、线程资源所属进程及线程环链前后指针
·线程返回控制块栈和栈顶指针
·线程的Capability校验
根据调用参数,重新设置创建线程的最高优先级、允许进程申请的最大信号量数、和允许进程创建的线程数,返回创建线程的最高优先级、允许进程申请的最大信号量数和当前进程申请的信号量数、允许进程创建的线程数和当前进程创建的线程数。
只有拥有系统的Capability校验,才能够分配处理机资源,只有拥有系统的Capability校验或进程的Capability校验,才能够查询处理机资源。
线程的创建
在内核中设置一个空闲线程信号量,并设置其初值为零,系统启动时,内核为线程控制块数组中每一个元素创建所有线程,这些线程创建后立即对系统中的空闲线程信号量执行P操作而在空闲线程信号量上睡眠。
执行线程的创建功能调用时,对空闲线程信号量执行一次V操作,唤醒一个睡眠在空闲信号量上的线程,把唤醒的线程控制块链入到进程创建线程的线程环链中,对当前进程创建的线程数执行加一操作,最后初始化线程返回控制块栈,进入进程运行。
线程在进程中运行完毕后,执行线程返回调用,当不能返回上一级进程时,执行线程终止操作,首先对当前进程创建的线程数执行减一操作,然后把线程控制块从到进程创建线程的线程环链中删除,最后线程对空闲线程信号量执行一次P操作重新睡眠,成为空闲线程。
只有拥有系统的Capability校验或进程的Capability校验,才能够为进程创建线程。
设置和查询线程属性
如果设置线程属性,首先在线程控制块中设置新的线程优先数和线程的Capability校验,然后调用就绪线程管理部件执行线程优先数调整,最后返回新的线程属性。
只有拥有线程所属进程的Capability校验和当前所属进程的Capability校验,或者只有拥有系统的Capability校验,才能够设置和查询线程属性。
分配和释放信号量
执行申请操作时,首先检查该进程是否还允许申请信号量,从空闲信号量环中取出一个信号量,插入到进程的信号量环链中;与此相反,执行释放信号量时,把信号量从进程的信号量环链取下信号量,插入到空闲信号量环中。
设置文件capability校验、检测线程在执行进程中是否能够访问文件
该操作的实现很简单,直接把文件的capability保存在内核相应数据结构中。
页框控制块表
页框控制块表用于管理页框,每个页框对应表中的一项,根据页框的使用情况,页框控制块表中的表项链入空闲页框队列或页框文件队列中。在页框控制块中存放着页框的有关信息,其中包括:页框的状态(空闲、正在读、未修改、已修改、正在写),页框中的数据在外存文件中的偏移量,页框中对应的地址映射信息等。
空闲页框队列
在每个存储体中设置一个空闲页框队列,空闲页框队列的结构如图4-5所示,存储体中所有没有被分配的页框都被链入空闲页框队列中,并用空闲队列队首指向空闲页框队列中的第一个页框。系统按栈式队列的方式管理空闲页框队列,每次分配一个页框,系统把队列首部的页框分配给存储域,并把分配的页框从队列首部删除;每次释放一个页框时,系统把释放的页框插入队列首部。
文件窗口控制块表
文件窗口控制块表用于实施对文件窗口的管理,每个在内存中打开的文件窗口对应表中的一项。如果文件窗口已经打开,打开文件窗口在文件窗口控制块表中对应的表项链入某个存储域控制块的打开文件窗口队列,否则链入空闲文件窗口队列。在打开文件窗口控制块中存放着文件窗口的有关信息,包括:文件服务器的标识、对应外存上文件的标识、访问文件的权限、文件窗口的大小和在外存文件中的位置、当前使用的页框、访问文件的Capability校验、文件窗口控制块所属的存储域等。
空闲文件窗口队列
和空闲页框队列相似,在每个存储体中设置一个空闲文件窗口队列,空闲文件窗口队列的结构如图4-6所示,存储体中所有没有被分配的文件窗口控制块都被链入空闲文件窗口队列中,并用空闲队列队队首指向空闲文件窗口队列中的第一个文件窗口控制块。系统按栈式队列的方式管理空闲文件窗口队列,每次分配一个文件窗口控制块,系统把队列首部的文件窗口控制块分配给存储域,并把分配的文件控制窗口块从队列首部删除;每次释放一个文件窗口控制块时,系统把释放的文件窗口控制块插入队列首部。
存储域控制块表
在系统中设置存储域的目的是为了实施对存储资源的管理。通过为存储域分配和回收存储资源,系统防止某些存储域占用过多的资源。存储资源包括:内存打开文件窗口和页框。
在每个存储体中设置一个存储域控制块表,存储域控制块表的结构如图4-7所示,表中每一项对应一个存储域,其中的信息包括:允许打开的最大文件窗口数、当前打开的文件窗口数、允许使用的最大页框数、当前使用的页框数、使用了多少页框数后启动写操作把数据反写回文件、当前多少个页框在执行读操作、当前多少个页框在执行写操作、当前打开文件窗口队列的首指针、当前使用的页框队列的首指针、存储域Capability的校验信息。
一个存储域所有的打开文件窗口控制块链成一个双向的环形队列:打开文件窗口队列,并把队首指针和存储域当前打开的文件窗口数存放在存储域控制块表的表项中,通过比较允许存储域打开的文件窗口数和当前该存储域打开的文件窗口数,系统可以判断是否还能够为该存储域打开文件窗口,防止为某一个或某几个存储域打开过多的文件窗口;通过存储域打开文件窗口队列的首指针,系统可以查找到存储域打开所有文件窗口的文件窗口控制块。
在LFYOS中,内存的作用是缓冲文件中的数据,一个页框存放着外存上文件中的一段数据。每个文件使用的所有页框的控制块,也链成一个环形队列:文件页框队列,并把队首指针存放在文件控制块中。根据文件页框队列,系统可以找到为文件分配的所有页框。
一个存储域使用的所有处于未修改状态和已修改状态的页框也链成一个环形队列:页框缓冲队列,并把队首指针存储在存储域控制块表中。系统根据线程对页框的访问,按LRU算法维护页框缓冲队列。每次向队列中插入一个页框,系统把该页框插入在链首;每次需要从队列中淘汰页框,系统选择淘汰队尾的页框;通过把页框从队列中删除后插入到队首,系统实现了维护页框队列的LRU算法。
执行打开文件窗口调用时,首先比较允许该存储域打开的最大文件窗口数和当前该存储域打开的文件窗口数,如果存储域已经打开了允许打开的所有文件窗口,直接返回出错;否则从空闲文件窗口队列中移出一个文件窗口控制块,插入到存储域的文件窗口队列中,对存储域当前打开的文件窗口数执行加一操作,设置文件窗口控制块中的各个域(文件服务器的标识,对应的外存上文件的标识,访问文件的权限,文件窗口的大小和在外存文件中的位置,访问文件的Capability校验和访问文件窗口的Capability校验,文件窗口控制块所属的存储域)。文件页框队列首指针设置为空。
执行关闭文件窗口调用时,首先判断当前使用的页框数是否为零,如果为零把文件窗口控制块从存储域的文件窗口队列中删除,插入到空闲文件窗口队列中,对存储域当前打开的文件窗口数执行减一操作。
如果大于零,首先对文件窗口执行Flush操作,把内存中已经修改的数据写回到文件中,然后执行从文件页框队列的链首开始,重复执行以下操作:
·如果页框的状态为正在读或如果页框的状态为正在写,睡眠等待读写完成。唤醒后返回重新执行关闭文件操作。
·如果页框的状态为未修改,把页框从地址映射部件中删除,把页框从文件页框队列和页框缓冲队列中删除,插入到空闲页框队列中,对存储域的当前使用的页框数执行减一操作。
·如果页框的状态为已修改,重新对文件窗口执行Flush操作,返回重新执行文件关闭操作。
在LFYOS中,执行打开文件窗口操作,需要拥有存储域Capability的校验或者拥有系统Capability的校验信息;执行关闭文件窗口操作,需要拥有访问文件窗口的Capability校验或者拥有系统Capability的校验信息。
把文件数据反写回文件Flush和访问文件测试Touch
执行Flush调用时,首先从文件窗口控制块取出文件页框队列的首指针,然后按从队列首部到队列尾部顺序,检查每个页框的状态,对于处于已修改状态的页框,把页框控制块从页框缓冲队列删除,设置其状态为正在写状态,调用文件服务器执行写文件操作。
执行访问文件测试Touch调用时,首先查询页表(和计算机系统的体系结构相关的,不同的地址映射机制采用不同的查询方法),如果对应的页表项在页表中存在,说明该页已经从文件服务器中读入内存,根据页表项中的信息中的信息,可以确定对应的页框控制块,把页框控制块移到页框缓冲队列的队首,返回访问文件测试Touch调用的各项信息。
如果对应的页表项在页表中不存在,那么需要从文件服务器中读入文件数据。首先,比较存储域中的两个域:允许使用的最大页框数和当前使用的页框数,判断是否允许该存储域继续使用空闲页框,如果没有足够的空闲页框,执行页框淘汰算法,把内存中的文件数据反写回文件中,释放占用的页框。页框淘汰算法执行完毕后存储域也就拥有了足够的空闲页框。
如果存在足够的空闲页框,那么从空闲页框队列的队首删除一个页框控制块,把它插入到文件页框队列,但并不插入页框缓冲队列,初始化页框控制块,状态设置为正在读,通过线程迁移调用启动文件服务器执行读文件操作。读文件操作完成后,返回访问文件测试Touch调用的各项信息。
页框淘汰算法重复执行以下操作:
根据页框缓冲队列尾部的页框控制块的状态,选择执行:
·如果为未修改状态:把该页框控制块从文件页框队列和页框缓冲队列删除,插入到空闲页框队列中,同时修改页表中该页框控制块对应的信息,对存储域控制块中当前使用的页框数执行减一操作。
·如果为已修改状态:把页框控制块页框缓冲队列删除,设置其状态为正在写状态,调用文件服务器执行写文件操作。
·其它状态出错。
页框淘汰算法重复执行,直到以下条件中成立为止:存储域控制块中的当前多少个页框在执行写操作大于等于最多同时写多少个页框,或者当前使用的页框数小于使用了多少页框数后,启动写操作把数据反写回文件。
执行把文件数据反写回文件Flush调用和访问文件测试Touch调用,需要拥有访问文件窗口的Capability校验或者拥有系统Capability的校验信息。
设置文件数据已经被修改
在LFYOS中,文件数据读入页框以后页框处于未修改状态,表示淘汰该页框时不需要写回该页框中的数据,如果线程修改了页框中的数据,操作系统的内核将使线程迁移至内存管理器,调用内存管理器中的设置文件数据已经被修改功能,该功能把页框的状态修改为已修改状态,因此当执行页框淘汰时,将把该页的内存写回文件中。
存储管理器执行该功能时,首先检查参数,根据调用参数调用search_physical_block()函数确定需要设置修改的页框,把该页框的状态改为已修改状态。
分配和查询存储资源
在LFYOS中,存储资源包括文件窗口和页框。执行分配存储资源调用时,设置存储域控制块中的允许该存储域打开的最大文件窗口数和允许该存储域使用的最大页框数;执行查询存储资源调用时,返回允许该存储域打开的最大文件窗口数、当前该存储域打开的文件窗口数、允许该存储域使用的最大页框数、当前该存储域使用的页框数。
在LFYOS中,只有拥有系统Capability的校验,才能为存储域分配资源;只要拥有存储域Capability的校验或者拥有系统Capability的校验信息,即可查询存储域的存储资源。
存储体的初始化
调用该功能实现存储体的初始化,初始化内存管理器中的各个数据结构。
1.虚拟地址空间和文件窗口的对应关系
在我们当前的实现中,在4G的虚拟地址空间(线性地址空间)中,0-3G分别对应线程的六个已经打开的文件窗口,高端的一个G由操作系统使用。如果线程运行于核心态(0态),线程也可以访问高端的数据;如果线程运行于用户态(3态),线程仅仅可以访问高端的操作系统代码(读取或执行),不能访问操作系统数据。
LDT没有使用,在GDT中,仅仅包含20项内容。描述符0未用,描述符0x08指向任务的tss,描述符0x10、0x18直接映射到4G的线性地址空间,只能在核心态下使用,描述符0x10用于读取和执行,描述符0x18用于读取和修改。
描述符0x23、0x2b及其以下的描述符由运行于用户态的应用程序使用,描述符0x23映射区域从线性地址0至操作系统代码的终止,因此通过描述符0x23,应用程序可以读取和执行操作系统内核代码,但是不能修改操作系统内核代码,也不能访问操作系统数据。描述符0x2b映射区域从线性地址0开始,长度为3G+8K,低端的3G对应六个文件窗口,每个文件最大长度窗口为512M,3G-3G+4K的页面用于向操作系统内核传递参数。当线程调用内核提供的系统功能调用时,如果需要传递的参数很多,无法全部放在寄存器,则把其它的参数放在3G-3G+4K的页面中。3G+4K-3G+8K的页面由文件系统使用,当内存管理器通过线程迁移调用请求文件系统执行文件读写时,文件系统不需要知道数据的具体存放位置,只需从区间3G+4K-3G+8K读或向区间3G+4K-3G+8K写即可。
描述符0x033、0x03b,描述符0x043、0x04b,描述符0x053、0x05b,描述符0x063、0x06b,描述符0x073、0x07b,描述符0x083、0x08b分别对应六个文件窗口,描述符0x0X3用于读取和执行,描述符0x0Xb用于读取和修改。
描述符0x093、描述符0x09b分别映射至线性地址3G-3G+8K的两个页面。
2.和文件有关的几个概念
@存储体:在虚地址空间基于文件操作系统中,把物理地址空间划分为几个存储体,每个存储体由一个内存管理器负责管理和维护(内存管理器是一个在核心态下运行的程序),应用程序打开文件窗口时需要确定在哪个存储体中打开文件窗口,当发生存储访问失效异常时,操作系统系统内核利用存储体信息确定线程迁移到哪个进程。
@系统文件窗口:内存管理器管理和维护的文件窗口。当打开文件时,内存管理器建立系统文件窗口。
@线程文件窗口:当线程运行时,对线程而言,GDT中各个段描述符对应的文件窗口
@进程文件窗口:当线程运行时,对进程而言,GDT中各个段描述符对应的文件窗口。当线程访问地址空间中的数据时,如果当前进程设定了进程文件窗口,访问进程文件窗口中的数据,否则访问线程文件窗口中的数据。
3.和文件窗口有关的几个系统功能调用
@设置进程文件窗口的capability校验(21号功能):该系统功能调用用来建立进程文件窗口和系统文件窗口的对应关系,同时设置进程文件窗口的capability校验。
@设置线程文件窗口的capability校验(22号功能):该系统功能调用用来建立线程文件窗口和系统文件窗口的对应关系,同时设置线程文件窗口的capability校验。
@检查线程是否可以访问某个文件窗口:只有进程文件窗口的capability校验和系统文件窗口的capability校验相同,或者线程文件窗口的capability校验和系统文件窗口的capability校验相同,应用程序才能访问相应的系统文件窗口。
4.进程文件窗口和系统文件窗口的对应关系,线程文件窗口和系统文件窗口的对应关系:当应用程序在系统中运行时,如果访问虚拟地址空间对应的文件窗口,系统首先判断相应的进程文件窗口和系统文件窗口的对应关系,判断二者的capability是否相同,如果相同则认为应用程序访问进程文件窗口对应的系统文件窗口,否则判断相应的线程文件窗口和系统文件窗口的对应关系,判断二者的capability是否相同,如果相同则认为应用程序访问线程文件窗口对应的系统文件窗口,否则说明应用程序不能访问虚拟地址空间对应的文件窗口。
1.线程和文件之间不存在任何确定的对应关系:数据存储在文件中,只要不影响安全性,任何文件可以被任何线程访问,任何线程也都可以计算任何文件中的数据。
2.线程和进程之间也不存在确定的对应关系:线程可以在不同的进程之间迁移,在不同时刻线程以不同进程的身份申请和使用资源。由于所有应用程序在同一个文件空间中运行,线程迁移时,仅仅需要执行CPU现场的迁移,不需要执行程序代码、数据和堆栈等的迁移。
3.文件和进程之间也不存在确定的对应关系:文件属于文件服务器,是文件服务器维护的一个对象,进程是系统进行资源管理的对象,是操作系统内核实施资源管理的抽象,是系统资源的持有者。
总之,在系统内存中至少需要常驻以下三个部件,其它的内容可以在系统运行时动态地安装和卸载,可以在系统运行时动态地换入换出:
1.内核
2.内存管理器
3.至少一个具体的文件服务器或者一个通信服务器
void call_kernel(struct thread_environment *env);
其中,数据结构struct thread_environment 的定义为:
struct thread_environment{
int system_call,system_call_arg1; /*ax,bx*/
int system_call_arg2,system_call_arg3; /*cx,dx*/
int system_call_arg4,system_call_arg5; /*si,di*/
int system_call_arg6,system_call_arg7; /*bp,r7*/
run_point point; /*ip,cs,flag,sp,ss */
int (*function)(int thread_id,struct thread *t,
struct return_stack *rt,struct thread_environment *env,
union kernel_call_parameter *parameter);
union kernel_call_parameter parameter;
};
#define ax system_call
#define bx system_call_arg1
#define cx system_call_arg2
#define dx system_call_arg3
#define si system_call_arg4
#define di system_call_arg5
#define bp system_call_arg6
#define r7 system_call_arg7
typedef struct thread_environment REG;
函数void call_kernel(struct thread_environment *env)的定义为(完全用内嵌汇编实现):
extern void call_kernel(struct thread_environment *env);
2. 通过寄存器传递参数,也通过内核参数区传递参数的例子
#include”../include/os.h”
{
REG r;
union system_call_parameter *par;
par=get_kernel_parameter();
r.ax=22;
r.bx=0;
r.cx=0;
r.dx=0;
r.si=512;
r.di=READ_WRITE;
RESET_CAPABILITY(par->capability.capability_1); /*设置参数区中的参数*/
call_kernel(&r);
}
1.功能:实现线程从一个进程迁移到另一个进程,线程在目标进程中从进程的初始执行点开始运行。通过线程返回系统功能调用,线程可以返回原来的进程中。
2.格式:ax.寄存器的值为零,bx寄存器的绝对值为进程ID。
如果当前的进程为管程,bx>0表示线程离开当前进程进入目标进程后,允许其它线程进入当前进程;bx <0表示即使线程迁移离开当前进程进入目标进程,也不允许其它线程进入当前进程,因此当线程从目标进程返回后,可以保证数据的一致性;bx=0为非法。
3.返回值:如果ax.寄存器的值小于零,表示失败;否则,ax.寄存器的值为同时返回了几次,因此如果大于零也表示失败;bx.寄存器的值为TRUE表示正常返回,为FALSE表示异常返回(例如,线程在目标进程中运行时发生了不可恢复的异常而返回)。
4.例:
#include”../include/os.h”
{
REG r;
r.ax=0;
r.bx=2;
/*迁移至2号进程,从其初始执行点开始运行线程离开当前进程进入目标进程后,允许其它线程进入当前进程。*/
call_kernel(&r);
}
1号功能
1.功能:实现线程从一个进程迁移到另一个进程,线程在目标进程中从进程的初始执行点开始运行,和0号功能不同的是:线程不能再次返回到原来的进程中。
2.格式:ax.寄存器的值为1,bx寄存器的绝对值为进程ID。
如果当前的进程为管程,bx>0表示线程离开当前进程进入目标进程后,允许其它线程进入当前进程;bx <0表示即使线程迁移离开当前进程进入目标进程,也不允许其它线程进入当前进程。只有对当前进程的管程信号量执行V操作,才能允许其它线程进入当前进程;bx=0为非法。
3.返回值:如果执行成功,线程不可能返回,返回则表示失败。
4.例:
#include”../include/os.h”
{
REG r;
r.ax=1;
r.bx=(-2);
/*迁移至2号进程,从其初始执行点开始运行线程离开当前进程进入目标进程后,不允许其它线程进入当前进程。*/
call_kernel(&r);
}
2号功能
1.功能:线程迁移(修改线程可以访问的物理页框)。
2.格式:ax.寄存器的值为2
cx寄存器存放存储体ID,dx寄存器存放参数页框ID,si寄存器存放线程可以直接访问的物理页框ID,其它参数和格式同0号功能。
3.返回值:同0号功能
3号功能
1.功能:实现线程从当前的进程返回到原来的进程,如果线程已经没有进程可以返回,则线程终止退出。
2.格式:ax.寄存器的值为3,bx寄存器的值为TRUE表示正常返回,否则为异常返回。如果线程由于异常进入当前的进程,异常返回表示引起线程运行异常的原因没有解决,因此内核将使线程再次返回,直至线程可以正常运行为止。这就是为什么执行一次线程返回调用会引起多次线程返回的原因,而0号功能的返回值当其大于等于零时即为线程返回的次数。
3.返回值:不可能返回
4.例:
#include”../include/os.h”
{
REG r;
r.ax=3;
r.bx=TRUE;
call_kernel(&r);
}
在传统操作系统中,线程进入内核时,将从一个固定的地址开始执行,与此类似,在虚地址空间基于文件操作系统中,当线程从一个进程进入另一个进程时,线程从目标进程的初始执行点开始执行。通过设置进程的初始执行点,可以指定线程在进程中开始执行的地址。
当线程在目标进程中开始执行时,ax寄存器存放当前线程返回栈的栈顶指针,通过该参数,线程可以判断是否可以再次执行线程迁移,迁移到其它的进程。
bx寄存器的绝对值存放着当前线程的ID,也就是当前线程的标志信号量ID,该信号量和当前的进程具有相同的capability校验。如果bx>0,表示线程通过调用线程迁移系统功能调用进入当前进程,如果bx<0,表示线程并没有调用线程迁移系统功能调用,但是线程执行时产生了异常,内核中的异常处理程序使线程迁移到当前的进程,请求进程执行异常处理。
如果当前的进程成功解决了异常(例如,内存管理器成功把页面调入内存从而解决了存储访问失效异常),执行线程返回时,bx中的值为TRUE,线程返回到原来的进程中继续运行;如果当前的进程无法成功解决了异常,表示线程在原来的进程中已无法运行,执行线程返回时,bx中的值为FALSE,內核将执行多次线程返回,直到线程能够正确执行为止。
通过线程迁移和线程返回处理异常,即使线程运行时出现不能解决的异常,仅仅使线程返回到原来的进程中,不会使线程或进程完全退出,解决了UNIX系统中的core dump问题。
4号功能
1.功能:对信号量执行P、V操作,实现线程之间的同步互斥。
2.格式:ax.寄存器的值为4
bx中为执行V操作的信号量ID的绝对值,如果bx=0,不执行V操作;如果bx<0,则执行V操作以前对该信号量复位。cx中为执行V操作时信号量的增加值,如果cx中的值大于等于零,把信号增加相应的数值,如果必要唤醒在信号量睡眠的线程;如果cx中的值小于零,则把在信号量睡眠的线程全部唤醒。
dx中为执行P操作的信号量ID的绝对值,如果dx=0,不执行P操作;如果dx<0,则执行P操作以前对该信号量复位。si寄存器中的值指定当信号量的值小于0时线程是否睡眠,si寄存器中的值为TRUE,则睡眠,如果为FALSE,不睡眠,di寄存器中的值指定当线程睡眠时,是否允许其它线程进入进程,如果di寄存器中的值为TRUE,允许其它线程进入进程;如果di寄存器中的值为FALSE,不允许其它线程进入进程。
在参数区中,get_kernel_parameter()->capability.capability_1存放V操作信号量的capability校验,get_kernel_parameter()->capability.capability_2放P作信号量的capability校验。
3.返回值:bx中存放V操作的执行结果,dx中存放P操作的执行结果,大于等于表示成功,小于零表示失败。
4.例1:对信号量执行V操作
#include”../include/os.h”
{
REG r;
union system_call_parameter *par;
par= get_kernel_parameter(); /*取参数区地址*/
COPY_CAPABILITY((*(memory_body->capability)),(par->capability.capability_1)); /*设置信号量的capability校验*/
r.system_call=4;
r.system_call_arg1=semaphore; /*信号量ID*/
r.system_call_arg2=1; /*增加值为1*/
r.system_call_arg3=0; /*不执行P操作*/
call_kernel(&r);
}
5.例2:对信号量执行P操作
#include”../include/os.h”
{
REG r;
union system_call_parameter *par;
par= get_kernel_parameter(); /*取参数区地址*/
COPY_CAPABILITY((*(memory_body->capability)),(par->capability.capability_2)); /*设置信号量的capability校验*/
r.system_call=4;
r.system_call_arg1=0; /*不执行V操作*/
r.system_call_arg3= semaphore;; /*信号量ID*/
r.system_call_arg4=TRUE; /*如果需要,线程睡眠*/
r.system_call_arg5=TRUE; /*允许其它线程进入进程*/
call_kernel(&r);
}
6.例3:对信号量执行V操作和P操作
#include”../include/os.h”
{
REG r;
union system_call_parameter *par;
par= get_kernel_parameter(); /*取参数区地址*/
COPY_CAPABILITY((*(memory_body->capability)),(par->capability.capability_1)); /*设置V操作信号量的capability校验*/
COPY_CAPABILITY((*(memory_body->capability)),(par->capability.capability_2)); /*设置P操作信号量的capability校验*/
r.system_call=4;
r.system_call_arg1=v_semaphore; /*V操作信号量ID*/
r.system_call_arg2=1; /*增加值为1*/
r.system_call_arg3= p_semaphore; /*P操作信号量ID*/
r.system_call_arg4=TRUE; /*如果需要,线程睡眠*/
r.system_call_arg5=TRUE; /*允许其它线程进入进程*/
call_kernel(&r);
}
#include”../include/os.h”
{
union system_call_parameter *par=get_kernel_parameter();
REG r;
r.system_call=5;
r.system_call_arg1=sem_id;
r.system_call_arg2=TRUE;
r.system_call_arg3=TRUE;
r.system_call_arg4=(-1);
get_current_time(&(par->set_semaphore_time.first_time));
TIME_INC(1,(par->set_semaphore_time.first_time));
SET_MINIMAL_STEP(par->set_semaphore_time.step_time);
RESET_CAPABILITY(par->set_semaphore_time.semaphore_capability);
call_kernel(&r);
}
#include”../include/os.h”
{
union system_call_parameter *par;
REG r;
par=get_kernel_parameter();
RESET_CAPABILITY(par->capability.capability_1);
r.ax=6;
r.bx=1;
r.cx=(int)test; /* this is the entry point where the new thread will execute */
r.dx=DEFAULT_SP; /*this is stack pointer for the new thread */
call_kernel(&r);
}
struct process{
run_point start_point; /*进程的初始执行点*/
int (*driver)(struct file_system_call_parameter *par);/*进程对应的驱动程序*/
int priority; /*进程的优先级*/
int semaphore; /*进程的管程信号量*/
int enter_thread_number,id;
/*进入进程的线程数,进程的ID*/
int max_thread_number,thread_number,thread_ring;
/*允许进程创建的最多线程数,当前创建的线程数*/
/* thread_ring;为内核使用的数据结构*/
int max_semaphore_number,semaphore_number,semaphore_ring;
/*允许进程申请的最多信号量数,当前申请的信号量数*/
/* semaphore_ring;为内核使用的数据结构*/
struct user_file_information file[USER_FILE_NUMBER];
/*进程的文件信息*/
struct capability capability;
/*进程的capability校验信息*/
}
11号功能
1.功能:查询进程的资源。
2.格式:ax.寄存器的值为11
bx寄存器存放进程ID
在参数区中,get_kernel_parameter()->process_attribute.capability存放capability校验。只有拥有系统的capability校验,或者进程的capability校验,才能查询进程的资源。
3.返回值:如果查询进程的资源成功,ax.寄存器的值大于等于零,同时在参数区get_kernel_parameter()-> process_attribute.process中存放进程的各项参数;如果分配失败,ax.寄存器的值小于零。
4.例:查询进程的资源和分配进程的资源
#include”../include/os.h”
{
REG r;
union system_call_parameter *par;
par=get_kernel_parameter();
RESET_CAPABILITY(par->process_attribute.capability);
RESET_CAPABILITY(par->process_attribute.process.capability);
r.ax=11;
r.bx=1;
call_kernel(&r);
par->process_attribute.process.max_thread_number=THREAD_NUMBER;
par->process_attribute.process.max_semaphore_number=SEMAPHORE_NUMBER;
RESET_CAPABILITY(par->process_attribute.capability);
RESET_CAPABILITY(par->process_attribute.process.capability);
par->process_attribute.process.start_point.ip=enter_file_system;
par->process_attribute.process.start_point.cs=0x23;
par->process_attribute.process.start_point.ds=0x2b;
par->process_attribute.process.start_point.ss=0x2b;
r.ax=10;
r.bx=1;
call_kernel(&r);
}
#include”../include/os.h”
{
REG r;
union system_call_parameter *par;
par=get_kernel_parameter();
RESET_CAPABILITY(par->capability.capability_1); /*进程capability校验*/
RESET_CAPABILITY(par->capability.capability_2); /*信号量的capability校验*/
r.ax=12;
r.bx=2; /*进程ID*/
r.cx=1; /*寄存器存放信号量初值*/
call_kernel(&r);
}
13号功能
1.功能:释放信号量
2.格式:ax.寄存器的值为13
bx寄存器存放信号量ID
在参数区中,get_kernel_parameter()->capability.capability_1存放信号量的capability校验。
3.返回值:如果释放信号量成功,ax.寄存器的值大于等于零;如果释放信号量失败,ax.寄存器的值小于零。
15号功能
1.功能:查询线程属性
2.格式:ax.寄存器的值为15
bx寄存器存放线程ID
在参数区中,get_kernel_parameter()->thread_attribute.current_process_capability存放线程当前所属进程的capability校验。只有拥有线程当前所属进程的capability校验,才能查询线程属性。
3.返回值:
如果bx寄存器存放线程ID大于零,表示查询指定线程的属性成功;如果查询线程属性失败,ax.寄存器的值小于零;如果查询线程属性成功,ax.寄存器的值大于等于零,同时各项属性参数保存在:
a)get_kernel_parameter()->thread_attribute.priority:线程优先级
b)get_kernel_parameter()->thread_attribute.return_stack_top:线程返回栈栈顶指针
c)get_kernel_parameter()->thread_attribute.process:线程所属进程
d)get_kernel_parameter()->thread_attribute.current_process:线程当前所属进程
e)get_kernel_parameter()->thread_attribute. exception_item.flag:存放线程是正常迁移到当前进程还是异常迁移到当前进程。
如果bx寄存器存放线程ID小于零,表示查询当前运行线程的属性,
a)ax.寄存器的值返回线程ID,也就是说线程对应的信号量。
b)bx.寄存器的值返回线程优先级
c)cx.寄存器的值返回线程返回栈栈顶指针
d)dx.寄存器的值返回线程所属进程
e)si.寄存器的值返回线程当前所属进程
di.寄存器的值返回存放线程是正常迁移到当前进程还是异常迁移到当前进程
#include”../include/os.h”
{
REG r;
r.ax=20;
r.bx=0; /*零号存储体*/
call_kernel(&r);
block_number=r.ax;
block_base=r.bx;
}
#include”../include/os.h”
{
REG r;
RESET_CAPABILITY(get_kernel_parameter()->capability.capability_1);
/*设置文件校验信息*/
r.ax=21;
r.bx=2; /*进程的文件窗口ID*/
r.cx=512; /*当发生地址映射失效异常时,同时建立地址映射的虚空间区域长度*/
r.dx=READ_ONLY;/* dx寄存器存放文件窗口访问权限*/
r.si=0;/*si寄存器存放该进程文件窗口对应的系统文件在哪个存储体*/
r.di=0;/*di寄存器存放该进程文件窗口对应的系统文件是存储体中的哪个文件窗口*/
call_kernel(&r);
}
22号功能
1.功能:设置线程文件的capability校验
2.格式:ax.寄存器的值为22
bx寄存器存放线程文件窗口ID
cx寄存器存放线程文件窗口map_length,也就是当发生地址映射失效异常时,同时建立地址映射的虚空间区域长度。
dx寄存器存放线程文件窗口访问权限
在参数区中,get_kernel_parameter()->capability.capability_1存放线程文件窗口的capability校验。
si寄存器存放该进程文件对应的系统文件窗口在哪个存储体
di寄存器存放该进程文件对应的系统文件窗口是存储体中的哪个系统文件窗口
3.返回值:
如果设置成功,则ax的返回值大于等于零
如果设置失败,则ax的返回值小于零
4.例:设置文件的进程capability校验
#include”../include/os.h”
{
REG r;
RESET_CAPABILITY(get_kernel_parameter()->capability.capability_1);
./*设置文件校验信息*/
r.ax=22;
r.bx=2; /*线程文件ID*/
r.cx=512; /*当发生地址映射失效异常时,同时建立地址映射的虚空间区域长度。*/
r.dx=READ_ONLY; /* dx寄存器存放文件访问权限*/
r.si=0; /*si寄存器存放该线程文件对应的系统文件在哪个存储体*/
r.di=0; /* di寄存器存放该线程文件对应的系统文件是存储体中的哪个文件*/
call_kernel(&r);
}
23号功能
1.功能:检查线程是否可以访问某个文件
2.格式:ax.寄存器的值为23
bx寄存器存放文件ID
3.返回值:
如果可以访问文件,则ax的返回值大于等于零,其值为该文件对应的系统文件是存储体中的哪个系统文件。
a)cx寄存器返回该文件对应的系统文件位于哪个存储体
b)dx寄存器返回该文件对应的系统文件是存储体中的哪个系统文件窗口
c)si寄存器返回当发生地址映射失效异常时,同时为多少个页面建立地址映射。
d)di寄存器返回文件访问权限
如果不可以访问文件,则ax的返回值小于零
4.例:打开文件窗口
#include"../include/os.h"
{
REG r;
union memory_call_parameter *par)
struct file *f;
par->open_file_window.file_window_id=fd;
f=&(par->open_file_window.file_window);
f->file.file.server_processor=0;
f->file.file.server_process=2;
f->file.file.file_handler=0;
f->file.proxy.processor=0;
f->file.proxy.process=((fd&0x01)==0)?2:2;
f->file.open_window_id=fd;
f->file.window_base_1=0;
f->file.window_base_2=0;
f->file.window_length=512*1024*1024;
f->file.memory_process=0;
f->file.right=READ_WRITE;
RESET_CAPABILITY(f->file.capability);
RESET_CAPABILITY(par->open_file_window.process_capability);
f->file.read_in_flag=TRUE;
f->file.swap_out_flag=TRUE;
r.ax=0;
r.bx=0;
call_memory(&r);
}
1号功能
1.功能:关闭文件窗口
2.格式:
ax寄存器存放存储体ID。
bx.寄存器的值为1,也就是关闭文件窗口的功能号。
在参数区中,get_memory_parameter()->close_file_window.file_window_id存放请求关闭的文件窗口ID。
在参数区中,get_memory_parameter()->par->close_file_window.file_capability存放文件窗口capability校验。
3.返回值:
如果关闭成功,则ax的返回值大于等于零。
如果关闭失败,则ax的返回值小于零。
4号功能
1.功能:把某个文件窗口的所有已经修改的物理页框中的数据写回文件中。
2.格式:
ax寄存器存放存储体ID。
bx.寄存器的值为4,也就是flush文件窗口的功能号。
在参数区中,get_memory_parameter()->flush_file_window.give_up_flag存放是否放弃标志,如果该标志为TRUE,则仅仅标志各个已经修改的物理页框中的数据已经写回文件中,但是不执行写文件操作。
在参数区中,get_memory_parameter()->flush_file_window.file_window_id存放文件窗口的ID。
在参数区中,get_memory_parameter()->flush_file_window.file_capability存放文件窗口的capability校验。
3.返回值:
如果flush文件窗口成功,则ax的返回值大于等于零。
如果flush文件窗口失败,则ax的返回值小于零。
7号功能
1.功能:设置允许存储域使用的资源。
2.格式:
ax寄存器存放存储体ID。
bx.寄存器的值为7,也就是设置允许存储域使用资源的功能号。
在参数区中,get_memory_parameter()->memory_resource,存放资源管理的数据结构struct memory_resource。
3.返回值:
如果设置允许存储域使用的资源成功,则ax的返回值大于等于零,同时在参数区get_memory_parameter()->memory_resource返回设置后资源的使用情况。
如果查询存储域的资源失败,则ax的返回值小于零。
10号功能
1.功能:初试化存储体
2.格式:
ax寄存器存放存储体ID。
bx.寄存器的值为10,也就是安装存储体的功能号。
在参数区中,get_memory_parameter()->setup.memory_body_parameter,存放存储体的各项参数。
3.返回值:
如果安装存储体成功,则ax的返回值大于等于零。
如果安装存储体失败,则ax的返回值小于零。
12号功能
1.功能:检查文件窗口的属性
2.格式:
ax寄存器存放存储体ID。
bx.寄存器的值为12,也就是检查文件窗口属性的功能号。
在参数区中,get_memory_parameter()存放文件窗口的各项参数。
3.返回值:
如果检查文件窗口的属性成功,则ax的返回值大于等于零。
如果检查文件窗口的属性失败,则ax的返回值小于零。
13号功能
1.功能:设置文件窗口的属性
2.格式:
ax寄存器存放存储体ID。
bx.寄存器的值为12,也就是设置文件窗口属性的功能号。
在参数区中,get_memory_parameter()存放文件窗口的各项参数。
3返回值:
如果设置文件窗口的属性成功,则ax的返回值大于等于零。
如果设置文件窗口的属性失败,则ax的返回值小于零。
14号功能
1.功能:检查页框使用情况
2.格式:
ax寄存器存放存储体ID。
bx.寄存器的值为14,也就是检查页框使用情况的功能号。
cx寄存器存放重复循环的次数。
dx寄存器存放每次检查多少个页表项。
3.返回值:没有返回值。
struct file_system_call_parameter {
int command,sleep_semaphore;
struct file file;
struct thread_physical_block block;
/*线程当前可以访问的两个物理页框*/
union file_system_operation_parameter parameter;
};
其中command的取值为:
#define OPEN_FILE 0 /*打开文件*/
#define CLOSE_FILE 1 /*关闭文件*/
#define READ_FILE 2 /*读文件*/
#define WRITE_FILE 3 /*写文件*/
#define CONTROL_FILE 4 /*控制文件*/
sleep_semaphore是用于实现同步和互斥的信号量,和当前进程的capability具有相同的capability。文件系统可以使用。
结构 struct file的定义为:
struct file{
struct{
struct network_address network_node; /*文件系统的网络地址*/
int server_processor,server_process,file_handler;
/*文件系统所存在的处理机ID,文件系统的进程ID*/
/*文件在文件系统中的文件ID,访问权限*/
}file;
struct{
int processor,process;
/*文件系统的本地代理所存在的处理机ID,进程ID*/
}proxy;
int memory_process,open_window_id;
/*文件窗口所属的存储域,文件窗口在内核中的标识数*/
int window_base_1,window_base_2,window_length,right;
/*文件窗口在文件中的基地址和长度*/
struct capability capability;
/*文件窗口的capability 校验*/
int read_in_flag,swap_out_flag;
/*是否从文件中读入数据的标志,是否对换到外存标志*/
};
联合union file_system_operation_parameter的定义为:
union file_system_operation_parameter{
struct {
int begin_rw,end_rw; /*数据在文件中的起始地址和终止地址*/
}read_write;
};
根据参数区中存放的结构为struct file_system_call_parameter操作文件请求,文件系统进程执行相应的功能。如果需要读写数据,则从get_thread_physical_block()处写数据或把数据读到get_thread_physical_block()处。