进程与线程

进程 和 线程都是动态概念

进程 = 资源(包括寄存器值,PCB,内存映射表)+指令序列

线程 = 指令序列

线程的资源是共享的

进程间的资源是独立隔离的,内存映射表不同,占用物理内存地址是分割的

线程的切换只是切换PC,切换了指令序列

进程的切换不仅切换PC,还包括切换资源,即切换内存映射表

用户级线程:调用yield函数,自己主动让出CPU,0.11内核看不见,内核只能看见所属进程而看不见用户线程,所以一个用户级线程需要等待,内核会切到别的进程上,不会切到该进程下的其他用户级线程!!

内核级线程:内核能看见内核级线程,一个线程需要等待,内核会切到所属进程下的其他内核级线程。

内核级线程:

只有内核级线程才能发挥多核性能,因为多核级线程共用一套MMU,即内存映射表,但是有多个CPU,可以一个CPU执行一个内核级线程

进程无法发挥多核性能,因为进程切换都得切MMU,即切换内存映射表,一个内核级线程的切换需要两套栈:用户栈+内核栈

系统调用中断过程:

1,INT中断自动压栈的有下一条指令,以及用户级线程SS:SP,就是下面五个参数

2,_system_call把寄存器保护压栈是压到内核栈中,需要手动压栈

3,系统调用,(有可能是_sys_fork,其实就是根据标号找到系统调用),结束之后继续执行,要执行reschedule,先push $ret_from_sys_call,让其在_schedule之后返回到ret_from_sys_call,_schedule为c函数,结束右括号会把ret_from_sys_call pop出来,返回到这里执行,即执行ret_from_sys_call;

4,在ret_from_sys_call中pop出_system_call是保存的寄存器内容,然后中断返回!!

5,中断返回是在最后,中断返回会把SS:SP以及用户态的下一条指令POP出来,即把5个寄存器pop出来!!!这样就会返回到用户栈,运行用户态的下一条指令!!!

代码:

reschedule:
    pushl $ret_from_sys_call
    jmp _schedule                              //如果用call就是先将下一条指令压栈,然后jmp,这样只能顺序往下走,但是用push+jmp则可以改变跳转地址!!

switch_to五段论

在“实地址模式”中,IRET 指令执行到中断程序或过程的远返回。在执行此操作的过程中,处理器从堆栈将返回指令指针、返回代码段选择器以及 EFLAGS 映像分别弹入 EIP、CS 以及 EFLAGS 寄存器,然后恢复执行中断的程序或过程。

这里要注意,中断出口这里已经经过了前面的switch_to,中断的iret已经不是原来的中断返回了,是切换后的新中断的执行返回!!这样返回以后就来到了引发该新中断的用户态代码来执行。

内核级线程切换实例

fork函数经典调用

if (!fork())          //关键在于 INT 80 后面的指令 mov res, %eax;父进程和子进程都会执行这个代码,但是%eax的值不一样
{
     子进程
}
else
{
     父进程
}

fork创建子进程时,copy_process复制父进程相应资源,申请内存空间,创建内核栈,创建用户栈。

eip[0] 赋值为新的程序的开始执行内存地址(可执行程序ELF结构里有entry地址),实际是赋值给esp+28地址处。

eip[3] 赋值为新的程序的用户栈,实际是赋值给esp+40地址处。

汇编调用c函数

实际上是把参数都压到栈里,然后c程序就可以调用,用call来调用

但是要注意c语言 调用结束后,要把栈里的参数删除,即addl指令,吧指针改一下,忽略那些参数

实际上是

push 参数

push 返回值

jmp 调用地址

c函数右括号会生成ret指令,会返回到返回地址

cpu调度算法

周转时间:从开始申请执行任务,到执行任务完成

响应时间:从开始申请执行任务到开始执行任务

三种调度方法:

调度算法:考虑优先级,任务类型,响应时间,周转时间,调度算法要简单些,内耗低。

1,先来先服务 平均周转时间可能会很长

2,短作业优先(SJF) 周期时间短,但是响应时间长,适用于后台程序,如gcc的编译,快点把整个程序编译完

3,时间片轮转(RR) 响应时间可以得到保证,nT,n为任务个数,T为时间片长度,适用于前台程序,IO操作多的

4,优先级轮转 固定优先级,可能会造成程序一直没法执行下去,需要动态调整优先级

