The compiler considers the partial specializations based on a template template argument once you have instantiated a specialization based on the corresponding template template parameter. The following example demonstrates this:
#include<iostream>
#include<typeinfo>
usingnamespacestd;
// 声明一个A的Base模板
template<class T, class U> class A {
public:
int x;
};
// 声明A的偏特化版本
template<class U> class A<int, U> {
public:
short x;
};
template<template<class T, class U> class V> class B {
template<template<class T, class U> class V> class B {
public:
A<T,U> a_;
};
这样是会报错的,因为template<class T, class U> class V只会用来修饰类V而不会用来修饰B。所以在编译的时候,会在B里面说,T/U没有定义。
那么,如果想让B类里面有如下声明:
class B {
public:
T t_;
U u_;
A<T,U> a_;
};
应该怎么办?
应该把B类的声明改成如下:
#include<iostream>
#include<typeinfo>
usingnamespacestd;
template<class T, class U> class A {
public:
T t_;
U u_;
};
template<class U> class A<int, U> {
public:
short x;
};
template<typename T, typename U, typename X, template<typename, typename> class V> class B {
public:
V<T, U> v_;
voidprint(){
cout << "B" << endl;
}
};
B<int, int, float, A> c;
intmain(){
c.print();
}
这里要特别注意看声明。在声明V的时候,所有的这个V会引用到的变量都是在template<typename T, typename U, typename X, ..> B也就是T, U, X这三个里面取。至于具体取哪个,并不能从template的声明里面看出来。
真正要实例化V类的时候,也就是B的内部声明的时候,才会知道V到底是怎么实例化的。
V<T, U> v_;
V<U, X> ux_;
所以可以总结一下,关于模析模板参数。
如果模板模板参数想起作用,那么只能在<typename X, typename Y, typename Z, template<class, class> class V>里面取,并且声明模板板参数的时候,不能写明template<class T, class U> class V,而是需要写成template<class, class> class V这种形式,表示参数都从前面的列表里面选择。
如果是写成template<class T, class U> class V这种格式,并且T/U不在外部列表里面。比如template<typename T, typename U, typename X, template<typename T, typename U> class V>这里会因为T/U重复声明,导到编译不过。那么需要改写成template<typename T, typename U, typename X, template<typename A, typename B> class V> class B {};。但是按照这种写法,模板参数A/B就只能在B类定义的时候内部各种实例化,而不能在外部指定。
所以,也可以总结成。
如果想定义B类的时候,直接也把内部类A的定义决定了。那么,声明的时候,必须采用1.
如果想B类内部自由定义A类的实例化,那么必须采用格式2.
模板模板参数的取值范围
模板模板参数的取值范围可以是:
类型声明在前面,比如<typename X, typename Y, typename Z, template<class, class> class V>。这个时候,类V的模板参数可取范围是X, Y, Z。
类型声明在后面,比如<template<class, class> class V, typename X, typename Y, typename Z>也是同样可以取到X/Y/Z的。
template <template<class> class H, class S>
voidf(const H<S> &value){
}
再如
template <template<class, class> class V, class T, class A>
voidf(V<T, A> &v){
// This can be "typename V<T, A>::value_type",
// but we are pretending we don't have it
T temp = v.back();
v.pop_back();
// Do some work on temp
std::cout << temp << std::endl;
}
缺省值
template <typename T, template <typename> class Cont = Deque>
class Stack {
//...
};
//...
Stack<int> aStack1; // use default: Cont is Deque
Stack<std::string,List> aStack2; // Cont is List
只是需要注意的是,这里的Deque的声明也需要是template <typename> class Deque { };这样。也就是只有一个模板参数。
syscall.h Kernel-private definitions for system call handling
syscall.c System call implementation code
lib/ Makefrag Makefile fragment to build user-mode library, obj/lib/libjos.a
entry.S Assembly-language entry-point for user environments
libmain.c User-mode library setup code called from entry.S
syscall.c User-mode system call stub functions
console.c User-mode implementations of putchar and getchar, providing console I/O
exit.c User-mode implementation of exit
panic.c User-mode implementation of panic
user/ * Various test programs to check kernel lab 3 code
这里面就是lab3新增加的文件。
进程管理
JOS里面是把进程叫做env。定义是在inc/env.h。内核是用这个数据结构来管理用户的进程。
进程的结构体如下:
struct Env {
struct Trapframe env_tf; // Saved registers
struct Env *env_link; // Next free Env
envid_t env_id; // Unique environment identifier
envid_t env_parent_id; // env_id of this env's parent
enum EnvType env_type; // Indicates special system environments
unsigned env_status; // Status of the environment
uint32_t env_runs; // Number of times environment has run
// Address space
pde_t *env_pgdir; // Kernel virtual address of page dir
};
然后详细介绍了每个字段。
env_tf This structure, defined in inc/trap.h, holds the saved register values for the environment while that environment is not running: i.e., when the kernel or a different environment is running. The kernel saves these when switching from user to kernel mode, so that the environment can later be resumed where it left off.
env_link This is a link to the next Env on the env_free_list. env_free_list points to the first free environment on the list.
env_id The kernel stores here a value that uniquely identifiers the environment currently using this Env structure (i.e., using this particular slot in the envs array). After a user environment terminates, the kernel may re-allocate the same Env structure to a different environment - but the new environment will have a different env_id from the old one even though the new environment is re-using the same slot in the envs array.
env_parent_id The kernel stores here the env_id of the environment that created this environment. In this way the environments can form a “family tree,” which will be useful for making security decisions about which environments are allowed to do what to whom.
env_type This is used to distinguish special environments. For most environments, it will be ENV_TYPE_USER. We’ll introduce a few more types for special system service environments in later labs.
env_status This variable holds one of the following values:
ENV_FREE Indicates that the Env structure is inactive, and therefore on the env_free_list.
ENV_RUNNABLE Indicates that the Env structure represents an environment that is waiting to run on the processor.
ENV_RUNNING Indicates that the Env structure represents the currently running environment.
ENV_NOT_RUNNABLE Indicates that the Env structure represents a currently active environment, but it is not currently ready to run: for example, because it is waiting for an interprocess communication (IPC) from another environment.
ENV_DYING Indicates that the Env structure represents a zombie environment. A zombie environment will be freed the next time it traps to the kernel. We will not use this flag until Lab 4.
env_pgdir This variable holds the kernel virtual address of this environment’s page directory.
这个结构体是由一个链表来管理的。
struct Env *envs = NULL; // All environments
struct Env *curenv = NULL; // The current env
static struct Env *env_free_list; // Free environment list
作业1
Exercise 1. Modify mem_init() in kern/pmap.c to allocate and map the envs array. This array consists of exactly NENV instances of the Env structure allocated much like how you allocated the pages array. Also like the pages array, the memory backing envs should also be mapped user read-only at UENVS (defined in inc/memlayout.h) so user processes can read from this array.
You should run your code and make sure check_kern_pgdir() succeeds.
Exercise 5. Modify trap_dispatch() to dispatch page fault exceptions to page_fault_handler(). You should now be able to get make grade to succeed on the faultread, faultreadkernel, faultwrite, and faultwritekernel tests. If any of them don’t work, figure out why and fix them. Remember that you can boot JOS into a particular user program using make run-x or make run-x-nox. For instance, make run-hello-nox runs the hello user program.