操作系统——第二章笔记(四)

一.进程通信
进程通信是指进程之间的信息交换。
1.低级通信——进程之间的互斥和同步
信号量机制是有效的同步工具,但作为通信工具缺点如下:
(1)效率低(通信量少)
(2)通信对用户不透明(程序员实现,操作系统只提供共享存储器供代码操作)
2.高级进程通信
用户直接利用操作系统提供的一组通信命令,高效地传送大量数据的通信方式。
操作系统隐藏了进程通信的细节,对用户透明,减少了通信程序编制上的复杂性。
二.进程通信的类型
高级通信机制可归结为四大类
① 共享存储器系统(操作存储区方式)
相互通信的进程共享某些数据结构或共享存储区,进程之间能够通过这些空间进行通信。
a. 基于共享数据结构的通信方式(低级)
b. 基于共享存储区的通信方式(高级)
例:两台主机之间QQ消息的传送
在发送端开辟一个缓存区,存放发送的数据,通过物理媒体的传递通过网卡到接收端的缓冲区。
问题:为什么QQ发送的消息不会出现在浏览器上?
解答:每个应用程序都有自己的端口,通过端口找到再各自电脑上的缓冲区,从中发送或者获取数据
1)基于共享数据结构
诸进程公用某些数据结构,借以实现诸进程间的信息交换。
如生产消费问题,定义共享的数据结构:n个长度的有界缓冲区。
程序员:提供对公用数据结构的设置及对进程间同步的处理。
操作系统:提供共享存储器。
特点:复杂、低效率,还只适合传递相对少量的数据。
2)基于共享存储区(适用于大量信息传递,底层同步处理由操作系统控制)
 在存储器中划出了一块共享存储区,诸进程可通过对共享存储区中数据的读或写来实现通信。
 进程通信前先向系统申请获得共享存储区中的一个分区,并指定该分区的关键字;
 若系统已经分给了其他进程,则将该分区的描述符返回给申请者,申请者把获得的共享存储分区连接到本进程上;此后,便可像读、写普通存储器一样地读、写该公用存储分区。多进程借助该区通信。
②消息传递系统(发–收方式)
 最广泛使用的一种,进程间的数据交换,以格式化的消息为单位。屏蔽底层复杂操作。
 单机:操作系统底层编程中的消息传递系统调用;
 计算机网络:消息称为报文。程序员直接利用系统提供的一组通信命令(原语)进行通信。(④客户机-服务器系统)
③管道通信(中间文件方式)
 所谓“管道”,是指用于连接一读进程和一写进程以实现通信的一个共享文件,又名pipe文件。
 向共享文件输入的写进程以字符流形式将大量的数据送入管道;而接收管道输出的读进程则从管道中接收(读)数据。
 首创于UNIX系统。其管道机制需提供三方面的协调能力:互斥、同步、确定对方是否存在。
例子:
对管程进行删除文件操作:

#include<sys/types.h>
#include<sys/stat.h>
#include<errno.h>
#include<fcntl.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define FIFO “/tmp/myfifo”
main(int argc,char**argv)
{   char buf_r[100];
    int fd;
    int nread;
  /*创建管程*/
   if((mkfifo(FIFO,O_CREAT|O_EXCL)<0)&&(errno!=EEXIST))
  printf(“cannot create fifoserver\n”)
printf(“Preparing for reading bytes…\n”);
memset(buf_r,0,sizeof(buf_r));
/*打开通道*/
fd=open(FIFO,O_RDONLY|O_NONBLOCK,0);
if(fd==-1)
{   perror(“open”);
    exit(1);
}
while(1)
{   memset(buf_r,0,sizeof(buf_r));
    if((nread=read(fd,buf_r,100))==-1)
    {
      if(errno==EAGAIN)
         printf(“no data yet\n”);
    }else
  printf(“read %s from FIFO\n”,buf_r);
  sleep(1);
}
pause();/*暂停,等待信号*/
unlink(FIFO);//删除文件
}

对管程进行写入数据操作:

#include<sys/types.h>
#include<sys/stat.h>
#include<errno.h>
#include<fcntl.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define FIFO_SERVER “/tmp/myfifo”
main(int argc,char**argv)
{   int fd;
    char w_buf[100];
    int nwrite;
/*打开管道*/
fd=open(FIFO_SERVER,O_WRONLY|O_NONBLOCK,0);
if(argc==1)
{     printf(“Please send something\n”)
      exit(-1);
}
strcpy(w_buf,argv[1]);
/*向管道写入数据*/
if((nwrite=write(td,w_buf,100))==-1)
{   if(errno==EAGAIN)
      printf(“The FIFO has not been read yet.Please try later\n”);
}
else
       printf(“write %s to the FIFO\n”,w_buf);
}

编程操作命名管道的例子:

#include<stdio.h>
 #include<string.h>
 #include<unistd.h>
 #include<sys/types.h>
 #include<errno.h>
 #include<fcntl.h>
