进程与线程

注意:

  • 多线程可以提高CPU利用率,不能提高内存利用率

进程

进程七状态转换图

活动就绪------suspend------>静止就绪
活动就绪<------active------静止就绪
活动阻塞------suspend------>静止阻塞
活动阻塞<------active------静止阻塞
运行-->就绪 原因:时间片用完/被抢占(优先级等)
进程挂起的原因有:
1、终端用户的请求
2、父进程的请求
3、负荷调节的需要
4、操作系统的需要
进程出现故障将进入终止状态

  • wait()、notify()和notifyAll():Object类中的方法
    从这三个方法的文字描述可以知道以下几点信息:
    1)wait()、notify()和notifyAll()方法是本地方法,并且为final方法,无法被重写。
    2)调用某个对象的wait()方法能让当前线程阻塞,并且当前线程必须拥有此对象的monitor(即锁)
    3)调用某个对象的notify()方法能够唤醒一个正在等待这个对象的monitor的线程,如果有多个线程都在等待这个对象的monitor,则只能唤醒其中一个线程
    4)调用notifyAll()方法能够唤醒所有正在等待这个对象的monitor的线程
    有朋友可能会有疑问:为何这三个不是Thread类声明中的方法,而是Object类中声明的方法(当然由于Thread类继承了Object类,所以Thread也可以调用者三个方法)?其实这个问题很简单,由于每个对象都拥有monitor(即锁),所以让当前线程等待某个对象的锁,当然应该通过这个对象来操作了。而不是用当前线程来操作,因为当前线程可能会等待多个线程的锁,如果通过线程来操作,就非常复杂了。
  • Condition是个接口,基本的方法就是await()和signal()方法;(Condition是在java 1.5中才出现的,它用来替代传统的Object的wait()、notify()实现线程间的协作,相比使用Object的wait()、notify(),使用Condition1的await()、signal()这种方式实现线程间协作更加安全和高效。因此通常来说比较推荐使用Condition。)
    Condition依赖于Lock接口,生成一个Condition的基本代码是lock.newCondition()
    调用Condition的await()和signal()方法,都必须在lock保护之内,就是说必须在lock.lock()和lock.unlock之间才可以使用Conditon中的await()对应Object的wait(); Condition中的signal()对应Object的notify(); Condition中的signalAll()对应Object的notifyAll()
  • ThreadLocal类用于创建一个线程本地变量
    ThreadLocal存放的值是线程封闭,线程间互斥的,主要用于线程内共享一些数据,避免通过参数来传递
    线程的角度看,每个线程都保持一个对其线程局部变量副本的隐式引用,只要线程是活动的并且 ThreadLocal 实例是可访问的;在线程消失之后,其线程局部实例的所有副本都会被垃圾回收
    在Thread类中有一个Map,用于存储每一个线程的变量的副本。
    对于多线程资源共享的问题,同步机制采用了“以时间换空间”的方式,而ThreadLocal采用了“以空间换时间”的方式
  • sleep()
    sleep()是Thread类中的方法,而wait()则是Object类中的方法。sleep()方法导致了程序暂停,但是他的监控状态依然保持着,当指定的时间到了又会自动恢复运行状态。在调用sleep()方法的过程中,线程不会释放对象锁。 wait()方法会导致线程放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象调用notify()方法后本线程才进入对象锁定池准备获取对象锁进入运行状态。
    注意是准备获取对象锁进入运行状态,而不是立即获得

创建进程

  1. 申请空白PCB(进程控制块);
  2. 为新进程分派资源;
  3. 初始化PCB;
  4. 将新进程插入就绪队列;

进程控制块PCB(Process Control Block)

注意:

Unix操作系统的进程控制块中常驻内存的是proc
Linux创建进程的时候,同时创建进程控制块和进程对应的堆栈。该堆栈分为用户栈和系统栈,也就是核心栈。用户栈在用户态使用,用来保存局部变量,函数参数等,而系统栈在进程陷入内核态使用,里面保存的是用户栈的地址,以及在进行进程上下文切换时需要保存的参数,返回值等。因为每个进程都有可能发生系统调用陷入内核,从用户态到内核态发生一个中断,那么内核栈就会保存这个进程操作内核的调用信息,如果常驻内存的话就会被下个进程发生的系统调用的信息覆盖掉,所以内核栈是不会常驻内存的
proc存放的是系统经常要查询和修改的信息,需要快速访问,因此常将其装入内存 。

PCB一般包括

  1. 程序ID(PID、进程句柄):它是唯一的,一个进程都必须对应一个PID。PID一般是整型数字
  2. 特征信息:一般分系统进程、用户进程、或者内核进程等
  3. 进程状态:运行、就绪、阻塞,表示进程现的运行情况
  4. 优先级:表示获得CPU控制权的优先级大小
  5. 通信信息:进程之间的通信关系的反映,由于操作系统会提供通信信道
  6. 现场保护区:保护阻塞的进程用
  7. 资源需求、分配控制信息
  8. 进程实体信息,指明程序路径和名称,进程数据在物理内存还是在交换分区(分页)中
  9. 其他信息:工作单位,工作区,文件信息等

