跟我一起写操作系统

    返回首页    发表留言
本文作者:李德强
          第六节 停止进程
 
 

        我们在创建进程和线程时,并没有为程序设置停止的条件,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号