又到了复习周了,接下来这一周,我将用一系列我本人的的操作系统笔记带着你一起重新学习操作系统
进程
操作系统中最核心的一个概念:进程。
其实,在某一瞬间,cpu只能运行一个进程,但在1秒钟内,他可能运行多个进程,这样就产生了并行的错觉,这种现象就是伪并行也就是多次切换进程,这种快速切换进程称作多道程序设计。
-
进程控制块 (PCB)
- 用于描述和控制进程的运行,是操作系统中最重要的记录型数据结构。
- 记录了操作系统所需要的、用于描述进程当前情况以及进程控制运行的全部信息。
- 系统创建一个新进程时,会为它建立一个PCB;进程结束时又收回PCB
- 用于描述和控制进程的运行,是操作系统中最重要的记录型数据结构。
-
进程通信的类型
低级通信:数据量小,交互的是控制信息。例如: **信号量 **
高级通信:交换信息量较大的过程。 例如• 缓冲 • 信箱 • 管道 • 共享区
-
进程模型
关键思想:一个进程是某种类型的活动,他有程序、输入、输出以及状态。单个处理器可以被若干个进程共享,它使用某种调度算法决定何时停止一个进程的工作,并转而为另一个进程提供服务
-
进程的创建,有4种主要事件导致进程的创建:
- 系统初始化
- 正在运行的程序执行了创建进程的系统调用
- 用户请求创建一个进程
- 一个批处理作业的初始化
在UNIX中,只有系统调用可以创建新的进程:fork,子进程的初始地址空间是父进程的一个副本,程序正文是在子进程和父进程间共享的,使用copy-on-write方法使可写的内存不能被修改
-
进程的终止,一般也有4种条件:
- 正常退出
- 出错退出(给了错误的参数)
- 严重错误(引用了非法内存、执行了非法指令等等)
- 被其他进程杀死($\color{orange}{kill} $命令)
- 前两个使自愿的,后两个是非自愿的,在UNIX种,调用$\color{orange}{exit} $结束进程
-
进程的层次结构,在UNIX种,有进程树$\color{orange}{pstree} $,子进程是继承父进程,进程只有一个父进程,但是可以有多个子进程,但是在windows种没有进程层次这个概念,所有进程的地位都是相同的
进程的状态
-
三态:
-
运行(running)态:进程占有处理器正在运行的状态。
进程已获得CPU,其程序正在执行。在单处理机系统中,只有一个进程处于执行状态; 在多处理机系统中,则有多个进程处于执行状态。 -
就绪(ready)态:进程具备运行条件,等待系统分配处理器以便运行的状态。
当进程已分配到除CPU以外的所有必要资源后,只要再获得CPU,便可立即执行,进程这时的状态称为就绪状态。在一个系统中处于就绪状态的进程可能有多个,通常将它们排成一个队列,称为就绪队列。 -
等待(wait)态:又称阻塞态或睡眠态,指进程不具备运行条件,正在等待某个时间完成的状态。
也称为等待或睡眠状态,一个进程正在等待某一事件发生(例如请求I/O而等待I/O完成等)而暂时停止运行,这时即使把处理机分配给进程也无法运行,故称该进程处于阻塞状态。 -
引起进程状态转换的具体原因如下: 运行态→等待态:等待使用资源; 等待态→就绪态:资源得到满足; 运行态→就绪态:运行时间片到;出现有更高优先权进程。 就绪态—→运行态:CPU 空闲时选择一个就绪进程。
-
-
五态:
五态模型在三态模型的基础上增加了新建态(new)和终止态(exit)。
- 新建态:对应于进程被创建时的状态,尚未进入就绪队列。
创建一个进程需要通过两个步骤:
1.为新进程分配所需要资源和建立必要的管理信息。
2.设置该进程为就绪态,并等待被调度执行。 - 终止态:指进程完成任务到达正常结束点,或出现无法克服的错误而异常终止,或被操作系统及有终止权的进程所终止时所处的状态。
处于终止态的进程不再被调度执行,下一步将被系统撤销,最终从系统中消失。
终止一个进程需要两个步骤:
1.先等待操作系统或相关的进程进行善后处理(如抽取信息)。
2.然后回收占用的资源并被系统删除。
引起进程状态转换的具体原因如下: NULL→新建态:执行一个程序,创建一个子进程。 新建态→就绪态:当操作系统完成了进程创建的必要操作,并且当前系统的性能和虚拟内存的容量均允许。 运行态→终止态:当一个进程到达了自然结束点,或是出现了无法克服的错误,或是被操作系统所终结,或是被其他有终止权的进程所终结。 运行态→就绪态:运行时间片到;出现有更高优先权进程。 运行态→等待态:等待使用资源;如等待外设传输;等待人工干预。 就绪态→终止态:未在状态转换图中显示,但某些操作系统允许父进程终结子进程。 等待态→终止态:未在状态转换图中显示,但某些操作系统允许父进程终结子进程。 终止态→NULL:完成善后操作。
- 新建态:对应于进程被创建时的状态,尚未进入就绪队列。
-
七态
三态模型和五态模型都是假设所有进程都在内存中的事实上有序不断的创建进程,当系统资源尤其是内存资源已经不能满足进程运行的要求时,必须把某些进程挂起(suspend),对换到磁盘对换区中,释放它占有的某些资源,暂时不参与低级调度。起到平滑系统操作负荷的目的。
引起进程挂起的原因是多样的,主要有:
1.终端用户的请求。当终端用户在自己的程序运行期间发现有可疑问题时,希望暂停使自己的程序静止下来。亦即,使正在执行的进程暂停执行;若此时用户进程正处于就绪状态而未执行,则该进程暂不接受调度,以便用户研究其执行情况或对程序进行修改。我们把这种静止状态成为“挂起状态”。2.父进程的请求。有时父进程希望挂起自己的某个子进程,以便考察和修改子进程,或者协调各子进程间的活动。
3.负荷调节的需要。当实时系统中的工作负荷较重,已可能影响到对实时任务的控制时,可由系统把一些不重要的进程挂起,以保证系统能正常运行。
4.操作系统的需要。操作系统有时希望挂起某些进程,以便检查运行中的资源使用情况或进行记账。 5.对换的需要。为了缓和内存紧张的情况,将内存中处于阻塞状态的进程换至外存上。
七态模型在五态模型的基础上增加了挂起就绪态(ready suspend)和挂起等待态(blocked suspend)。
- 挂起就绪态:进程具备运行条件,但目前在外存中,只有它被对换到内存才能被调度执行。
- 挂起等待态:表明进程正在等待某一个事件发生且在外存中。
引起进程状态转换的具体原因如下: 等待态→挂起等待态:操作系统根据当前资源状况和性能要求,可以决定把等待态进程对换出去成为挂起等待态。 挂起等待态→挂起就绪态:引起进程等待的事件发生之后,相应的挂起等待态进程将转换为挂起就绪态 挂起就绪态→就绪态:当内存中没有就绪态进程,或者挂起就绪态进程具有比就绪态进程更高的优先级,系统将把挂起就绪态进程转换成就绪态。 就绪态→挂起就绪态:操作系统根据当前资源状况和性能要求,也可以决定把就绪态进程对换出去成为挂起就绪态。 挂起等待态→等待态:当一个进程等待一个事件时,原则上不需要把它调入内存。但是在下面一种情况下,这一状态变化是可能的。当一个进程退出后,主存已经有了一大块自由空间,而某个挂起等待态进程具有较高的优先级并且操作系统已经得知导致它阻塞的事件即将结束,此时便发生了这一状态变化。 运行态→挂起就绪态:当一个具有较高优先级的挂起等待态进程的等待事件结束后,它需要抢占 CPU,而此时主存空间不够,从而可能导致正在运行的进程转化为挂起就绪态。另外处于运行态的进程也可以自己挂起自己。 新建态→挂起就绪态:考虑到系统当前资源状况和性能要求,可以决定新建的进程将被对换出去成为挂起就绪态。
挂起进程等同于不在内存中的进程,因此挂起进程将不参与低级调度直到它们被调换进内存。
挂起进程具有如下特征:
- 该进程不能立即被执行
- 挂起进程可能会等待一个事件,但所等待的事件是独立于挂起条件的,事件结束并不能导致进程具备执行条件。 (等待事件结束后进程变为挂起就绪态)
- 进程进入挂起状态是由于操作系统、父进程或进程本身阻止它的运行。
- 结束进程挂起状态的命令只能通过操作系统或父进程发出。
以上进程的状态转换来自:https://zhuanlan.zhihu.com/p/60982639
进程(线程)间的通信
管道
管道在linux中就是|,这个不是竖线,是匿名管道管道,管道是一种缓存,单向的,一端输入,必须在另一端输入,如果不输出则会永久阻塞。我们可以做个实验:
- 首先创建一个叫hello的管道
-
向管道输入“hello world”
我们发现终端会阻塞在这里
-
我们再创建一个终端,输出管道
这时候就可以输出hello world,并且阻塞也解除了
消息队列模型
简单来说就是一个发邮件的过程,双方定义好相关的正文text和类型type,然后就可以互相发送消息,不会造成阻塞。
这个消息队列有一个key和msgid,负责管理双方的通信,他们可以通过msgid和key在消息队列上进行收发信息,从而不会干扰其他的进程的通信,也可以一个队列共享很多个进程的通信
共享内存模型
这个共享内存其实就是我们通常有紧急任务不得不开会的时候的那个会议室,它可以使进程不用一个个去拷贝信息,直接到共享内存里面去拿,通过shmid来拿对应的共享内存
信号量
操作系统中用信号量来表征一种资源或状态。信号量其实就是一个计数器,一般是配合共享内存来使用,通过互斥和同步来管理进程,
信号量会定义两个原子操作:一个是P 操作,我们称为申请资源操作。另一个是V 操作,我们称为归还资源操作
P操作:S=S-1;如果S小于0,则进程进入等待状态无限循环,否则继续执行。
V操作:S=S+1;如果S>=0,则唤醒等待队列中的一个等待进程。
**原子操作:**当两个进程同时达到共享内存,需要取资源的时候,必须分个先来后到
竞争条件
两个或多个程序读写某些共享数据,而最后的结果取决于进程运行的精确时序
临界区
要避免竞争条件?关键是需要互斥(就是以某种手段,比如加锁等等操作确保当一个进程在使用一个共享变量或文件时,其他进程不能做同样的操作)
我们把对共享内存进行访问的程序片段称作临界区
信号
这个信号跟信号量是完全不一样的,信号只是一个代号(数字),linux有几十种信号,每个信号,linux都会定义一个函数来代表这个信号的意义,例如:kill -9 这个9就是一个信号,意义就是无论进程是否运行,强制杀死该进程。
一旦有信号产生,我们就有下面这几种,用户进程对信号的处理方式。
- 执行默认操作,Linux 对每种信号都规定了默认操作
- **捕捉信号,**我们可以为信号定义一个信号处理函数。当信号发生时,我们就执行相应的信号处理函数。
- 忽略信号,有两个信号是应用进程无法捕捉和忽略的,即 SIGKILL 和 SEGSTOP,它们用于在任何时候中断或结束某一进程。
信号的发送
例如,Ctrl+C 产生 SIGINT 信号,Ctrl+Z 产生 SIGTSTP 信号。
处理机调度
当多个程序进入到了就绪状态的时候就会发生调度,这个调度相应的会有调度算法
中断向量
中断向量,为了维持CPU上对多个顺序进程的错觉做更多的阐述。它包含中断服务程序的入口地址
进程的行为
前者把大量时间花在计算上,计算密集型,后者把大量时间花在等待IO上,叫IO密集型。但是,随着CPU变得越来越快,更多进程偏向于I/O密集型,因为CPU的改进比磁盘的改进快得多
何时调度
- 创建一个新进程后,需要决定运行子进程还是父进程
- 在一个进程退出之后,必须做出调度决策
- 当一个进程阻塞在I/O和信号量上或由于其他原因阻塞时,必须选择另一个进程运行
- 当IO中断发生
- 非抢占式调度:挑选一个进程,然后让该进程运行直至阻塞,或知道该进程自动释放CPU
- 抢占式调度:挑选一个进程,并且让该进程运行某个固定时段的最大值,如果该时段结束时,该进程仍在运行,就挂起当前进程,进而挑选另一个进程运行。
调度算法分类
不同的环境运行不同的算法
- 批处理,处理存货清单、薪水册等。一般用户都会等,所以用非抢占式算法是可以的
- 交互式,不能等,抢占式
- 实时,非抢占,只运行那些用来推进现有应用的程序。
批处理系统中的调度(非抢占式)
-
先来先服务(非抢占的)
- 优点:易于理解和运用。一个单链表即可
- FCFS 策略的缺点是,平均等待时间往往很长。
-
最短作业优先(非抢占的)
- 当输入队列中有若干个同等重要的作业被启动时,调度程序应该使用最短作业优先
- 只有在所有作业都可同时运行的情形下(就是他们的到达时间要一样),最短作业优先算法才是最优的。
-
最短剩余时间优先(抢占的)
交互式系统中的调度(抢占式)
- 轮转调度,假设所有的进程同等重要(到达时间一样,优先级一样),每个进程都被分配一个时间片,如果在时间片内,该进程还在运行,则挂起该进程,把CPU分配给另一个进程。唯一的有缺点是时间片的长度,结论:时间片太短会导致过多的上下文切换,太长可能引起对短的交互请求的响应时间变长,那么 RR 调度就演变成了 FCFS 调度。根据经验,80% 的 CPU 执行应该小于时间片。
- 优先级调度,优先级越高越先调度。 缺点:低优先级的进程很可能产生饥饿现象
- 多级队列调度算法,将就绪队列分成多个单独队列,根据进程属性,如内存大小、进程优先级、进程类型等,一个进程永久分到一个队列,每个队列有自己的调度算法。此外,队列之间应有调度,通常采用固定优先级抢占调度。每个队列与更低层队列相比具有绝对的优先。
- 彩票调度,基本思路:进程提供各种系统资源的彩票,一旦需要做出一项调度决策时,就随机抽出一张彩票,拥有该彩票的进程获得该资源
实时系统中的调度
实时系统是一种时间起着主导作用的系统。特点:正确的但是迟到的应答往往比没有还要糟糕
- 硬实时:必须满足绝对的截止时间
- 软实时:可以容忍偶尔错失截止时间
下面重点介绍几个调度算法。(重中之重)
先到先服务调度FCFS
FCFS 调度算法是非抢占的。一旦 CPU 分配给了一个进程,该进程就会使用 CPU 直到释放 CPU 为止,即程序终止或是请求 I/O。
这里要注意几个概念:
- 周转时间=作业完成时间−作业提交时间, 特别注意作业提交时间不是作业进内存的时间,而是发出请求,提交就开始计时,如果无法安排进内存,那么就等待,等待的这部分时间也要计数。
- 带权周转时间=作业周转时间/作业实际运行时间
优点:实现简单,有利于长作业和CPU繁忙的进程
缺点:使短作业等待长作业,重要的作业等待可能不是很重要的长作业,不能用于分时和实时系统。
最短作业优先(SJF)调度算法
每个进程与其下次 CPU 执行的长度关联起来。当 CPU 变为空闲时,它会被赋给具有最短 CPU 执行的进程。如果两个进程具有同样长度的 CPU 执行,那么可以由 FCFS来处理。
SJF 算法的真正困难是如何知道下次 CPU 执行的长度。对于批处理系统的长期(或作业)调度,可以将用户提交作业时指定的进程时限作为长度。一种方法是试图近似 SJF 调度。虽然不知道下一个 CPU 执行的长度,但是可以预测它。可以认为下一个 CPU 执行的长度与以前的相似。因此,通过计算下一个 CPU 执行长度的近似值,可以选择具有预测最短 CPU 执行的进程来运行。
SJF 算法可以是抢占的或非抢占的。
优点:有利于短作业或短进程。降低了作业的平均等待时间, 提高了系统的吞吐量。
缺点:用户可能有意或无意地缩短作业的估计执行时间,致使该算法不一定能真正做到短作业优先;不利于长作业; 不能满足紧迫作业。
优先级调度算法
优先调度可以是抢占的或非抢占的。
优先级调度算法的一个主要问题是无穷阻塞或饥饿。因为优先级过低的进程会一直得不到进程,被优先级过高的进程所占用,而且该进程如果是阻塞调用,则很容易会造成无穷阻塞
低优先级进程的无穷等待问题的解决方案之一是老化。老化逐渐增加在系统中等待很长时间的进程的优先级
上面提到的最短作业优先调度属于简单的优先级算法
最高响应比优先算法HRN
是对FCFS方式和SJF方式的一种综合平衡,HRN调度策略同时考虑每个作业的等待时间长短和估计需要的执行时间长短,从中选出响应比最高的作业投入执行。
相应比: R=响应时间 /需运行的时间= 1+已等待的时间 /需运行时间(请务必记住此公式,我已默默把该公式抄到本本里面。。)
时间片轮转RR
在早期的时间片轮转法中,系统将所有的**就绪进程**按**先来先服务**的原则排成一个队列,每次调度时,把**CPU分配给队首进程**,并令其执行一个时间片。时间片的大小从几ms到几百ms(要自己设置大小,如果时间片**过大**,就会造成运行时间过程,退化成了**FCFS算法**,如果时间片**过短**,就会造成过多的**上下文切换**)。当执行的**时间片用完**时,由一个计时器发出时钟**中断**请求,调度程序便据此信号来停止该进程的执行,并**将它送往就绪队列的末尾**;然后,再把处理机分配给就绪队列中**新的队首进程**,同时也让它执行一个时间片。这样就可以保证就绪队列中的所有进程在一给定的时间内均能获得一时间片的处理机执行时间。换言之,系统能在给定的时间内响应所有用户的请求。
注意,如果时间片完成的时刻,有一个新进程到来,则将它插入到就绪队列的尾部
线程
每个进程有一个地址空间和一个控制线程
java线程的状态转换
- 新建(New)
- 运行(Runnable):包括操作系统线程状态中的Running和Ready
- 无限期等待(Waiting),Object的wait和join方法
- ·限期等待(Timed Waiting)Object的wait和join方法(设置了时间参数)
- 阻塞(Blocked),“阻塞状态”与“等待状态”的区别是**“阻塞状态”在等待着获取到一个排它锁**,这个事件将在另外一个线程放弃这个锁的时候发生;而“等待状态”则是在等待一段时 间,或者唤醒动作的发生。
经典的线程模型
多个线程共享一个地址空间和资源。多个进程共享物理内存、磁盘、打印机和其他资源
a属于1对1模型(是线程安全的,不会有并发问题,但是资源消耗严重),b属于一对多模型
其实这一块还有好多内容,但是好多都是跟java多线程有关,具体的总结下次再写!!
死锁
- 死锁的必要条件
- 互斥条件:至少有一个资源是临界资源。
- 不可剥夺条件:资源不能被抢占。
- 请求与保持条件:一个进程占有一个资源,并等待另一个资源。
- 环路条件:存在一个进程-资源的环形链。
银行家算法
当一个进程申请使用资源的时候,银行家算法通过先 试探 分配给该进程资源,然后通过安全性算法判断分配后的系统是否处于安全状态,若不安全则试探分配作废,让该进程继续等待。
https://blog.csdn.net/qq_33414271/article/details/80245715 可以参考此博文,我就不做过多的总结了!!
忙等待的互斥
-
屏蔽中断,每个进程在刚进入临界区后立即屏蔽所有中断,并在离开之前再打开中断,这个方法并不好,因为整个系统可能会因此而终止,而且如果是多核处理器,屏蔽中断只会对当前cpu有效,其他cpu也还是会执行,并可以访问共享内存。总的来说,屏蔽中断对于操作系统而言是一项不错的技术,但是对于用户进程则不是一种合适的通用互斥机制
-
锁变量,有漏洞的,当一个进程读出锁变量的值发现为0,而恰好在它将其设置为1之前,另一个进程被调度运行,将锁变量设置为1,当第一个进程再次运行时,它同样也将该锁设置为1,则此时同时有两个进程进入临界区,这时候,可以用CAS原子操作来修改这个锁变量,同时也要保证锁变量的可见性才行,但是有人会想用二次检查(double check),这个方法也是不行的,第二次检查的时候,后一个进程也可以修改锁变量
-
严格轮换法,使用自旋锁,连续测试一个变量直到某个值出现位置。
睡眠与唤醒
其实上面这些解法的本质:当一个进程想进入临界区,先检查是否允许进入,若不允许,则该进程原地等待,直到允许为止,就像是java的CAS操作
上面那些方法,会使CPU一直再运作,而不能休息,很容易造成活锁现象(永远忙等待下去),这种情况就是优先级反转问题
在java中,可以使用await(相当于底层的slepp,将引起调用进程阻塞的系统调用)和notify(唤醒,相当于底层的wakeup)
-
作为例子,可以使用生产者与消费者问题。简单描述一下这个生产者与消费者问题的解决方法:如果缓冲队列已满,那么当生产者仍想要将数据写入缓冲区的时候,会出现问题。它的解决办法是让生产者睡眠,也就是阻塞生产者。等到消费者从缓冲区中取出一个或多个数据项时再唤醒它。同样的,当消费者试图从缓冲区中取数据,但是发现缓冲区为空时,消费者也会睡眠,阻塞。直到生产者向其中放入一个新的数据。
这个逻辑听起来比较简单,而且这种方式也需要一种称作
监听
的变量,这个变量用于监视缓冲区的数据,我们暂定为 count,如果缓冲区最多存放 N 个数据项,生产者会每次判断 count 是否达到 N,否则生产者向缓冲区放入一个数据项并增量 count 的值。消费者的逻辑也很相似:首先测试 count 的值是否为 0 ,如果为 0 则消费者睡眠、阻塞,否则会从缓冲区取出数据并使 count 数量递减。每个进程也会检查检查是否其他线程是否应该被唤醒,如果应该被唤醒,那么就唤醒该线程。下面是生产者消费者的代码
死锁的检测
资源分配图
系统死锁,可利用资源分配图来描述。如图所示,用方框代表一个进程,用圆圈代表一类资源。由于一种类型的资源可能有多个,用框中的一个小圆圈代表一类资源中的一个资源。从进程到资源的有向边叫请求边,表示该进程申请一个单位的该类资源;从资源到进程的边叫分配边,表示该类资源已经有一个资源被分配给了该进程。
死锁定理
在资源分配图中(这里用上图做例子),找出既不阻塞又不是孤点的进程Pi(即找出一条有向边与它相连,且该有向边对应资源的申请数量小于等于系统中已有空闲资源数量。若所有的连接该进程的边均满足上述条件,则这个进程能继续运行直至完成,然后释放它所占有的所有资源)。消去它所有的请求边和分配边,使之成为孤立的结点。
-
P3是满足这一条件的进程结点,将P3的所有边消去
-
进程Pi所释放的资源,可以唤醒某些因等待这些资源而阻塞的进程,原来的阻塞进程可能变为非阻塞进程。在上图中,进程P2满足这样的要求,消去所有边后,P1也满足同样的要求。
总结:S为死锁的条件是当且仅当S状态的资源分配图是不可完全简化的,该条件为死锁定理。
死锁的解除
一旦检测出死锁,就应立即釆取相应的措施,以解除死锁。死锁解除的主要方法有:
-
资源剥夺法。挂起某些死锁进程,并抢占它的资源,将这些资源分配给其他的死锁进程。但应防止被挂起的进程长时间得不到资源,而处于资源匮乏的状态。
-
撤销进程法。强制撤销部分、甚至全部死锁进程并剥夺这些进程的资源。撤销的原则可以按进程优先级和撤销进程代价的高低进行。
-
进程回退法。让一(多)个进程回退到足以回避死锁的地步,进程回退时自愿释放资源而不是被剥夺。要求系统保持进程的历史信息,设置还原点。
文章参考:
《现代操作系统》
C语言中文网
本课程的PPT
个人唠叨
好了,又到了个人唠叨时间,今天,是一个特殊的日子,没错,是高考的第一天!
聊聊高考
两年前,我有幸参加了高考,还记得那时的我是抱着一种”稳了“的决心来参加高考的,因为自己的目标也不是很大,也只是想上个一本,因为之前在模拟考的时候有试过那么一次自己的分数线上了所谓的一本线,所以我就一直都是以这个为目标来维持着我高三这一学年的学习状态!总之,两年前,我是抱着一种希望,淡定,自信来看待高考
两年前,我觉得高考对我的影响:我不幸来到了我现在这所学校,为什么说不幸呢,因为,我一直觉得,两年前的自己是有这个实力可以去一个更好的学校的,但是我最终也因为分数的原因没去到,乖乖屈服在命运之下。
所以,在大一这一年,我努力学习,早早就定下了考研的决心(后面找时间再谈谈为什么我现在会放弃了考研这个想法),其实当时的我并不知道自己为什么要考研,只是为了考上一个好学校而考研,所以,这一年我一直处于一种自卑的状态,在同学聚会的时候、在过年聚会的时候,总之每当别人问起我去哪里读书了,我都会说东莞,并没有说出我的学校名字,因为我生怕别人听到我的学校名字之后,背后会说这说那。我甚至还撒谎说过自己去了哪所重本!!嗯,就这样,我的大一在高考失利后的阴影中度过!
两年后,也就是现在的我,早上起来刷了某音小视频后,才发现原来今天是高考噢!对的,现在的我,甚至连高考是什么时候都忘记了,额,并不是因为今年因为疫情而延期了,就算今天是6月7号,我也不一定会知道今天是高考。其实现在的我看到高考,也会想起自己高考失利后的样子,但是,人总是往前看的,不知道什么时候,我已经走出了这个阴影,开始在球场上说出自己的学校名字后,自己并不觉得有什么好羞耻的,甚至被别人说我是一个垃圾三本的学生,肯定进不了大厂,我心里也没有半点激动!也许是因为现在的我,知道自己想要什么,自己也确实进入人生第二个阶段了。很喜欢一句话”当大海退潮的时候,我们就知道到底谁在裸泳“,嗯,以这句话送给当下的自己送给那些和我一样起点比别人低,但是却不甘心做一条咸鱼的朋友们!
最后,用一句话送给正在高考的你们”你的努力从来不是为了别人,希望你的每一次‘考试’都能超常发挥!“,高考加油!
聊聊我的启蒙老师-潘老师
哈哈哈,今天安卓答辩也结束了,潘老师也结束了对我们一年的教学,那也借着今天这个特殊的日子再聊聊我的启蒙老师,我大学的启蒙老师除了我上面提到的潘老师之外,还有一位是超哥,大家怀念超哥的可以到这篇文章看看超哥的样子,嘻嘻!https://blog.csdn.net/Professor11/article/details/103552061
好了,进入主题吧,潘老师,他是一位怎么样的老师呢?其实,对于我来说,无论是上学期他教的python还是这个学期教的安卓,我真的没有认真去听课的,哈哈哈,希望潘老师看到不要生气!!所以,我在这里就不评价潘老师的教学质量。但是很幸运的是,每一次他在聊我们未来的发展的时候,我都会在听,起初听的时候,真的没特别留意他说的话,但是,现在回忆起来,觉得他说的每一句话都是很有用的。就在上学期期末,他一如往常地做课程总结,当时的我还处于迷茫状态,并不知道自己真的想做什么,想学什么,但是他在课堂上推荐了一些资源,推荐了一些路线给我们之后,当时我也忘记了他说了什么,我就去做了。接着,我寒假好像开始有意识地寻找自己方向,最终找到了java后端方向,并且现在也在这条路上走,自我感觉良好!嗯,就这么不经意的几句话,就足以改变你的人生,你说厉不厉害!
好了,今天也结课了,在这里,再说一句”谢谢老师!!!“