进程调度

进程调度

一、什么时候须要进程调度

  随着CPU的处理速度的加快,如今计算密集型的程序已经很少了,大量的时间仍是浪费在了IO操做上了,即主要的程序类型从计算密集型向I/O密集型转变,那么进程的调度的决策主要出如今下面几种状况:算法

  一、在建立一个新的进程后,须要先运行父进程仍是先运行子进程,这个须要操做系统来定夺。数据库

  二、在一个进程退出时,必需要作出调度。数组

  三、当一个进程阻塞在I/O和信号量上或者其余缘由阻塞的时候,必需要进行进程调度。多线程

  四、在一个I/O中断产生的时候。并发

  五、在时钟中断产生的时候,须要操做系统决定是否须要进行进程的切换。操作系统

二、调度算法

2.一、批处理系统的调度

一、先来先服务(FCFS).net

  主要的流程是CPU根据他们请求CPU的顺序进行调度,而且为非抢占式的,当某个做业先提交的时候其便拥有了CPU,其会一直向后运行,直到发生阻塞即将其后面的进程分配CPU,在操做系统的内部维护一个就绪队列和一个阻塞队列,被阻塞的进程会放入阻塞线程

队列中,直到阻塞队列中的某些进行变为就绪的时候会被从新放到队列的尾部,好像刚刚到达同样。blog

  优点在于便于理解比较公平,问题在于某一个进程须要1s的计算和0.01s的IO操做,另外的进程堆须要100s的IO操做,这样该线程因为这0.01s的IO操做会被堵住100s,很是不利于CPU密集型的进程。一样的在调度的时候长做业会得到大量的CPU资源队列

段做业相对而言没有,有利于长做业不利于短做业。

二、最短做业优先

  估计各个做业的时间长度,按照时间从短到长进行调度,也是非抢占式的。

  有利于短做业,即短做业的周转时间(从提交到结束做业的时间间隔)会获得下降,长做业的周转时间也没有特别多的提高。可是如何估计一个时间的长短比较困难,同时在做业不断提交的过程当中,长做业有可能一直等待下去,不利于长做业。

三、最短剩余时间优先

  最短做业优先的抢占版本就是最短剩余时间优先,问题依旧和最短做业优先差很少。

2.2 交互式系统中的进程调度

一、轮转调度

  用的最多的一种制度,每一个进程被分配一个时间段,被称为时间片,即容许进程在该时间片内运行,若是时间片结束时该进程还在运行,则将剥夺CPU并分配给另外的进程,若是某个进程在时间片前阻塞或者结束,则CPU当即切换

  问题在于时间片长短的设置,过短的话CPU花太多的时间在上下文切换上,太长的话整个系统的实时性又大打折扣,一般设置为20~50ms。

二、优先级调度

  为每一个进程分配一个优先级,容许优先级最高的可运行程序先运行,优先级可用由认为设定为静态优先级,也可让系统动态设置。

三、多级队列

  其设立优先级类,属于最高优先级类的进程运行1个时间片,属于次高优先级的进程运行2个时间片,再下一级使用4个时间片,以此类推。当一个进程加入队列后其拥有最给的优先级,再运行完时间片后插入到次优先级进行队列尾部,CPU选择

当前最高优先级的进程运行。

  可以兼顾短的进程和长的进程。

2.3 实时系统的进程调度

  实时系统是一种时间起着主导做用的系统,其在书中没有提到固定的算法,可是有一个规则须要把握才能表示该系统是可用调度的,即,其中n为事件数量C为cpu处理一个事件的时间,P为该事件的发生周期,N为CPU的核心数

  

3 、线程的调度

  因为线程有两种类型:用户级线程和内核级的线程,内核级的线程,内核级的线程对操做系统是不可见的,操做系统可见的是线程所在的进程,这也是为何用户级的操做系统支持在非多线程的CPU上运行的缘由,此时在用户级的进程中会存在一个本身的

线程调度算法在操做系统层面上也会存在一个全局的线程调度算法。因为用户级的线程是有单人编写的,所以对于抢占的问题不存在,更多的是合做。

  内核级的线程调度算法,他不用考虑线程属于哪一个线程,相应的线程可用被阻塞,而不用担忧用户级线程的非阻塞调用的问题,可是内核级线程相对于进程级的线程的问题在于内核级的上下文切带来了大量的时间消耗。还有一个区别是用户级的线程可用定制本身的线程调度算法,可是非阻塞调用依旧是其问题。