void schedule(void)
{
        int i,next,c;
        struct task_struct ** p;

/* check alarm, wake up any interruptible tasks that have got a signal */

        for(p = &LAST_TASK ; p > &FIRST_TASK ; --p)
                if (*p) {
                        if ((*p)->alarm && (*p)->alarm < jiffies) {
                                        (*p)->signal |= (1<<(SIGALRM-1));
                                        (*p)->alarm = 0;
                                }
                        if (((*p)->signal & ~(_BLOCKABLE & (*p)->blocked)) &&
                        (*p)->state==TASK_INTERRUPTIBLE)
                                {
                                        (*p)->state=TASK_RUNNING;
                                        /*可中断睡眠 => 就绪*/
                                        fprintk(3,"%d\tJ\t%d\n",(*p)->pid,jiffies);
                                }
                }

/* this is the scheduler proper: */

        while (1) {
                c = -1;
                next = 0;
                i = NR_TASKS;
                p = &task[NR_TASKS];
                while (--i) {
                        if (!*--p)
                                continue;
                        if ((*p)->state == TASK_RUNNING && (*p)->counter > c)
                                c = (*p)->counter, next = i;
                }
                //首先找到counter最大并且TASK_RUNNING的进程作为调度进程
                //counter 可以作为时间片和优先级的表达
                if (c) break;
                //如果所有进程的counter为0,则增加阻塞进程的counter
                for(p = &LAST_TASK ; p > &FIRST_TASK ; --p)
                        if (*p)
                                (*p)->counter = ((*p)->counter >> 1) +
                                                (*p)->priority;
        }
        /*编号为next的进程 运行*/
        if(current->pid != task[next] ->pid)
        {
                /*时间片到时程序 => 就绪*/
                if(current->state == TASK_RUNNING)
                        fprintk(3,"%d\tJ\t%d\n",current->pid,jiffies);
                fprintk(3,"%d\tR\t%d\n",task[next]->pid,jiffies);
        }
        switch_to(next);
}
void do_timer(long cpl)
{
        extern int beepcount;
        extern void sysbeepstop(void);

        if (beepcount)
                if (!--beepcount)
                        sysbeepstop();

        if (cpl)
                current->utime++;
        else
                current->stime++;

        if (next_timer) {
                next_timer->jiffies--;
                while (next_timer && next_timer->jiffies <= 0) {
                        void (*fn)(void);
    
                        fn = next_timer->fn;
                        next_timer->fn = NULL;
                        next_timer = next_timer->next;
                        (fn)();
                }   
        }   
        if (current_DOR & 0xf0)
                do_floppy_timer();
        //时间中断,减少counter,近似RR轮转算法,考虑到SJF短左右优先
        if ((--current->counter)>0) return;
        current->counter=0;
        if (!cpl) return;
        schedule();
}

 

资源下载链接为: https://pan.quark.cn/s/00cceecb854d 这个项目名为“mnist-nnet-hls-zynq7020-fpga prj”,是一个机器学习相关的工程,专注于利用高级综合(HLS)技术将针对MNIST数据集设计的神经网络(nnet)实现在Zynq 7020 FPGA平台上,以加速图像识别任务。项目提供的压缩包包含所有相关代码文件,如C/C++源码、HLS接口定义、Vivado HLS项目文件、硬件描述语言代码(Verilog或VHDL)及配置文件等,用户可通过这些代码理解、实现或修改设计流程。 项目标签“mnist-nnet-hls-z”进一步明确了其关注点:MNIST数据集、HLS技术以及Zynq目标平台。MNIST是用于手写数字识别的知名训练数据集;HLS可将高级编程语言转化为硬件描述语言;Zynq 7020是Xilinx的SoC FPGA,融合了ARM处理器可编程逻辑。文件名中提到的“vivado”指的是Xilinx的Vivado设计套件,它是一个用于FPGA设计、实现、仿真和调试的集成开发环境,其中的Vivado HLS工具能够将C、C++或SystemC编写的算法自动转换为硬件描述语言代码。 项目可能的实施步骤如下:首先,对MNIST数据集进行预处理,如归一化、降维等,使其适配神经网络模型输入;其次,构建适用于手写数字识别的神经网络模型,例如卷积神经网络(CNN)或全连接网络(FCN);接着,运用HLS工具将神经网络模型转化为硬件描述,并优化性能资源利用率;然后,在Vivado环境中,将生成的硬件描述代码映射到Zynq 7020的FPGA部分,进行时序分析综合优化;此外,由于Zynq是SoC,包含处理器系统,还需编写控制软件来管理调度FPGA上的硬件加速器,可能涉及OpenCV、OpenCL等库的使用;之后,
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值