更新时间:2021-12-02 02:28:50
// 进程控制结构体(PCB) --> 用来管理进程
struct tack_struct {
struct List list; // 双向链表, 用于连接各个进程控制结构体, 在Linux中这样的链表创建方式比较常见
volatile long state; // 表示进程的状态: 运行态, 停止态, 可中断态等
unsigned long flags; // 进程标志, 是进程还是线程, 也许这就是Linux中的线程被称为轻量级的进程的原因
struct mm_struct *mm; // 记录内存页表和程序段信息, 说白了就是管理内存中的程序(data, code, rodata, bss), 应用程序的栈顶地址
struct thread_struct *thread; // 用于保存进程切换时的数据
unsigned long addr_limit; // 进程地址空间范围
long pid;
long counter; // 进程占用的时间片
long signal; // 进程的信号
long priority; // 进程的优先级
};
struct mm_struct {
// pgd的值是从cr3寄存器中获取的, 就是页表的地址
pml4t_5 *pgd; // 页表指针
unsigned long start_code, end_code;
unsigned long start_data, end_data;
unsigned long start_rodata, end_rodata;
unsigned long start_brk, end_brk;
// 应用程序的栈顶地址
unsigned long start_stack;
};
// 用于保留现场
struct thread_struct {
unsigned long rsp0; // 内核层栈基地址
unsigned long rip; // 内核层代码指针
unsigned long rsp; // 内核层当前栈指针
unsigned long fs; // 存储fs段寄存器的值
unsigned long gs; // gs段寄存器的值
unsigned long cr2; // cr2控制寄存器的值
unsigned long trap_nr; // 产成异常的异常号
unsigned long error_code; // 异常的错误码
};
/*
* 在Linux内核中, 将task_truct结构体和进程的内核层栈空间融为一体, 低地址存放task_struct结构体, 余下的存放进程的内核层栈空间使用
*
*/
// 通过该联合体创建出来的是Linux下第一个进程, 注意: 这个进程不是我们提到的init进程, init进程是Linux第二个进程
union task_union {
struct task_struct task;
unsigned long stack[STACK_SIZE / sizeof(unsigned long)];
};
#define INIT_TASK(tsk) \
{\
.state = TASK_UNINTERRUPTIBLE, \
.flags = PF_KTHREAD,\
.mm = &init_mm,\
.thread = &init_thread, \
.addr_limit = 0xffff800000000000, \
.pid = 0, \
.counter = 1, \
.signal = 0, \
.priority = 0\
}
// 1, 2, 3都是初始化第一个进程
// 1
union task_union init_task_union __attribute__((__section__(".data.init_task"))) = {INIT_TASK(init_task_union.task)};
// 2
struct task_struct *init_task[NP_CPUS] = {&init_task_union.task, 0};
struct mm_struct init_mm = {0};
// 3
struct thread_struct init_thread = {
.rsp0 = (unsigned long)(init_task_union.stack + STACK_SIZE / sizeof(unsigned long)),
.rsp = (unsigned long)(init_task_union.stack + STACK_SIZE / sizeof(unsigned long)),
.fs = KERNEL_DS,
.gs = KERNEL_DS,
.cr2 = 0,
.trap_nr = 0,
.error_code = 0
};
struct tss_struct {
unsigned int reserved0;
unsigned long rsp0;
unsigned long rsp1;
unsigned long rsp2;
unsigned long reserved1;
unsigned long ist1;
unsigned long ist2;
unsigned long ist3;
unsigned long ist4;
unsigned long ist5;
unsigned long ist6;
unsigned long ist7;
unsigned long reserved2;
unsigned short reserved3;
unsigned short iomapbaseaddr;
}__attirbute__((packed));
#define INIT_TSS \
{\
.reserved = 0,
... \
.ist1 = 0xffff800000007c00,\
...\
}
struct tss_struct init_tss[NR_CPUS] = { [0 ... NR_CPUS - 1] = INIT_TSS };
/*
* 在之前我们恢复异常和中断的现场的时候就发现要对大量的寄存器中的数据压栈保存, 有在task_struct的thread在进程切换的时候保存数据的方式的启发, 我们可以定义一个结构体来保存保留
* 现场的寄存器的数据, 然后直接将这个结构体中的数据直接拷贝到内核栈中, 而不是一个一个地压入到内核栈中, 这样效率高
*/
/*
* 这个进程的现场保留的数据就交给了这个pt_regs结构体了, 他和异常以及中断时将大部分寄存器中的数据压栈是一样的
*/
/*
* 我们知道一个操作系统会管理很多个进程, 我们需要有一个函数来获取当前的task_struct
*/
inline struct task_struct *get_current() {
struct task_struct *current = NULL;
__asm__ __volatile__("andq %%rsp, %0":"=r"(current):"0"(~3276UL));
return current;
}
#define current get_current()
#define GET_CURRENT \
"movq %rsp, %rbx \n\t" \
"andq $ - 32768, %rbx \n\t"