操作系统管道的概念_操作系统管道 - CSDN
精华内容
参与话题
  • 第2章 处理机管理 2.1概述 2.1.1多用户(1)多用户: 多用户同时通过终端连接到计算机主机(2)宏观:在同一个时间范围内独立地使用计算机系统(3)微观:各用户程序并没有同时使用计算机的资源,共享只能是时间上的分割 ...

    第2章 处理机管理

     

    2.1概述

     

    2.1.1多用户

    (1)多用户: 多用户同时通过终端连接到计算机主机

    (2)宏观:在同一个时间范围内独立地使用计算机系统

    (3)微观:各用户程序并没有同时使用计算机的资源,共享只能是时间上的分割

     

    2.1.2程序

    (1)程序: 计算机处理的一系列的指令,按照一定的逻辑要求被划分成多个相关模块,这些模块必须顺序地执行

    (2)程序顺序执行的特征:顺序性,封闭性,可再现性

    (3)程序的并发执行的特征:间断性,失去封闭性,不可再现性

    (5)并发程序三个特点:动态性,制约性,并行性

    (6)并发程序:逻辑上并行,物理上串行

    1)物理上的串行:CPU串行地执行着一定大小的程序片断

    2)逻辑上的并行 :从宏观上看,在一个时间范围内,每一个程序都获得了运行

     

    2.1.4Linux中的描述

    (1)任务:Linux是一个多任务系统,程序的并行就是任务的并行,任务作为一个实体具有申请、占有、释放和抢占资源的资格

    (2)进程:进程之间有一种隶属关系,除隶属关系外它们又是相互独立的,可以产生,也可以消亡

     

    2.2进程及其状态

    2.2.1进程的定义

     

    (1)进程的引入:由于多道程序的特点,程序具有了并行、制约和动态的特征,就使得原来程序的概念已难以刻划和反映系统中的情况了

    (2)定义:

    1)进程是并发程序的一次执行过程

    2)进程是一个具有一定独立功能的程序关于某个数据集合的一次运行活动

    (3)进程:程序在并发环境下的执行过程

    (4)本质:

    1)进程的存在必然需要程序的存在

    2)进程是系统中独立存在的实体

    3)并发特性通过对资源的竞争来体现动态特性通过状态来描述

    4)进程和数据相关

    (5)进程与程序的主要区别:

    1)程序是永存的,进程是暂时的

    2)程序是静态的观念,进程是动态的观念 

    3)进程由三部分组成:程序+数据+进程控制块PCB(描述进程活动情况的数据结构) 

    4)进程和程序不是一一对应的:一个程序可对应多个进程即多个进程可执行同一程序 一个进程可以执行一个或几个程序 

    5)进程是一个独立的运行单位,能与其它进程并发(并行)执行。而程序则不可以并发执行。

    6)进程是进行计算机资源分配的基本单位,也是进行处理机调度的基本单位

    (6)进程特征:动态性、并发性、调度性、异步性、结构性

    (7)进程的分类

    系统进程:是执行操作系统核心代码的进程,起到对系统资源的管理和控制作用。

    用户进程:主要是执行用户程序的进程。

    系统进程和用户进程的区别:

    1)系统进程可以独占分配给它们的资源,也能以最高优先级运行;而用户进程需要通过系   统服务请求(系统调用)的方式来竞争使用系统资源。

    2)用户进程不能直接执行I/O操作,而系统进程可以。

    3)系统进程在内核态(管态)下活动,而用户进程则是在用户态(目态)下活动。

    2.2.2进程的状态及其转换

    (1)进程基本状态:

    1)运行态(Running):进程正在占用CPU;

    2)就绪态(Ready):进程具备运行条件,但尚未占用CPU;

    3)阻塞态(Blocked):进程由于等待某一事件不能享用CPU。

     

    (2)进程状态的转换:

    1)就绪态->运行态  2)运行态->就绪态  3)运行态->阻塞态  4)阻塞态->就绪态

     

    (3) UNIX进程状态及转换

    创建状态:进程刚被建立还没有被激活

    内存就绪:进程的数据区处于内存并被激活 

    外存就绪:进程的数据区处于外存并被激活

    核心运行:从内存就绪队列中调度一进程运行,处理机运行系统程序

    用户运行: 当前运行的是用户程序

    内存睡眠:处于核心运行态的进程申请某种资源而不能获得时,进程就变为内存睡眠状态

    外存睡眠:进程处于睡眠状态并且数据区处于外存

    被抢先:进程正从核心运行态向用户运行态转换时发生进程调度,它将处于被抢先状态

    僵死状态:进程完成了它的所有任务将被撤销之前

     

     

     

     

    (3)     Linux进程状态及转换

     

     

    2.2.3进程描述机构和进程实体

     

    2.2.3.1进程控制块

    (1)进程控制块PCBProcess Control Block:描述进程的数据结构,一个进程和其它进程以及系统资源的关系,记录了进程在各个不同时期所处的状态

    (2)进程控制块的作用:进程控制块是进程组成中最关键的部分。

    1)每个进程有唯一的PCB。 

    2)操作系统根据PCB对进程实施控制和管理。

    3)进程的动态、并发等特征是利用PCB表现出来的。

    4)PCB是进程存在的唯一标志

     

    2.2.3.2进程实体

    (1)进程实体由三部分构成:程序段数据段进程控制块PCB

    (2)进程实体的三种表现形式:

    1)程序段、数据段、PCB  

    2)数据段、PCB  

    3)数据段、PCB

     

    2.2.3.3PCB的组织

    (1)PCB的组织:为了统一管理、控制和调度进程,操作系统将进程控制块集中组织

    (2)PCB组织方式:线性队列、链接表、索引表

    (3)典型的形式

    PCB表:系统中专门开辟依次存放所有进程的PCB的一个区域

    进程队列:将不同状态进程分别组成队列的形式

     

    2.2.3.4进程上下文

    (1)进程上下文:进程物理实体支持进程运行的环境

    (2)进程上下文包括三个组成部分:

    用户级上下文:由用户程序段、用户数据段(含共享数据块)和用户堆栈组成的进程地址空间。

    系统级上下文:包括静态部分如PCB和资源表格,以及动态部分如核心栈、现场信息和控制信息、进程环境块、以及系统堆栈等组成的进程地址空间。

    寄存器上下文:由程序状态字寄存器和各类控制寄存器、地址寄存器、通用寄存器组成

     

    2.2.3.4进程虚空间

     进程的虚拟空间:将进程的PCB、用户堆栈、用户私有地址空间、共享地址空间等内容组合在一个具有一定逻辑顺序的空间

     

    2.3进程控制

    (1)进程从产生到消亡的整个过程都由操作系统来控制的,而操作系统只不过是一系列的

    能够独立运行、具有特定功能的程序

    (2)操作系统拥有某些特权,它是被称为系统调用或者原语的程序

    (3)在操作系统中,和计算机硬件直接打交道的程序被组织在一起,称为操作系统的内核

    (4)内核通过执行各种原语操作来实现各种控制和管理功能

    2.3.1原语

    (1)原语:执行过程中不可中断的、实现某种独立功能的、可被其他程序调用的程序

    2.3.2进程控制原语

    (1)进程控制原语:对进程的行为进行控制

    (2)最基本的进程控制原语:进程建立、进程调度、进程等待、进程唤醒、进程撤消

    (3)进程建立中, 父进程在调用创建原语之前必须准备的参数进程标识符、进程优先级以及进程程序的起始地址

    2.3.3Linux中的进程控制

    (1)进程建立fork()函数

    (2)进程等待wait()函数

    (3)进程自我终止exit()函数

    (4)进程删除kill()函数

    (5)信号signal()函数(可以用来终止进程)

    (6)获取进程的IDgetpid( )函数

    (7)获取进程之父进程IDgetppid( )函数

     

    2.3.4Windows中的进程控制 

     (1)Windows提供的系统调用称为应用程序编程接口 (API),它是应用程序用来请求和完成计算机操作系统执行的低级服务的一组例程

    CreateProcess()进程的初始化,建立进程与可执行文件之间的关系,标志进程状态,配置进程的输入输出

    GetGuiResources()对进程GUI资源的查看,它返回打开的GUI对象的数目,也可返回 指定进程中打开的USER对象的数目

    GetProcessVersion()进程版本查看

    SetProcessPriorityBoost()进程优先级提升

    ExitProcess()TerninateProcess()两个终止进程函数,前者先完成对进程资源的关 闭,再调用后者实现进程本身的终止

    (2)线程:进程中的逻辑小块,它具有挂起自身被挂起的能力,因此线程的状态是可以变化的

    线程创建CreateThread():它为线程分配空间,指定线程的起始地址等

    线程挂起SuspendThread()

    线程恢复ResumeThread()

    (3)在Windows中由微内核来管理线程的执行

    (4)微内核将CPU的时间划分成小的时间片,当线程获得一个时间片时就得以运行

    (5)线程的引入对系统的并行和提高效率带来了极大的好处:

    1)省去了资源申请环节,创建一个新线程花费时间少

    2)由于线程上下文只包含和CPU相关的内容,两个线程切换时花费时间少

    3)同一进程内的线程共享所有资源,之间相互通信无须调用内核

    (6)进程和线程的区别和联系:

    1)进程是拥有应用程序所有资源的对象

    2)线程是进程中一个独立的执行路径 

    3)一个进程至少要有一个线程,这个线程被叫做主线程

    4)一个进程的线程越多,该进程获得的CPU时间就越多,进程的运行时间就越快

    5)所有线程参与对CPU时间片的竞争

    6)线程运行时共享其对应进程所拥有的资源 

    2.4进程同步

    2.4.1互斥关系

    同步:是进程间共同完成一项任务时直接发生相互作用的关系(进程之间的一种协调配合关系)

     

    2.4.2同步关系

    互斥:若干进程竞争进入临界段时,相互之间所形成的排它性关系(从广义上讲它也属于同步关系的范畴)

     

    2.4.3临界区实现方法

     (1)临界资源(critical resource):是一次只能被一个进程使用的资源

    (2)临界段/临界区(critical section)是使用临界资源的程序段

    (3)互斥进入临界区的准则:

    1)每次至多只允许一个进程处于临界段中。

    2)对于请求进入临界段的多个进程,在有限时间内只让一个进入。

    3)进程只应在临界段中停留有限时间。

    4)如果进程不能进入自己的临界区,则应让出CPU,避免进程出现“忙等”现象。

    (4)临界区实现方法

    对于互斥,可以统一描述为:

         临界区入口手段;

              临界区;

        临界区出口手段;

    (5)临界区本身无须改变,关键是设计出有效的出入口方案,来实现临界区原则

    最简单的软件算法

    Dekker算法

    Peterson算法

    硬件指令“测试并设置(TS)”

    中断屏蔽方法

    2.4.4用P、V操作实现互斥与同步

    (1)信号量: 是一个数据结构

    1)信号量定义:信号量(信号灯)=<信号量的整型变量值S,指向PCB的指针Q>

    2)信号量的物理意义:

    1.初始化时,整型变量S为一非负整数

    当S变量的值为负数时,该值的绝对值代表Q指针所指向的进程控制块PCB的数目,被该指针所指向的进程处于等待状态

    当S变量的值不为负数时,Q指针指向空

    2.信号量初值为非负的整数变量,代表资源数。

    3.信号量值可变,但仅能由P、V操作来改变。

     

    (2)P操作原语P(S) 

    1)P操作一次,S值减1,即S=S-1(请求分配一资源); 

    2)如果S≥0,则该进程继续执行;如果S<0表示无资源,则该进程的状态置为阻塞态,把相应的PCB连入该信号量队列的末尾,并放弃处理机,进行等待(直至另一个进程执行V(S)操作)。

     

    (3)V操作原语(荷兰语的等待)V(S)  

    1)V操作一次,S值加1,即S=S+1(释放一单位量资源); 

    2)如果S>0,表示有资源,则该进程继续执行;如果S≤0,则释放信号量队列上的第一个PCB所对应的进程(阻塞态改为就绪态),执行V操作的进程继续执行。 

     

    (4)进程间简单同步与互斥的实现

    1)用P,V原语实现互斥的一般模型:设互斥信号量mutex初值为1 

    2)用P、V原语实现简单同步的例子:S1缓冲区是否空(0表示不空,1表示空),初值S1=0; S2缓冲区是否满(0表示不满,1表示满),初值S2=0; 

    3)生产者——消费者问题(OS典型例子):mutex互斥信号量,初值为1;full满缓冲区数,初值为0;empty空缓冲区数,初值为N;

    4) 实现计数: 设有M个进程都要以独享的方式用到某一种资源,且一次只申请一个资源,该种资源的数目为N, 当前有| N-M |个进程在等待资源

     

    (5)Windows中的互斥与同步

    1)互锁函数Interlock用来实现线程间一个长整数的读写,保证当一个线程正在修改该长整数值时,另一个有同样企图的线程被锁定等待

    2)对临界段CriticalSection的操作

    3)事件Event允许一个线程对其受信状态进行直接的控制

    4) 互斥体Mutexes引导对共享资源的访问

    5)信号量Semaphores用来记录可访问某一资源的最大线程数

     

    2.5进程通信

    进程通信:进程之间的信息交换

    通信方式有:消息通信,管道

     

    2.5.1消息通信

     (1)进程间的数据交换以消息为单位

    (2)用户直接利用系统中提供的一组通信命令(原语)进行通信

    (3)消息msg通常由消息头消息正文构成:

    1)消息头

    msgsender消息发送者

    msgreceiver消息接收者

    msgnext下一个消息的链指针

    msgsize整个消息的字节数

    2)消息正文

    msgtext消息正文

    (4)消息通信分类:

     

    1)直接通信方式

     

    2)间接通信方式

    发送进程使用发送原语直接将消息发送到某种中间实体(邮箱)中,接收进程使用接收原语从该中间实体中取出消息,也称为邮箱通信

     

    2.5.2管道

    (1)在两个进程的执行过程中,如果一个进程的输出是另一个进程的输入,可以使用管道文件

    (2)在Linux系统中,使用符号“|”来表示已建立管道文件

     

    (3)管道通信

    输入进程:从进程A的输出区读数据,写入管道文件

    管道文件;这是一个临时文件,输入进程向它写信息,输出进程从它读信息

    输出进程:将管道文件的数据读出,写入进程B的输入区

    (4)管道通信的关系

    互斥关系——输出和输入进程不可能同时读或者写

    同步关系——当管道文件为空时,输出进程等待输入进程

                当管道文件为满时,输入进程等待输出进程

    (4)管道:是数据流,它既可以是单向的,也可以是两个进程间双向的,又被分为匿名管道和命名管道

    2.5.3Windows中的进程通信

    邮件位——是单向机制,一个进程留下消息,等待另一个进程接收

    对邮件位的操作:

    创建邮件位CreateMailslot()

    读取邮件位信息GetMailslotInfo()

    改变邮件位信息SetMailslotInfo()

    对于邮件位中的数据,windows是通过对文件的操作来实现数据的读写的

    2.5.4Linux中的进程通信

    信号(Signals)属于Linux系统的低级通信,主要用于在进程之间传递控制信号

    管道(Pipes)是UNIX操作系统传统的进程通信技术,包括无名管道和有名管道两种,通过文件系统来实现

    消息队列(Message Queues)允许一个或多个进程写消息,一个或多个进程读取消息

    信号灯(Semaphores)最简单的形式就是内存中一个位置,它的取值可以由多个进程检验和设置

    共享内存方式可以使不同进程共同访问一块虚拟存储空间,通过对该存储区的共同操作来实现数据传递

     

    2.6死锁

    2.6.1死锁定义

    (1)死锁: 是若干进程都无知地等待对方释放资源而处于无休止的等待状态

    (2)产生死锁的根本原因:资源有限且操作不当。

     

    2.6.2死锁发生的必要条件

    (1)死锁产生的必要条件:资源的互斥使用,资源不可抢占,资源的部分分配,循环等待

    2.6.3对抗死锁

    解决死锁的三种方法:运行前预防,运行中避免,运行后解除 (检测与恢复)


    2.6.3.1运行前预防

     死锁预防的基本思想和可行的解决办法 

    1.死锁预防的基本思想:打破产生死锁的四个必要条件的一个或几个。

    2.预防死锁的策略:资源预先分配策略、资源有序分配策略。 

    1)资源预先分配策略:打破占有且申请条件,进程在运行前一次性地向系统申请它所需要的全部资源,如果所序言的全部资源得不到满足,则不分配任何资源,此进程暂不运行。 

    2)资源有序分配策略(序列号升序或减序)打破循环等待条件,把资源事先分类编号,按序分配,使进程在申请、占用资源时不会形成环路.

     

    什么是进程的安全序列,死锁与安全序列的关系 

    1.安全序列的定义:所谓系统是安全的,是指系统中的所有进程能够按照某一种次序分配资源,并且依次地运行完毕,这种进程序列{P1,P2,…,Pn}就是安全序列。 

    2.安全序列{P1,P2,…,Pn}是这样组成的:若对于每一个进程Pi(1≤i≤n),它需要的附加资源可以被系统中当前可用资源加上所有进程Pj(j<i)d当前占有资源之和所满足,则{P1,P2,…,Pn}为一个安全序列。 

    3.安全序列与死锁的关系:虽然存在安全序列一定不会有死锁发生,但是系统进入不安全状态(四个死锁的必要条件同时发生)也未必会产生死锁,当然,产生死锁后,系统一定处于不安全状态。 

     

    2.6.3.2运行中避免

     

    死锁的避免与银行家算法

    1.避免死锁的方法:银行家算法。 

    2.银行家算法的基本思想:分配资源之前,判断系统是否是安全的;若是,才分配。 

     

    2.6.3.3运行后解除

     

    死锁检测 

    1. 死锁的检测算法:是当进程进行资源请求时检查并发进程组是否构成资源的请求和占用环路。如果不存在这一环路,则系统中一定没有死锁。

    2. 总之:如果资源分配图中不存在环路,则系统不存在死锁;反之如果资源分配图中存在环路,则系统可能存在死锁,也可能不存在死锁。

     

    死锁恢复:

    1.死锁的恢复思想:一旦在死锁检测时发现死锁,就要消除死锁,使系统从死锁中恢复过来。 2.死锁的恢复方法:

    1)系统重新启动

    2)撤消进程、剥夺资源

     

     

    2.7实用系统中的进程

    2.7.1任务和进程

    任务和进程是对同一实体在不同时期的不同称呼

    任务表现为用户使用的可执行的单元

    在任务没有被启用时,它只是存在于辅助存储器上的一组程序和数据,它们被记录在Windows的注册表中

    通过鼠标对任务图标的点击,任务被装入内存中,并且开始运行,这时它就被称为进程

    此时进程拥有系统资源和私有资源,在进程运行终止时资源被释放或者关闭

    在基于进程的多任务环境下,两个或者多个进程可以并发执行

     

    2.7.2线程

    线程表示进程中可以并发执行的程序段,它是可执行代码的不可拆散的单位

    一个进程必须具有至少一个线程,多个线程可并行地运行于同一个进程中

    一个进程内的所有线程都共享进程的虚拟地址空间,因此,可以访问对应进程拥有的全局变量和资源

    线程是时间片的竞争者,线程一旦激活,就正常运行直到时间片用完,此时操作系统选择另一线程进行运行

    线程的状态分为执行和挂起,改变线程状态的函数有线程挂起SuspendThread()和线程恢复执行ResumeThread()

    由于一个进程中的线程可以并发执行,系统中可独立执行的实体远远多于进程数目,因此,执行效率得以提高

    2.7.3 Windows支持的同步对象

    互锁函数:用来实现线程间一个长整数的读写

    临界段:用来控制对特殊代码段的访问权

    事件:是一个线程将二进制的状态通知给另外的线程的工具

    互斥体:其目的是引导对共享资源的访问

    信号量:用来记录可访问某一资源的最大线程数

    展开全文
  • 进程通信(信号量、匿名管道、命名管道、Socket) 具体的概念就没必要说了,参考以下链接。 信号量 匿名管道 命名管道 Socket Source Code: 1. 信号量(生产者消费者问题) 1 #include <...

    进程通信(信号量、匿名管道、命名管道、Socket)

    具体的概念就没必要说了,参考以下链接。

    Source Code:

    1. 信号量(生产者消费者问题)

     1 #include <iostream>
     2 #include <Windows.h>
     3 #include <process.h>
     4 #include <vector>
     5 using namespace std;
     6 
     7 #define STD _stdcall  //被调用者负责清栈
     8 int BufferSize;       //缓冲区大小
     9 
    10 CRITICAL_SECTION CR;  //临界区
    11 HANDLE Empty = NULL;  //信号量:空闲缓冲区
    12 HANDLE Full = NULL;   //信号量:满缓冲区
    13 vector<int>Buffer;    //缓冲区
    14 
    15 /*生产者线程*/
    16 DWORD STD Producer(void *lp)
    17 {
    18     while(true)//自旋测试
    19     {
    20         //等待空缓冲区:P(empty)
    21         WaitForSingleObject(Full, INFINITE);//一直等待
    22         //进入缓冲区P(mutex)
    23         EnterCriticalSection(&CR);
    24         //生产数据
    25         int s = rand()%10;
    26         Buffer.push_back(s);
    27         cout << "Producer produces an element : " << s <<endl;
    28         //退出缓冲区V(mutex)
    29         LeaveCriticalSection(&CR);
    30         //增加满缓冲区V(full)
    31         ReleaseSemaphore(Empty, 1, NULL);
    32         //睡一会儿
    33         Sleep(2000);
    34     }
    35 }
    36 
    37 /*消费者线程*/
    38 DWORD STD Consumer(void *lp)
    39 {
    40     while(true)//自旋测试
    41     {
    42         //等待满缓冲区:P(empty)
    43         WaitForSingleObject(Empty, INFINITE);//一直等待
    44         //进入缓冲区P(mutex)
    45         EnterCriticalSection(&CR);
    46         //取出数据
    47         int r = Buffer[Buffer.size()-1];
    48         Buffer.pop_back();
    49         cout << "   Consumer consumes an element : " << r <<endl;
    50         //退出缓冲区V(mutex)
    51         LeaveCriticalSection(&CR);
    52         //增加空缓冲区V(full)
    53         ReleaseSemaphore(Full, 1, NULL);
    54         //睡一会儿
    55         Sleep(2000);
    56     }
    57 }
    58 
    59 int main()
    60 {
    61     cout << "Input the number of BufferSize : "; cin >> BufferSize;
    62     //创建信号量
    63     Empty = CreateSemaphore(NULL, 0, BufferSize, NULL);
    64     Full = CreateSemaphore(NULL, BufferSize, BufferSize,NULL);
    65 
    66     //初始化临界区
    67     InitializeCriticalSection(&CR);
    68 
    69     int pNum, cNum;
    70     cout << "Input the number of Producer(Max:10) : "; cin >> pNum;
    71     cout << "Input the number of Consumer(Max:10) : "; cin >> cNum;
    72 
    73     //创建线程
    74     int i;
    75     HANDLE handle[20];
    76     for(i=0; i<pNum; i++)
    77     {
    78         handle[i] = CreateThread(0, 0, &Producer, 0, 0, 0);
    79     }
    80     for(i=pNum; i<pNum+cNum; i++)
    81     {
    82         handle[i] = CreateThread(0, 0, &Consumer, 0, 0, 0);
    83     }
    84 
    85     //回收线程
    86     WaitForMultipleObjects(pNum+cNum, handle, true, INFINITE);
    87 
    88     //释放线程
    89     for(i=0; i<pNum+cNum; i++)
    90     {
    91         CloseHandle(handle[0]);
    92     }
    93 
    94     //释放缓冲区
    95     DeleteCriticalSection(&CR);
    96     return 0;
    97 }
    View Code

    结果:

    2. 匿名管道(本地父进程与子进程通信)

    原理:

    源码:

     1 /*
     2 *匿名管道:父子进程通信
     3 *date : 2018/12/3
     4 *author : yocichen
     5 *status : Done
     6 */
     7 #include <unistd.h>
     8 #include <stdio.h>
     9 #include <stdlib.h>
    10 #include <string.h>
    11 
    12 int main()
    13 {
    14     //pipe1 p to s, pipe2 s to p
    15     int fd_1[2], fd_2[2];
    16 
    17     if(pipe(fd_1)<0 || pipe(fd_2)<0)//fail to create pipe
    18     {
    19         printf("Fail to create the pipe.\n");
    20         return -1;
    21     }
    22 
    23     char buf[256];//
    24     const char *temp;
    25 
    26     //child
    27     int fork_result = fork();
    28     if(fork_result == 0)
    29     {
    30         close(fd_1[1]);//close read port
    31         close(fd_2[0]);//close write port
    32 
    33         //read message
    34         read(fd_1[0], buf, sizeof(buf));//read message from father port
    35         printf("\nChild  : receive a message from pipe1: %s\n", buf);
    36 
    37         //write message
    38         temp = "Hi, my parent, I love you too.";
    39         write(fd_2[1], temp, strlen(temp));//child write message to pipe2
    40     }
    41 
    42     else
    43     {
    44         close(fd_2[1]);
    45         close(fd_1[0]);
    46 
    47         //write message
    48         temp = "My child, I love you.";
    49         write(fd_1[1], temp, strlen(temp));//parent write message to pipe1
    50 
    51         //read message
    52         read(fd_2[0], buf, sizeof(buf));//read message from pipe2
    53         printf("\nParent : receive a message from pipe2: %s\n", buf);
    54     }
    55     return 0;
    56 }
    View Code

    (注意:该匿名管道程序为Linux系统开发,注意运行环境,windows下使用CodeBlocks可以运行)

    3.命名管道

    原理:

    源码:

     1 #include <windows.h>
     2 #include <stdio.h>
     3 #include <stdlib.h>
     4 
     5 int main()
     6 {
     7     //创建命名管道
     8     HANDLE namedPipe = CreateNamedPipeA("\\\\.\\pipe\\testName", PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, PIPE_TYPE_BYTE, 1, 1024, 1024, 0, NULL);
     9 
    10     //校验状态
    11     if(namedPipe == INVALID_HANDLE_VALUE)
    12     {
    13         printf("Server: Fail to create named pipe.\n");
    14     }
    15     else
    16     {
    17         printf("Server: Succeed to create pipe.\n");
    18     }
    19 
    20     OVERLAPPED op;
    21     ZeroMemory(&op, sizeof(OVERLAPPED));
    22 
    23     //创建事件对象
    24     op.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    25 
    26     //等待连接
    27     bool b = ConnectNamedPipe(namedPipe, &op);
    28     printf("Server: Listen...\n");
    29 
    30     int status = WaitForSingleObject(op.hEvent, INFINITE);
    31     //连接成功
    32     if(status == 0)
    33     {
    34         printf("Server: Succeed to connect.\n");
    35     }
    36     else
    37     {
    38         printf("Server: Fail to connect.\n");
    39     }
    40 
    41     //通信
    42     char buf[100] = "来玩个猜数游戏吧!\n";
    43     DWORD wp;
    44     WriteFile(namedPipe, buf, strlen(buf), &wp, NULL);
    45 
    46     int ans = rand()%9+1;
    47     while(status == 0)
    48     {
    49         ZeroMemory(buf, 100);
    50         ReadFile(namedPipe, buf, 100, &wp, NULL);
    51         printf("收到:%s\n", buf);
    52 
    53         if(int(buf[0] - '0') < ans)
    54         {
    55             WriteFile(namedPipe, "小了,再猜一次!\n", strlen("小了,再猜一次!\n"), &wp, NULL);
    56         }
    57         else if((buf[0]-'0') > ans)
    58         {
    59             WriteFile(namedPipe, "大了,再猜一次!\n", strlen("大了,再猜一次!\n"), &wp, NULL);
    60         }
    61         else
    62         {
    63             WriteFile(namedPipe, "猜对了!\n", strlen("小了,再猜一次!\n"), &wp, NULL);
    64             break;
    65         }
    66 
    67         if(buf[0] == '0')
    68         {
    69             printf("客户已退出!\n");
    70             break;
    71         }
    72     }
    73 
    74     //通信结束
    75     DisconnectNamedPipe(namedPipe);
    76     return 0;
    77 }
    Server
     1 #include <windows.h>
     2 #include <stdio.h>
     3 #include <stdlib.h>
     4 
     5 int main()
     6 {
     7     //检查管道是否存在
     8     bool b = WaitNamedPipeA("\\\\.\\pipe\\testName", 0);
     9 
    10     //打开管道
    11     HANDLE hFile = CreateFileA("\\\\.\\pipe\\testName", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    12 
    13     //是否连接成功
    14     if(b ==0 || hFile == INVALID_HANDLE_VALUE)
    15     {
    16         printf("Client: fail to connect.\n");
    17         return 0;
    18     }
    19     else
    20     {
    21         printf("Client: Succeed to connect.\n");
    22     }
    23 
    24     //通信
    25     char buf[100];
    26     ZeroMemory(buf, 100);
    27     DWORD rp;
    28     ReadFile(hFile, buf, 100, &rp, NULL);//读取
    29     printf(buf);
    30 
    31     while(true)
    32     {
    33         printf("输入数字:");
    34         scanf("%s", buf);
    35         WriteFile(hFile, buf, strlen(buf), &rp, NULL);
    36 
    37         while(ReadFile(hFile, buf, 100, &rp, NULL) == true)
    38         {
    39             printf("Server: ");
    40             printf(buf);
    41             break;
    42         }
    43     }
    44 
    45     CloseHandle(hFile);
    46     return 0;
    47 }
    Client

    4.Socket网络进程通信

    原理:

    源码:

     1 /*注意头文件顺序*/
     2 #include <winsock2.h>
     3 #include <stdio.h>
     4 #include <stdlib.h>
     5 #pragma comment(lib, "ws2_32.lib")      //引入动态链接库
     6 
     7 int main()
     8 {
     9     WORD ws_version = MAKEWORD(2, 2);   //指定Winsock version
    10     WSADATA wsaData;                    //WSA 函数的参数
    11 
    12     /*初始化winsock*/
    13     WSAStartup(ws_version, &wsaData);
    14 
    15     /*socket*/
    16     SOCKET s_server = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    17 
    18     SOCKADDR_IN addr_server;
    19     addr_server.sin_family = AF_INET;   //协议
    20     addr_server.sin_port = htons(5050); //端口
    21     addr_server.sin_addr.s_addr = htonl(INADDR_ANY);    //IP:任意IP
    22 
    23     /*bind*/
    24     int bind_status;
    25     bind_status = bind(s_server, (SOCKADDR*)&addr_server, sizeof(SOCKADDR));
    26     if(bind_status == SOCKET_ERROR)
    27     {
    28         printf("bind error : fail to bind! \n"); 
    29     }
    30     else
    31     {
    32         printf("bind successfully!\n");
    33     }
    34 
    35     /*listen*/
    36     listen(s_server, 5);//max=5
    37     printf("listening ... \n");
    38 
    39     SOCKADDR_IN addr_client;            //存储client地址信息
    40     int len = sizeof(SOCKADDR);
    41     int count = 0;                        //统计客户数目
    42     SOCKET s_client;                    //连接的socket
    43 
    44     char buf[1000];
    45     while(true)
    46     {
    47         printf("等待客户端连接...\n");
    48         /*accept*/
    49         s_client = accept(s_server, (SOCKADDR*)&addr_client, &len);
    50         if(s_client == INVALID_SOCKET)
    51         {
    52             printf("Accept error : fail to accept client! ");
    53         }
    54         else//连接成功
    55         {
    56             count++;
    57             printf("\nAccept successfully!\n");
    58             
    59             printf("---------------------------------------------\n");
    60             printf(" 编号:%d \n", count);
    61             printf(" Port:%d\n", ntohs(addr_client.sin_port));
    62             printf(" IP:%s\n", inet_ntoa(addr_client.sin_addr));//inet_ntoa(SOCKADDR.in_addr)网络地址转换为IP
    63             
    64             int recv_status = recv(s_client, buf, 100, 0);
    65             if(recv_status > 0)
    66             {
    67                 printf("收到:");
    68                 buf[recv_status] = 0x00;//截断
    69                 printf(buf);
    70                 printf("\n---------------------------------------------\n");
    71             }
    72             const char *sendData = "你好!客户端!我是服务器";
    73             send(s_client, sendData, strlen(sendData), 0);
    74             closesocket(s_client);
    75         }
    76     }
    77 
    78     closesocket(s_server);      //关闭socket
    79     WSACleanup();
    80 
    81     return 0;
    82 }
    Server
     1 #include <winsock2.h>
     2 #include <stdio.h>
     3 #include <stdlib.h>
     4 #include <windows.h>
     5 #include <iostream>
     6 #pragma comment(lib, "ws2_32.lib")      //引入动态链接库
     7 #define SERVER_IP "192.168.31.102"        //客户端IP
     8 using namespace std;
     9 
    10 int main()
    11 {
    12     WORD ws_version = MAKEWORD(2, 2);   //指定Winsock version
    13     WSADATA wsaData;                    //WSA 函数的参数
    14 
    15     /*初始化winsock*/
    16     WSAStartup(ws_version, &wsaData);
    17 
    18     while(true)
    19     {
    20         /*socket*/
    21         SOCKET s_client = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    22 
    23         SOCKADDR_IN addr_server;
    24         addr_server.sin_family = AF_INET;   //协议
    25         addr_server.sin_port = htons(5050); //端口
    26         addr_server.sin_addr.s_addr = inet_addr(SERVER_IP);
    27 
    28         char buf[100];
    29         int send_status, recv_status;
    30 
    31         /*Connect*/
    32         int cnct_status = connect(s_client, (SOCKADDR*)&addr_server, sizeof(SOCKADDR));
    33         if(cnct_status == 0)//连接成功
    34         {
    35                 printf("\nConnecting... done\n");
    36 
    37                 //向服务端发送消息
    38                 printf("输入发送信息:");
    39                 scanf("%s", buf);
    40                 send_status = send(s_client, buf, 10, 0);
    41                 if(send_status == SOCKET_ERROR)//发送失败
    42                 {
    43                     printf("send error!\n");
    44                 }
    45                 else
    46                 {
    47                     printf("发送:%s\n", buf);
    48                     //接受服务端消息
    49                     recv_status = recv(s_client, buf, 100, 0);
    50                     buf[recv_status] = 0x00;//截断
    51                     printf("收到:%s\n", buf);
    52                 }
    53         }
    54         else
    55         {
    56             printf("Test:fail to connect server! \n");
    57         }
    58         closesocket(s_client);
    59     }
    60 
    61     WSACleanup();
    62 
    63     return 0;
    64 }
    Client

     

    展开全文
  • 作业:用户在一次解决或是一个事务处理过程中要求计算机系统所做的工作的集合,它包括用户程序、所需要的数据集... 线程:是进程中的一个实体,是被操作系统独立调度和执行的基本单位。一个进程包含一个或多个线程。

           作业用户在一次解决或是一个事务处理过程中要求计算机系统所做的工作的集合,它包括用户程序、所需要的数据集控制命令等。作业是由一系列有序的步骤组成的。在执行一个作业可能会运行多个不同的进程。

              进程:程序在一个数据集上的一次运行过程。是操作系统资源分配的基本单位。

              线程:是进程中的一个实体,是被操作系统独立调度和执行的基本单位。一个进程包含一个或多个线程。

               线程特征

    1、线程的执行状态包括运行、就绪和等待。

    2、进程中的所有线程共享所属进程内的主存和其他资源。

    3、拥有自己的线程控制块和执行栈,寄存器。

               进程和线程的区别

    1、进程间是独立的,在内存空间、上下文环境上。而线程是运行在进程空间内的,同一进程所产生的线程共享同一内存空间。

    2、进程间是可以并发执行的,线程之间也可以并发执行。但同一进程中的两端代码只有在引入线程的情况下才能并发执行。

    3、线程是属于进程的,当进程退出时,该进程所产生的线程都会被强制退出并清除。

    4、线程占用的资源要少于进程占用的资源,线程间的切换速度比进程间的切换快的多。

            管程:是定义了一个数据结构和在该数据结构上能为并发进程所执行的一组操作。这些操作能同步进程和改变管程中的数据。它是一种进程同步机制。在结构上类似于面向对象中的类。在功能上和信号量和p,v操作类似。可以更方便的管理系统的临界资源。

             管程特征:

    1、模块化:一个管程就是一个可单独编译的实体,结构上和类相仿。

    2、抽象数据类型。

    3、信息隐蔽。

            管程要素:

    1、安全性:管程中的数据变量在管程之外是不可见的,只能有该管程的操作过程存取。

    2、互斥性:任一时刻只能有一个调用者进入管程。

    3、等待机制:设置等待队列及相应的操作,对资源进行管理。

           管程和上面三个名词,区别大,但是容易混淆。

            管道:是一种进程通信机制。是共享文件模式,它基于文件系统,在两个进程之间,以先进先出的方式实现消息的单向传送。管道是一种特殊文件。


    展开全文
  • 操作系统概念学习笔记 9 线程

    千次阅读 2015-05-12 08:14:14
    操作系统概念学习笔记 9线程概述单个进程可以包括多个控制线程。线程 ——一种CPU利用的基本单元,它是形成多线程计算机的基础。线程是CPU使用的基本单元,它由线程ID、程序计数器、寄存器集合和栈组成。它与属于...

    操作系统概念学习笔记 9

    线程


    概述

    单个进程可以包括多个控制线程。

    线程 ——一种CPU利用的基本单元,它是形成多线程计算机的基础。

    线程是CPU使用的基本单元,它由线程ID、程序计数器、寄存器集合和栈组成。它与属于统一进程的其他线程共享代码段、数据段和其他操作系统资源。

    一个传统重量级的进程只有单个控制线程,如果进程有多个控制线程,那么能同时做多个任务。

    这里写图片描述

    单线程与多线程

    动机

    一个应用程序通常是作为一个具有多个控制线程的独立进程实现的。如一个忙碌的网页服务器如果有多个(或数千个)客户并发访问它,可以建立多个线程而非进程,因为创建进程很耗时间,而单个线程的进程一次只能处理一个请求。

    线程在远程过程调用(RPC)系统中个也有很重要的作用,通常,PRC服务器是多线程的。当一个服务器接收到消息,它使用独立线程处理消息,这允许服务器能处理多个并发请求。

    现代许多操作系统都是多线程的,少数线程在内核中运行,每个线程完成一个指定的任务。

    多线程的优点

    1. 响应度高:即使其部分阻塞或执行较冗长操作,该程序仍能继续执行,从而增加了对用户的相应程度。

    2. 资源共享:线程默认共享它们所属进程的内存和资源。代码和数据共享的优点是它允许一个应用程序在同一地址空间有多个不同的活动线程。

    3. 经济:进程创建所需要的内存和资源的分配比较昂贵。由于线程能共享它们所属进程的资源,所以创建和切换线程会更为经济。

    4. 多处理器体系结构的利用:多线程的优点之一是能充分使用多处理器体系结构。以便每个进程能并行运行在不同的处理器上。不管有多少CPU,单线程进程只能运行在一个CPU上,在多CPU上使用多线程加强了并发功能。

    多线程模型:

    有两种不同的方法来提供线程支持:用户层的用户线程和内核层的内核线程。用户线程受内核支持,而无需内核管理;而内核线程由操作系统支持和管理。事实上所有当代操作系统都支持内核线程。在用户线程和内核线程之间必然存在一种关系。

    多对一模型:

    多对一模型将许多用户级线程映射到一个内核线程。线程管理由线程库在用户空间进行的,因而效率比较高。但是如果一个线程执行了阻塞系统调用,那么整个线程会阻塞。因为任意时刻只能有一个线程能够访问内核,多个线程不能并行运行在多处理器上。

    这里写图片描述

    一对一模型:

    一对一模型每个用户线程映射到一个内核线程。该模型在一个线程执行阻塞系统调用时,能允许另一个线程继续执行。它也允许多个线程能并行运行在多处理器系统上,这种模型的唯一缺点是每创建一个用户线程就会创建一个相应的内核线程。由于创建内核线程的开销会影响应用程序的性能,所以这种模型的绝大多数实现限制了操作系统所支持的线程数量。

    这里写图片描述

    多对多模型:

    多对多模型多路复用了许多用户线程到同样数量或更小数量的内核线程上。多对多模型没有这两者的缺点:开发人员可创建任意多的用户线程,并且相应内核线程能在多处理器系统上并发执行。而且当一个线程执行阻塞系统调用时,内核能调度另一个线程来执行。

    一个流行多对多模型的变种仍然多路服用了许多用户线程到同样数量或更小数量的内核线程上,但也允许将一个用户线程绑定到某个内核线程上。这个变种有是被称为二级模型。

    这里写图片描述

    这里写图片描述

    thread library):为程序员提供创建和管理线程的API。主要有两种方法来实现线程库。

    (1)在用户空间中提供一个没有内核支持的库,此库的所有代码和数据结构都存在于用户空间中。调用库中的一个函数知识导致用户空间中一个本地函数调用,而不是系统调用。

    (2)执行一个有操作系统直接支持的内核级的库。此时,库的代码和数据结构存在于内核空间中。调用库中的一个API函数通常会导致对内核的系统调用。

    目前使用的三种主要的线程库是:

    (1)POSIX Pthread
    (2)Win32
    (3)Java

    Pthread作为POSIX标准扩展,可以提供用户级或内核级的库。Win32线程库是适用于Windows操作系统的内核级线程库。Java线程API允许线程在java程序中直接创建和管理。然而,由于大多数JVM实例运行在宿主操作系统之上,Java线程API通常采用宿主系统上的线程库来实现。

    Pthread

    Pthread是由POSIX标准为线程创建和同步定义的API。这是线程行为的规范,而不是实现。操作系统设计者可以根据意愿采取任何形式来实现。

    所有Pthread程序都需要包括pthread.h头文件。

    Pthread_t tid声明了所常见线程的标识符。每个线程都有一组属性,包括栈大小和调度信息。

    Pthread_attr_t attr表示线程的属性,通过函数调用pthread_attr_init(&attr)来设置这些属性。

    Pthread_create()创建一个独立线程。除了传递线程标识符和线程属性外,还要传递函数名称。

    pthread_join()函数等待

    pthread_exit()完成

    Pthread实例程序:

    以下示例实验程序实现并发的两个线程合作将整数 X 的值从 1 加到 10 的功
    能。它们通过管道相互将计算结果发给对方。

    
    /*
    * description
    :  tpipe.c
    * copyright
    : (C)2015  by 孙铭超
    * Function
    : 利用管道实现在在线程间传递整数
    */
    #include <stdio.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <pthread.h>
    void task1(int *); //线程 1 执行函数原型
    void task2(int *); //线程 2 执行函数原型
    int pipe1[2],pipe2[2];//存放两个无名管道标号
    pthread_t thrd1,thrd2;//存放两个线程标识
    int main(int argc,char *arg[])
    {
    int ret;
    int num1,num2;
    //使用pipe()系统调用建立两个无名管道。建立不成功程序退出,执行终止
    if(pipe(pipe1) < 0){
    perror("pipe not create");
    exit(EXIT_FAILURE);
    }
    if(pipe(pipe2) < 0){
    perror("pipe not create");
    exit(EXIT_FAILURE);
    }
    //使用pthread_create系统调用建立两个线程。建立不成功程序退出,执行终止
    num1 = 1 ;
    ret = pthread_create(&thrd1,NULL,(void *) task1,(void *) &num1);
    if(ret){
    perror("pthread_create: task1");
    exit(EXIT_FAILURE);
    }
    num2 = 2 ;
    ret = pthread_create(&thrd2,NULL,(void *) task2,(void *) &num2);
    if(ret){
    perror("pthread_create: task2");
    exit(EXIT_FAILURE);
    }
    //挂起当前线程切换到thrd2 线程
    pthread_join(thrd2,NULL);
    //挂起当前线程切换到thrd1 线程
    pthread_join(thrd1,NULL);
    exit(EXIT_SUCCESS);
    }
    //线程 1 执行函数,它首先向管道写,然后从管道读
    void task1(int *num)
    {
    int x=1;
    //每次循环向管道 1 的 1 端写入变量X的值,并从
    //管道 2 的 0 端读一整数写入X再对X加 1,直到X大于 10
    do{
    printf("thread%d read: %d\n",*num,x++);
    write(pipe1[1],&x,sizeof(int));
    read(pipe2[0],&x,sizeof(int));
    }while(x<=9);
    //读写完成后,关闭管道
    close(pipe1[1]);
    close(pipe2[0]);
    }
    //线程 1 执行函数,它首先从管道读,然后向管道写
    void task2(int * num)
    {
    int x;
    //每次循环从管道 1 的 0 端读一个整数放入变量X中,
    //并对X加 1 后写入管道 2 的 1 端,直到X大于 10
    do{
    read(pipe1[0],&x,sizeof(int));
    printf("thread2 read: %d\n",x++);
    write(pipe2[1],&x,sizeof(int));
    }while( x<=9 );
    //读写完成后,关闭管道
    close(pipe1[0]);
    close(pipe2[1]);
    }

    运行结果:

    sora@sora:~/SORA/program/Pthread$ gcc -g -c demo.c
    sora@sora:~/SORA/program/Pthread$ gcc demo.o -l pthread -o demo
    sora@sora:~/SORA/program/Pthread$ ./demo 
    thread1 read: 1
    thread2 read: 2
    thread1 read: 3
    thread2 read: 4
    thread1 read: 5
    thread2 read: 6
    thread1 read: 7
    thread2 read: 8
    thread1 read: 9
    thread2 read: 10
    

    Win32线程

    Win32线程库创建线程的技术在某些方面类似与Pthread技术。

    Win32线程:Win32 API必须包括windows.h头文件

    线程的创建使用了CreateThread() 将一组线程的属性传递给此函数。

    在Pthread程序中采用的pthread_join()语句实现线程等待,在Win32中采用同等功能的函数WaitForSingleObject(),从而使创建者线程阻塞。

    Java线程

    Java线程:线程是Java程序中程序执行的基本模型。

    所有Java程序至少有一个控制线程组成,即使是一个只有main()函数的简单Java程序也是在JVM中作为一个线程运行的。

    Java程序中有两种创建线程的技术。

    (1)创建一个新的类,它从Thread类派生,并重载它的run()函数。

    (2)定义一个实现Runnable()接口的类。当一个类执行runnable()时,他必须定义run()函数,而实现run()函数的代码被作为一个独立的线程执行。

    创建Thread对性并不会创建一个新的线程,实际上是用start()函数来创建新线程。为新的对象调用start()函数需要做两件事:

    一是在JVM分配内存并初始化新的线程;

    二是调用run()函数,实现线程适合在JVM中运行
    (注意,从不直接调run()函数,而是调用start()函数,然后它再调用run()函数)。

    与pthread_join()相对应的java中有join()函数。

    三者比较:

    在Win32和Pthread共享数据很方便,可以将共享数据简单的声明为全局数据。而对于Java没有全局数据的概念,在Java程序中如果两个或更多的线程需要共享数据,通过向相应的线程传递对共享对象的引用来实现。

    多线程问题

    系统调用fork()和exec()

    在多线程程序中,系统调用fork()和exec()的语义有所改变。

    如果程序中一个进程调用fork(),那么新进程会复制所有线程,还是新进程只有单个线程?有的UNIX系统有两种形式的fork(),一种复制所有线程,另一种只复制调用了系统调用fork()的线程。

    Exec()工作方式:如果一个线程调用系统调用exec(),那么exec()参数所指定的程序会替换整个进程,包括所有线程。

    如果调用fork()之后立即调用exec(),那么没有必要复制所有线程,因为exec()参数所指定的程序会替换整个进程。在这种情况下,只复制调用线程比较适当。不过,如果在fork()之后另一进程并不调用exec(),那么另一进程就应复制所有进程。

    取消

    线程取消(thread cancellation)是在线程完成之前来终止线程的任务。

    要取消的线程通常称为目标线程。目标线程的取消可在如下两种情况下发生:

    一是异步取消(asynchronous cancellation):一个线程立即终止目标线程。

    二是延迟取消(deferred cancellation):目标线程不断地检查它是否应终止,这允许目标线程有机会以有序方式来终止自己。

    如果资源已经分配給要取消的线程,或者要取消的线程正在更新与其他线程所共享的数据,那么取消会有困难,对于异步取消尤为麻烦。操作系统回收取消线程的系统资源,但是通常不回收所有资源。因此,异步取消线程并不会使所需的系统资源空闲。相反采用延迟取消时,允许一个线程检查它是否是在安全的点被取消,pthread称这些点为取消点(cancellation point)

    信号处理

    信号处理:信号在Unix中用来通知进程某个特定时间已发生了,信号可以同步或异步接收。所有有信号具有同样的模式:

    (1)信号有特定事件的发生所产生

    (2)产生的信号要发送到进程

    (3)一旦发送,信号必须交易处理。

    同步信号的例子包括访问非法内存或被0除。在这种情况下,如果运行程序执行这些动作,那么就产生信号,同步信号发送到执行操作而产生信号的同一进程(同步的原因)。

    当一个信号由运行进程之外的事件产生,那么进程就异步接收这一信号。这种信号的例子包括使用特殊键(Ctrl + C)或者定时器到期。通常,异步信号被发送到另一个进程。

    每个信号可能由两种可能的处理程序中的一种来处理:

    (1)默认信号处理程序

    (2)用户定义的信号处理程序

    每个信号都有一个默认信号处理程序,当处理信号是在内核中运行的,这种默认动作可以用用户定义的信号处理程序来改写。信号可以按照不同的方式处理。有的信号可以简单的忽略(如改变窗口大小),有的需要终止程序来处理(非法内存访问)

    单线程程序的信号处理比较直接,信号总是发送给进程。

    当多线程时,信号会

    (1)发送信号到信号所应用的线程

    (2)发送信号到进程内的每个线程

    (3)发送信号到进程内的某些固定线程

    (4)规定一个特定线程以接收进程的所有信号。

    发送信号的方法依赖于信号的类型。

    大多数线程版Unix允许线程描述它会接收什么信号和拒绝什么信号。,因为信号只能处理一次,所以信号通常发送到不拒绝它的第一个线程。

    pthread还提供了pthread_kill函数,此函数允许限号被传送到一个指定线程。

    Windows,通过异步过程调用(asynchronous procedure call,APC)来模拟信号。

    线程池

    多线程服务器有一些潜在问题:第一个是关于处理请求之前用以创建线程的时间,以及线程在完成工作之后就要被丢弃这一事实。第二个,如果允许所有并发请求都通过新线程来处理,那么将没法限制在系统中并发执行的线程的数量。无限制的线程会耗尽系统资源。解决这一问题是使用线程池。

    线程池的思想是在进程开始时创建一定数量的线程,并放入到池中以等待工作。当服务器收到请求时,他会唤醒池中的一个线程,并将要处理的请求传递给他,一旦线程完成了服务,它会返回到池中在等待工作。如果池中没有可用的线程,那么服务器会一直等待直到有空线程为止。

    线程池的优点:

    (1)通常用现有线程处理请求要比等待创建新的线程要快

    (2)线程池限制了在任何时候可用线程的数量。

    线程池中的线程数量由系统CPU的数量、物理内存的大小和并发客户请求的期望值等因素决定。比较高级的线程池能动态的调整线程的数量,以适应具体情况。

    线程特定数据

    同属一个进程的线程共享进程数据。

    在某些情况下每个线程可能需要一定数据的自己的副本,这种数据称为线程特定数据。可以让每个线程与其唯一的标识符相关联。

    调度程序激活

    内核与线程库的通信问题,就是要讨论多对多模型。这种协调允许动态调整内核线程的数量以保证其最好的性能。

    在用户内核线程之间设置一种中间数据结构。轻量级进程(LWP),他表现为一种应用程序可以调度用户线程来运行的虚拟处理器。每个LWP与内核线程相连,该内核线程被操作系统调度到物理处理器上运行,如果内核线程阻塞,LWP阻塞,与LWP相连的用户线程也阻塞。如果在单处理器上,一次只能运行一个线程,则只需要一个LWP就够了,但I/O密集的应用程序可能需要多个LWP来执行。

    一种结局用户线程库与内核间通信的方法为调度器激活(scheduler activation),内核提供一组虚拟处理器(LWP)给应用程序,应用程序可调度用户进程到一个可用的虚拟处理器上。

    展开全文
  • 程序员必知的 89 个操作系统核心概念

    万次阅读 多人点赞 2020-03-31 19:13:39
    操作系统(Operating System,OS):是管理计算机硬件与软件资源的系统软件,同时也是计算机系统的内核与基石。操作系统需要处理管理与配置内存、决定系统资源供需的优先次序、控制输入与输出设备、操作网络与管理...
  • 操作系统操作系统纠错本

    千次阅读 2018-11-25 19:02:08
    操作系统操作系统纠错本 标签(空格分隔):【考研纠错本】 考研操作系统纠错本 考研操作系统纠错本 1. 操作系统的基本概念 我的微信公众号 1. 操作系统的基本概念 ...
  • 操作系统考试大纲

    千次阅读 2017-05-28 21:12:13
     1、掌握操作系统的基本概念、基本原理和基本功能,理解操作系统的整体运行过程。  2、掌握操作系统进程、内存、文件和I/O管理的策略、算法、机制以及相互关系。 3、能够运用所学的操作系统原理、方法与技术...
  • 这些操作系统概念,保你没听过!

    千次阅读 多人点赞 2020-02-10 12:40:45
    操作系统概念 大部分操作系统提供了特定的基础概念和抽象,例如进程、地址空间、文件等,它们是需要理解的核心内容。下面我们会简要介绍一些基本概念,为了说明这些概念,我们会不时的从 UNIX 中提出示例,相同的...
  • 操作系统笔记(b站王道视频)

    千次阅读 多人点赞 2019-09-19 15:38:20
    操作系统概念和定义1.操作系统的层次结构2.操作系统的功能和目标3.操作系统总结4. 操作系统的四个基本特征5.操作系统四个特征总结6.操作系统的发展和分类7.操作系统的发展和分类的总结8.操作系统的运行机制 体系...
  • 进程通信 进程通信的概念与方法 顾名思义,进程通信就是指进程之间的信息交换。 进程是分配系统资源的单位(包括内存地址空间),因此各进程拥有的内存地址...所以,为了保证进程间的安全通信,操作系统提供了一...
  • 操作系统学习基本概念汇总

    千次阅读 2017-11-11 09:47:22
    操作系统又是一门计算机专业必学的专业课之一,同样对于很多考研统考的同学来说也是必考的课程。本篇帖子是我大学时期整理的操作系统的基本知识点。足以应付大学时代的期末考试。 1. 存储管理研究课题的四个方面:...
  • 面试/笔试第二弹 —— 操作系统面试问题集锦

    万次阅读 多人点赞 2017-11-16 20:10:40
    本文对面试/笔试过程中经常会被问到的一些关于操作系统的问题进行了梳理和总结,一方面方便自己温故知新,另一方面也希望为找工作...关于这块内容的初步了解和整体掌握,建议大家读一读《操作系统概念》(高教版)一书。
  • 操作系统概念辨析

    千次阅读 2011-10-27 10:50:21
    额,简历中有一句“熟悉操作系统原理”,底层公司、设备商公司应该会要问这些东东吧。有几个问题总结一下: 目录: 1.信号量(semaphore),互斥量(mutex),事件,临界区概念解析 2.自旋锁的概念 3.单内核和微内核...
  • Linux管道 - 系统调用pipe()函数实现

    千次阅读 2018-07-18 14:49:12
    管道概念 1、管道是操作提供的一块操作系统内存; 2、管道是Unix中最古老的进程间通信方式; 3、我们把一个进程连接到另一进程的数据流称为管道。 管道通信原理 父进程和子进程之间,或者两个兄弟进程之间,...
  • 管道概念

    千次阅读 2015-03-23 20:26:02
    什么是管道? ... 管道包括无名管道和命名管道两种,前者用于父进程和子进程间的通信,后者可用于运行于同一系统中的任意两个进程间的通信。 无名管道的创建  无名管道由pipe( )函数创建
  • 在类Unix计算机操作系统,一个管道是一个序列过程中通过其链接在一起的标准流,使每一个过程(输出标准输出)直接提供输入(标准输入)到下一个。 管线标准的shell语法是列出多个命令,以竖线(在常见的UNIX空话...
  • 操作系统概念: 1.进程 本质上是正在执行的一个程序,进程在其地址空间内读写,该地址空间存放有可执行程序、程序的数据以及程序的堆栈,进程的资源集包括寄存器、打开文件的清单、突出的报警、有关进程的清单。进程...
  • 什么是管道

    千次阅读 2018-01-28 15:04:28
    而这里要讲的是计算机中的管道,确切的说是linux操作系统中的管道。那么,linux中的管道是什么?有什么作用? 管道 对于从事互联网行业的人来说,管道技术几乎每天都会用到。在计算机世界中,由于进程之间相互独立...
  • 操作系统基础知识复习总结

    万次阅读 多人点赞 2019-08-17 20:59:38
    操作系统 操作系统概述 操作系统作用 存储管理 处理机管理 设备管理 文件管理 用户接口 操作系统的定义 是管理和控制计算机系统中各种硬件和软件资源、合理地组织计算机工作流程的...
  • 5万字、97 张图总结操作系统核心知识点

    千次阅读 多人点赞 2020-07-14 09:19:25
    这不是一篇教你如何创建一个操作系统的文章,相反,这是一篇指导性文章,教你从几个方面来理解操作系统。首先你需要知道你为什么要看这篇文章以及为什么要学习操作系统。 搞清楚几个问题 首先你要搞明白你学习操作...
1 2 3 4 5 ... 20
收藏数 71,224
精华内容 28,489
关键字:

操作系统管道的概念