//建立管道
 int main(int argc,char *argv[])
{
   //pipefile=”/tmp/mypipefile”
   //使用mkfifo函数创建一个FIFO管道
  if((mkfifo(pipefile,0666))<0){
perror(“failed to mkfifo”);
exit(1);}
 printf(“mkfifo successes.name is %s\n”,pipefile);
 return 0;
}
    //读管道
 int main(int argc,char *argv[]) {
  if(argc!=2) {
   printf("not enough params,give pipefile name\n");
   exit(1);
  }
  char *pipefile;
  pipefile=argv[1];
  char buf[100];
  int fd,i;
  printf("read open namedpipe!\n");
  fd=open(pipefile,O_RDONLY,0);    //只有当读和写的两个open都是执行的时候才可以继续执行,否则将等待
  printf("ok! namedpipe opened for read!\n");
  i=read(fd,buf,100);       //当没有写操作执行时才可读,否则等待
  buf[i]=0;
  printf("ok! reaed from namedpipe: %s!\n",buf);  
  return 0;
 }
 //写管道
 int main(int argc,char *argv[]) {
  if(argc!=2) {
   printf("not enough params,give pipefile name\n");
   exit(1);
  }
  char *pipefile;
  pipefile=argv[1];
  int fd;
  char *test="test strings!";
  printf("open namedpipe --%s for write!\n",pipefile);
  fd=open(pipefile,O_WRONLY,0);        //只有当读和写的两个open都是执行的时候才可以继续执行,否则将等待
  printf("ok! namedpipe --%s opened for write!\n",pipefile);
  sleep(10);
  printf("write wake up! hahaha!\n");
  write(fd,test,strlen(test));
  printf("OK! namedpipe write successfully!\n");
  exit(0);
 }

④Client-Server system
3.消息传递通信的实现方法
1)直接通信方式
2)间接通信方式
三.消息传递系统的实现
单机和网络环境下的高级进程通信广泛采用“消息传递”方式,需要考虑的问题:
① 通信链路的建立
② 消息格式
③ 同步方式
四.认识线程
进程的产生是为了提高效率,进程效率低线程效率高。
1.线程的引入
多道程序管理:追求效率的目的下实现“并发”
利用进程实现的多道程序系统中
进程是一个可拥有资源的独立单位;
是一个可独立调度和分派资源的基本单位
并发程度不是随意设定的:
 并发进程数量不宜过多,切换频率不宜过高。
 限制并发程度问题所在:进程实体信息量大,对进程的管理操作越多,与运行时间的比值就越大,运行效率就低。
2.线程的属性
多线程OS中,一个进程包括多个线程,每个线程都是利用CPU的基本单位。
1)轻型实体:只需一点必不可少的、能保证独立运行的资源。(TCB)
2)独立调度和分派的基本单位:调度切换迅速且开销小。
3)可并发执行。
4)共享进程资源:同进程中的线程可共享相同的进程地址空间、已打开文件、信号量机构等。
3.线程的信息
TCB管理什么信息?
状态参数
 标识符、运行状态、优先级、寄存器状态、堆栈、专有存储器、信号屏蔽等。
 运行状态:执行、就绪、阻塞
4. 线程的创建和终止
在多线程OS中,应用程序启动时,通常只有一个线程(初始化线程)在执行,它根据需要再创建若干线程。
5.多线程系统中的进程
 进程只是用于分配系统资源
 包括多个线程
 不是执行实体,线程在进程范围内作为执行实体。
6.线程的管理
 同步和通信机制
1)互斥锁
 比较简单的,控制线程互斥访问资源;
 适用于高频度使用的关键共享数据和程序段;
 unlock和lock两个锁操作原语;
2)条件变量
 与互斥锁一起使用
 锁保证互斥进入临界区,但利用条件变量使线程阻塞
 注意不满足条件时,wait条件变量:
○1释放互斥锁
○2进程阻塞在条件变量指向队列中
○3被唤醒后要重新再设互斥锁
3)信号量
 私用信号量(private samephore)
 用于同进程的线程间同步,数据结构存放在应用程序的地址空间。属于特定进程,OS感知不到其存在。
 公用信号量(public samephore)
 用于不同进程间或不同进程中线程的同步,数据结构由OS管理,存放在受保护的系统存储区。
五.线程的实现方式
1.内核线程KST(kernel-level thread)
 依赖于内核,利用系统调用由OS内核在内核空间完成创建、撤消、切换等线程工作。
 时间片分配给线程,所以多线程的进程获得更多CPU时间。
2.用户线程ULT(user-level thread)
无须利用系统调用,不依赖于OS核心。进程利用线程库函数创建、同步、调度和管理控制用户线程。
 调度由应用软件内部进行,通常采用非抢先式和更简单的规则,也无需用户态/核心态切换,速度比kst快。
3.组合方式
内核支持多KST线程的管理,同时也允许用户应用程序级的线程管理。

PS:
1.KST优缺点:
 多处理器系统下可实现多线程并行
 一个线程发起系统调用而阻塞,不会影响其它线程的运行
 线程切换开销远小于进程切换
 内核本身也采用多线程技术可提高系统执行速度和效率
BAD:
 用户态运行线程,调度和管理线程则是内核态。模式的切换开销大。
2.ULT优缺点:
 用户线程的维护由应用进程完成;内核不了解用户线程的存在;线程切换不需要内核特权;
 用户线程调度算法可针对应用优化;
 多线程的实现与平台无关
 一旦系统调用引起进程阻塞,则整个进程的所有线程都不能执行
 以进程为单位分配cpu,所有在多处理器系统中没有优势

猜你喜欢

转载自blog.csdn.net/weixin_43271377/article/details/83217070