我们在创建进程和线程时,并没有为程序设置停止的条件,start_main()函数和线程函数的结尾总有一个无休止的for循环。这是因为当进程或线程结束之后我们需要释放相关的内存资源,将已经申请过的内存块释放掉。需要注意的是其中线程是与进程共享代码段,所以在释放内存时需要先释放子线程的资源再释放父进程的资源。否则如果先释放父进程资源时,子线程还在运行,将无法使用内存相关段的错误。为start_main()函数加入停止进程的中断请求:
void start_main()
{
int argc = 0;
char **args = NULL;
main(argc, args);
int params[2];
//type为4,代表停止进程
params[0] = 4;
//请求0x80号中断服务
__asm__ volatile("int $0x80" :: "a"(params));
}
再编写一个start_pthread()函数用于调用线程函数和停止线程:
extern int pthread_function(void *args);
void start_pthread()
{
void *args = (void *)0;
pthread_function(args);
int params[2];
//type为4,代表停止进程
params[0] = 4;
//请求0x80号中断服务
__asm__ volatile("int $0x80" :: "a"(params));
}
可以看到这个函数与start_main()函数没有什么本质的区别,但它的还是有区别于start_main()的。我们需要一个函数来调用线程函数,并且在线程函数运行结束之后还要调用0x80号中断服务来停止线程。由于start_main()是用来调用main()函数的,而start_pthread()是用来调用线程函数的,所以单独编写一个调用线程的程序,并将其编译成可重定位的文件。在系统内核安装多任务时载入这个程序并为其重定位:
//从文件系统读入程序
s_file *fp = f_open("/usr/bin/start_pthread", FS_MODE_READ, 0, 0);
//申请页面用于存放程序代码
start_pthread_data = alloc_page(process_id, 1, 0, 0);
//读入程序
f_read(fp, fp->fs.size, (u8 *) start_pthread_data);
//程序大小
u32 run_size = fp->fs.size;
//关闭文件
f_close(fp);
start_pthread_data_offset = relocation_elf(start_pthread_data);
当创建一个新的线程时,则为这个线程的pcb分配一个启动程序内存页面,并将start_pthread 程序复制到这个页面中,再将call命令和call命令的参数设置为正确的内容:
//程序入口函数start_pthread所在内存
pcb->run = alloc_page(process_id, 1, 0, 0);
//一个页面
pcb->run_size = 0x1000;
//复制start_pthread函数内容到pcb->run中
mmcopy(start_pthread_data + start_pthread_data_offset, pcb->run, 0x1000);
//如果有参数
if (args != NULL)
{
//设置传入参数
u32 *args_addr = pcb->run + 9;
*args_addr = (u32) args;
}
//设置call pthread_function的固定位置
u32 *pthread_function = pcb->run + 0x14;
*pthread_function = (run - (pcb->run + 0x14) – 4);
为pcb中加入两个属性,一个是父进程指针一个是子进程/线程指针:
typedef struct process_control_block
{
//进程号
u32 process_id;
//类型,进程、线程
u32 type_pt;
//任务描述段
s_tss tss;
//代码段和数据段的局部描述符
s_gdt ldt[2];
... ...
//父进程/线程
void *parent;
//子进程/线程
void *children;
} s_pcb;
创建进程时为其初始化父进程和子进程。因为一个线程要与其父进程共享代码段,所以其父进程必须是一个进程,而不是线程:
//线程的父进程
while (parent_pcb->parent != NULL)
{
parent_pcb = parent_pcb->parent;
}
//设置父进程线程
pcb->parent = parent_pcb;
s_list *p_list = alloc_mm(sizeof(s_list));
p_list->node = pcb;
//设置其父进程线程的子线程为当前线程
parent_pcb->children = list_insert_node(parent_pcb->children, p_list);
实现停止进程的中断服务函数:
void pcb_stop(s_pcb *pcb)
{
s_list *list_node = NULL;
//从运行链表中移出此进程
list_pcb = list_remove_node(list_pcb, pcb, &list_node);
//加入到停止链表
list_pcb_stop = list_insert_node(list_pcb_stop, list_node);
//执行一次调度,跳过当前进程
schedule();
}
在时钟中断时调用pcb_free()函数来释放已停止的进程资源:
void pcb_free()
{
s_list *p = list_pcb_stop;
while (p != NULL)
{
s_list *pf = p;
p = p->next;
s_pcb *pcb = (s_pcb *) pf->node;
if (pcb->children == NULL)
{
s_list *list_node = NULL;
list_pcb_stop = list_remove_node(list_pcb_stop, pcb, &list_node);
if (pcb->parent != NULL)
{
s_pcb *parent = pcb->parent;
s_list *list_n = NULL;
//把当前pcb从其父进程的children中移出
parent->children = list_remove_node(parent->children, pcb, &list_n);
}
u32 pid = pcb->process_id;
free_page_by_pid(pid);
free_mm(pf, sizeof(s_list));
printf("free process %d\n", pid);
}
}
}
使用if (pcb->children == NULL)来判断一个进程是否有子进程,只有其所有的子进程全部停止并释放之后才可以释放进程。使用printf("free process %d\n", pid);来显示进程/线程资源释放的情况。编译运行并查看结果:

源代码的下载地址为:
https https://github.com/magicworldos/lidqos.git git git@github.com:magicworldos/lidqos.git subverion https://github.com/magicworldos/lidqos branch v0.23
Copyright © 2015-2023 问渠网 辽ICP备15013245号