四、经典的IPC问题

4.1 哲学家问题

  假设有五位哲学家围坐在一张圆形餐桌旁,作如下两件事情之一:吃饭,或者思考。吃东西的时候,他们就中止思考,思考的时候也中止吃东西。餐桌中间有一大碗意大利面,每两个哲学家之间有一只餐叉。由于用一只餐叉很难吃到意大利面,因此假设哲学家必须用两只餐叉吃东西。他们只能使用本身左右手边的那两只餐叉。哲学家就餐问题有时也用米饭和筷子而不是意大利面和餐叉来描述,由于很明显,吃米饭必须用两根筷子。

  可使用规定只有左右两个筷子同时可用的时候才进行就餐,不然放弃本身的资源,可是若是5个哲学家同时就餐那就出现了死锁,可用在没法拿到筷子的状况下随机延迟再从新拿,可是再某些不能出现死锁的状况下是不能够的。如下代码可用有效的处理哲学家问题

#define N
#define left (i+N-1)%N
#define right (i+1)%N
#define THINKING 0
#define HUNGRY 1
#define EATING 2
typedef int semaphore;
int state[N];//记录各个哲学家当前处于的状态(3种)
semaphore mutex=1;//临界区锁
semaphore s[N];//发卡器,对具有吃条件的线程发卡,不具有的条件的就等着
void philosopher(int i)
{
while(TRUE)
{
}
}
void take_forks(int i)
{
down(&mutex);
state[i]=HUNGRY;
test(i);
up(&mutex);
down(&s[i]);
}
void put_forks(int i)
{
down(&mutex);
state[i]=THINKING;
test(LEFT);
test(RIGHT);
up(&mutex)
}
void test(i)
{
if(state[i]==HUNGRY&&state[LEFT]!=EATING&&state[RIGHT]!=EATING)
{
state[i]=EATING;
up(&s[i]);
}
}

  在上述的解法种主要的是s[i]这个数组,经过这个数组咱们可用实现对不具有吃的条件的可是想吃的哲学家发个挂起卡(state[i]=HUNGRY&&s[i]=0),同时在结束吃的时候须要唤醒和其抢资源的哲学家,二唤醒的依据就是被发挂起卡了,此时被挂起的线程就能够运行了。

4.2 读者-写者问题

  有读者和写者两组并发线程,共享一个数据库,当两个或以上的读线程同时访问共享数据时不会产生反作用,但若某个写线程和其余线程(读线程或写线程)同时访问共享数据时则可能致使数据不一致的错误。所以要求[1]

一、容许多个读者能够同时对文件执行读操做;
二、只容许一个写者往文件中写信息;
三、任一写者在完成写操做以前不容许其余读者或写者工做;
四、写者执行写操做前,应让已有的读者和写者所有退出。
  也就是说,读进程不排斥其余读进程,而写进程须要排斥其余全部进程,包括读进程和写进程。在这个问题中就存在一个读者优先仍是写者优先的问题,即,当一个进程在读的时候,后续的读是否该超越后续的写进程优先被执行仍是后续的写先行被调度呢?

这两中有各自的实现。

4.2.1读者优先
typedef int semaphore;
semaphore mutex=1;//该互斥量用来实现对读者数量的互斥,由于该变量对于判断是否写是否重要
semaphore db=1;//该互斥量用来进行对读写者的互斥调用,即读时禁止写
int rc=0;//记录当前读者的数量,由于数量为1的话说明这个进程开启了读,此时须要设置db
void read()
{
while(TRUE)
{
down(&mutex);
rc+=1;
if(rc==1)
  down(&db)
up(&mutex) 
read_data();
down(&mutex)
rc-=1;
if(rc==0)
    up(&db)//当前没有读者了 
up(&mutex);
use_data();
}
}
void writer()
{
while(TRUE)
{
down(&db)
writer();
up(&db);
} }
4.2.2写者优先

  相对于读者优先中存在一个问题,就是写者没法致使读者的长时间挂起,也就是说,当写者完成后第二个写准备就绪的时候有可能被读者抢走了优先权。为了处理这种状况,须要对写者进行改动,读者不变,此时须要一个变量来记录写者的数量,这样使用相似于读者的思路就能够实现写者的优先抢占,下面是个人第一个想法,实际上是错误的,在读者与写者都在等待的时候并不会将临界区的全部权给写者。

