码迷,mamicode.com
首页 > 编程语言 > 详细

进程与线程

时间:2015-10-13 12:03:39      阅读:241      评论:0      收藏:0      [点我收藏+]

标签:

进程

?

进程间通信总结

  1. 一个进程如何把信息传递给另一个
  2. 确保两个或更多进程在关键活动中不会出现交叉
  3. 顺序

?

临界区

定义:对共享内存进行访问的程序片段

?

优秀的解决方案的4个条件

  1. 任何两个进程不能同时处于临界区
  2. 不应对CPU的速度和数量做任何假设
  3. 临界区外运行的进程不得阻塞其他进程
  4. 不得使进程无限期等待进入临界区

?

实现互斥的方案

  1. 屏蔽中断

    适用于内核进程、不适合用户进程

  2. 锁变量

    共享锁,初始值为0,测试它

  3. 严格轮换法
  4. ????while (true){
  5. ????????while (turn != 1)
  6. ????????????critical_region();
  7. ????????turn = 0;
  8. ????????noncritical_region();
  9. ?

    用于忙等待的锁称为自旋锁

  10. Peterson解法

#define FALSE????0

#define TRUE????1

#define N????????2????/*进程数量*/

?

int turn;????????????/*现在轮到谁*/

int interested[N]; /*所有值初始化为0:FALSE*/

?

void enter_region(int process){ /*进程号 0 或 1*/

????int other; /*其他进程*/

????other = 1 - process; /*另一方进程*/

????interested[process] = TRUE;/*表明自己感兴趣*/

????turn = process;/*设置标志*/

????while (turn == process && interested[other] == TRUE);/*空语句*/

}

?

void leave_region(int process){/*将要离开的进程*/

????interested[process] = FALSE;/*表明不感兴趣*/

}

?

  1. TSL指令

    Test and Set Lock 测试并加锁

    指令如下:

    TSL RX, LOCK

    它将一个内存字lock读到寄存器RX中,然后再该内存地址上存一个非零值。读字和写字操作保证是不可分割的,即该指令结束之前其他处理器均不能访问该内存字。执行TSL指令的CPU将锁住内存总线,以禁止其他CPU在本指令结束之前访问内存。

    ?

    实现:

    ?

    enter_region:

    ????????TSL REGISTER, LOCK

    ????????CMP REGISTER, #0

    ????????JNE enter_region

    ????????RET

    ?

    Leave_region:

    ????????MOVE LOCK, #0

    ????????RET

    ?

    XCHG 指令 原子性地交换两个位置的内容

    实现:

    ?

    enter_region:

    ????MOVE REGISTER, #1

    ????XCHG REGISTER, LOCK

    ????CMP REGISTER, #0

    ????JNE enter_region

    ????RET

    ?

    Leave_region:

    ????MOVE LOCK, #0

    ????RET

优先级反转:

考虑有两个进程L和H, 优先级 L < H,也就是说,只要H出于就绪状态它就可以运行。 在某一个时刻, L出于临界区中,此时H变到就绪状态,准备运行。现在H开始忙等待, 但由于当H出于就绪态L不会被调度,也就无法离开临界区,所以H将永远忙等待下去。

睡眠与唤醒

错误解法

#define N????100????????/*缓冲区中的槽数*/

int count = 0;

?

void producer(void){

????int item;

????while (true){

????????item = produce_item();

????????if (count == N) sleep();

????????insert_item(item);

????????count++;

????????if (count == 1) wakeup(consumer);

????}

}

?

void consumer(void){

????int item;

????while (true){

????????if (count == 0) sleep();

????????item = remove_item();

????????count--;

????????if (count == N - 1) wakeup(producer);

????????consume_item(item);

????}

}

错误原因是对count的访问不加限制。考虑这样的情况,当count == 0 时,consumer 从内存中取得 count准备测试的时候,调度器将consumer换出并执行producer,此时producer生产item,并将count加1, 并猜测 consumer此时一定在休眠中,然而consumer并未休眠(准备测试count或准备进入休眠,被调度器换出),所以wakeup信号将丢失。而当调度器又重新让consumer执行的时候,它测试之前得到的count值 ==0 ,于是进入休眠。Producer继续生产item,总有一刻槽位满了,producer也进入休眠,最终两个进程都陷入无止境的休眠中!

?

信号量(semaphore)

?

适用一个整型变量来累计唤醒次数,以供以后适用。

使用信号量解决生产者-消费者问题

#define N 100

typedef int semaphore;

semaphore mutex = 1;

semaphore empty = N;

semaphore full = 0;

?

void producer(void){

????int item;

????while (true){

????????item = produce_item();

????????down(&empty);

????????down(&mutex);

????????insert_item(item);

????????up(&mutex);

????????up(&full);

????}

}

?

void consumer(void){

????int item;

????while (true){

????????down(&full);

????????down(&mutex);

????????item = remove_item();

????????up(&mutex);

????????up(&empty);

????????consume_item(item);

????}

}

?

二元信号量

mutex 初始值为1,供两个或多个进程适用的信号量。保证同时只有一个进程可以进入临界区,称为二元信号量。

?

互斥量(mutex)

如果不需要信号量的技术能力,有时可以使用信号量的简化版本,称为互斥量。

当一个线程或进程需要访问临界区时,它调用mutex_lock,如果该互斥量当前是解锁的——临界区当前可用,此调用成功,该线程可以进入该临界区。

如果互斥量已经加锁,调用线程被阻塞,直到临界区中的线程完成并调用metux_unlock。

?

mutex_lock 和 mutex_unlock 的实现

mutex_lock:

????TSL REGISTER, MUTEX????????|

????CMP REGISTER, #0

????JZE ok

????CALL thread_yield

????JMP mutex_lock

ok:????RET

?

mutex_unlock:

????MOVE MUTEX, #0

????RET

?

Pthread中的互斥 Posix 线程库

?

互斥量相关的调用

?

调度

?

批处理调度算法

  1. 先来先服务
  2. 最短作业优先
  3. 最短剩余时间优先

?

?

交互式系统中的调度算法

  1. 轮转调度

    时间片设得太短会导致过多的进程切换,降低了CPU效率;而设得太长有可能引起对短的交互请求的响应时间过长

  2. 优先级调度

    可能产生饥饿现象

  3. 多级队列

    CTSS,

  4. 最短进程优先

    如何找到那个最短的进程?

    1. 根据过去的行为进行推测

      当a = 1/2,有

      T0, T0/2 + T1/2, T0/4+T1/4+T2/2, …. 即新的值加上老的值除以2,右移一位

  5. 保证调度

    向用户做出明确的性能保证,然后去实现它。比如:若用户工作时有n个用户登录,则用户将获得CPU处理能力的1/n

  6. 彩票调度

    向进程提供各种系统资源的彩票,一旦需要做出一项调度决策,就随机抽出一张彩票,拥有该彩票的进程获得该资源。

  7. 公平分享调度

    以上的调度策略,调度的是进程自身,并不关心进程的拥有着是谁。

?

实时系统中的调度

特点:正确的但是迟到的回答往往比没有更糟糕。

有m个周期事件,时间i以周期Pi发生,并需要Ci秒的cpu时间

可调度条件:

?

Sigma(i= 1~m) [ Ci/ Pi] <= 1

?

?

线程调度

  1. 用户线程
  2. 内核线程

    不用考虑线程属于哪个进程

?

经典的IPC问题

?

哲学家就餐问题

错误的解法

#define N????5

?

void philosopher(int i){

????while (true){

????????think();

????????take_fork(i);

????????take_fork((i + 1) % N);

????????eat();

????????put_fork(i);

????????put_fork((i + 1) % N);

????}

}

改进:使用一个二元信号量,对think 之后的五个语句进行保护。在开始拿叉之前,哲学家现对互斥量mutex执行down操作。在放回叉子之后,它在对mutex执行up操作。

缺点:性能低下,任一时刻只能有一位哲学家就餐,而5把叉子能够允许两个哲学家同时就餐。

?

?

正确的解法

#define N????????5

#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];

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(int i){

????if (state[i] == HUNGRY && state[LEFT] != EATING && state[RIGHT] != EATING){

????????state[i] = EATING;

????????up(&s[i]);

????}

}

?

?

?

读者写着问题:

?

在一个读者到达时,且一个写者在等待时,读者在写者之后被挂起,而不是立即允许进入。

一种解法

typedef int semaphore;

semaphore mutex = 1;

semaphore db = 1;

int rc = 0;

?

void read(void){

????while (TRUE){

????????down(&mutex);

????????rc = rc + 1;

????????if (rc == 1) down(&db);

????????up(&mutex);

????????read_data_base();

????????down(&mutex);

????????rc = rc - 1;

????????if (rc == 0) up(&db);

????????up(&mutex);

????????use_data_read();

????}

}

void writer(void){

????while (TRUE){

????????think_up_data();

????????down(&db);

????????write_data_base();

????????up(&db);

????}

}

进程与线程

标签:

原文地址:http://www.cnblogs.com/jimmysue/p/4874042.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!