Linux下多线程编程常用的pthread库提供的函数名和意义
pthread_create 创建一个线程
pthread_join用来等待一个线程的结束
pthread_mutex_init 初始化一个线程互斥锁
pthread_exit结束一个线程

进程间通信(IPC,InterProcess Communication)

通信方法:

  1. 高级通信: 文件记录及锁定,管道、信号、共享内存、Socket通信、远程过程调用(RPC),消息传递(直接:消息缓冲区;间接:信箱)等。
  2. 低级通信:管程,信号量
  • 管道( pipe ):管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。
  • 有名管道 (named pipe) : 有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信。
  • 信号量( semophore ) : 信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。
  • 消息队列( message queue ) : 消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。
  • 信号 ( signal ) : 信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。
  • 共享内存( shared memory ) :共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的 IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号两,配合使用,来实现进程间的同步和通信。
  • 套接字( socket ) : 套解口也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同及其间的进程通信。

fork

fork()
使用fork函数得到的子进程从父进程的继承了整个进程的地址空间,包括:进程上下文、进程堆栈、内存信息、打开的文件描述符、信号控制设置、进程优先级、进程组号、当前工作目录、根目录、资源限制、控制终端等。
子进程与父进程的区别在于:
1、父进程设置的锁,子进程不继承(因为如果是排它锁,被继承的话,矛盾了)
2、各自的进程ID和父进程ID不同
3、子进程的未决告警被清除;
4、子进程的未决信号集设置为空集。 -mickole博客

  • sleep()方法强制使当前线程休眠,释放CPU资源,以便使得其他所有线程有机会运行。
  • yield()方法使得当前的线程让出CPU的使用权,以使得比该线程优先级相同更高的线程有机会运行。该线程在让出CPU使用权之后可能再次被选中,因此yield()方法可能会不起作用(这也说明了yield()方法不会使得比当前线程优先级低的线程运行)。
  • java虚拟机中如果多个线程优先级相同,则会随机选择一个线程占用CPU,处于运行状态的线程会一直运行,直至它不得不放弃CPU为止,因此不一定是分时调度。
  • 分时调度模型是让所有线程轮流获得CPU使用权。

进程互斥实现机制

信号量
对于记录型信号量,当 s<0 的时候,请求进程会阻塞
对于整型信号量,当s<=0的时候,请求进程不会阻塞,而是进入盲等状态

  • -和+,DOWN()和UP(),SLEEP()和WAKEUP(),P()和V(),Wait() 和Signal()。虽然它们名字都不一样,可意思都是相同的。前者是申请资源,后者是释放资源。
    生产者与消费者
    生产者与消费者
    举例:
    单生产者单消费者:
#define N 100
typedef int semaphore;
semaphore empty = N;
semaphore full = 0;
void producer(void)
{
    int item;
    while(TRUE){
        item = produce_item();
        down(&empty);
        insert_item(item);
        up(&full);
    }
}
void consumer(void)
{
    int item;
    while(TRUE){
        down(&full);
        item = remove_item();
        up(&empty);
        consume_item(item);
    }
}

多生产者多消费者:

#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(&full);
        up(&mutex);
    }
}
void consumer(void)
{
    int item;
    while(TRUE){
        down(&full);
        down(&mutex);
        item = remove_item();
        up(&empty);
        up(&mutex);
        consume_item(item);
    }
}

低级调度

  • 如果系统只有用户态线程,则线程对操作系统是不可见的,操作系统只能调度进程;
  • 如果系统中有内核态线程,则操作系统可以按线程进行调度;
  • 系统态,即内核态,三种情况下会出现由用户态转为内核态,中断,异常,系统调用
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,440评论 5 467
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,814评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,427评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,710评论 1 270
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,625评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,014评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,511评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,162评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,311评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,262评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,278评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,989评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,583评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,664评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,904评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,274评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,856评论 2 339

推荐阅读更多精彩内容

  • 又来到了一个老生常谈的问题,应用层软件开发的程序员要不要了解和深入学习操作系统呢? 今天就这个问题开始,来谈谈操...
    tangsl阅读 4,085评论 0 23
  • 处理器架构 主要有两种选择:单个多核处理器和多个单核处理器。 核心 处理器核心是CPU重要组成部分。处理器所有的计...
    狮_子歌歌阅读 686评论 0 2
  • 进程 进程模型 操作系统中最核心的概念是进程:这是对正在运行程序的一个抽象。一个进程就是一个正在执行程序的实例,包...
    SeaRise阅读 446评论 0 0
  • 前言 当我们使用计算机时,可以同时做许多事情,例如一边打游戏一边听音乐。这是因为操作系统支持并发任务,从而使得这些...
    我没有三颗心脏阅读 4,393评论 1 12
  • 其实,在写上一篇文字的时候,并没有把《撒哈拉的故事》看完,与家人的书信当时是没有看的。 与家人通信时的三毛,更加感...
    睐美人阅读 428评论 1 1