semaphore  w_mutex,w_lock;
int wc;
void writer()
{
while(TRUE)
{
down(&w_mutex);//与同时写wc 的造成互斥
wc+=1;
up(&w_mutex);
if(wc==1)//>1的时候依据获取了全部权
    down(&db);//获取数据库全部权  
down(&w_lock)//不可以同时写
write();
up(&w_lock)//写完成
down(&w_mutex);//与同时写wc 的造成互斥
wc-=1;
up(&w_mutex);
if(wc==0)//=0时候,没有写了,交出全部权
    up(&db);//交出全部权  

}

}

  主要的缘由是,虽然在读所有完成后,写操做可用紧紧把握全部权,可是核心问题在于,在读的过程当中依旧可让新的读进程超越写而注册到读中,处理的办法是加一把锁,让后于写进程的进行没法进行读数量+1的操做,所以上述的代码应该这样写:

读者:

typedef int semaphore;
semaphore mutex=1;//该互斥量用来实现对读者数量的互斥,由于该变量对于判断是否写是否重要
semaphore db=1,lock_in=1;//db是用来对资源进行互斥的访问,可是lock_in是用来进行对当前的读进行加锁
int rc=0;//记录当前读者的数量,由于数量为1的话说明这个进程开启了读,此时须要设置db
void read()
{
while(TRUE)
{
down(&lock_in); down(&mutex); rc+=1; if(rc==1) down(&db)
up(&lock_in); up(&mutex) read_data(); down(&mutex) rc-=1; if(rc==0) up(&db)//当前没有读者了 up(&mutex); use_data(); } } void writer() { while(TRUE) { down(&db) writer(); up(&db); } }

写者:

semaphore  w_mutex;
int wc;
void writer()
{
while(TRUE)
{
down(&w_mutex);//与同时写wc 的造成互斥
wc+=1;
up(&w_mutex);
if(wc==1)//>1的时候依据获取了全部权
    down(&lock_in);//加锁,禁止读进程继续添加到读程序中 
down(&db)//db这下身兼多职,不只可以表明了读写互斥了,并且在写里面也可以互斥
write();
up(&db)//写完成
down(&w_mutex);//与同时写wc 的造成互斥
wc-=1;
up(&w_mutex);
if(wc==0)//=0时候,没有写了,交出全部权
    up(&lock_in);//容许读进行进行添加任务

}

}

  

  lock_in 的具体意思是在writer进行写等待的时候,全部的写进程都会被阻塞在down(&db),此时lock_in加锁,这也的后果是在写进程以后的进程都会被阻塞在读者的down(&lock_in),当前的读者一旦完成读的时候全部权db就会给写者,此时写者利用db进行同类互斥的写,注意的是此时后加入的读者还阻塞在down(&lock_in),修改db是无所谓的,在完成写以后也就是wc=0的时候释放阻塞在down(&lock_in)的进程。妙啊!

 4.2.3 公平竞争

  相似的思想能够实现公平竞争,读操做没有变化,可是写却变化为:

semaphore  w_mutex;
int wc;
void writer()
{
while(TRUE)
{
down(&lock_in);//加锁,禁止读进程继续添加到读程序中 ,同时能够被阻塞 down(&w_mutex);//与同时写wc 的造成互斥 wc+=1; up(&w_mutex);
up(&lock_in);//为了将写的其余进程所有读入,可以早释放就早释放。 down(&db)//db这下身兼多职,不只可以表明了读写互斥了,并且在写里面也可以互斥 write(); up(&db)//写完成 down(&w_mutex);//与同时写wc 的造成互斥 wc-=1; up(&w_mutex); } }

  谁先来谁就紧紧把握住lock_in阻止另外一种操做的注册,很是公平,注意同类间的互斥操做又要求该临界区尽量的短,以让其它同类进程注册。并且lock_in 这个锁的做用时间长短直接决定了哪一个优先的操做

[1]、https://blog.csdn.net/sunny_ss12/article/details/47435687

相关文章
相关标签/搜索