操作系统-杂记

进程

  • 进程本质上是正在执行的一个程序
  • 在这个地址空间中,进程可以进行读写。该地址空间中存放有可执行程序、程序的数据以及程序的堆栈
  • 除了该进程自身地址空间的内容以外,均存放在操作系统的一张表中,称为进程表(process table),
  • 进程表是数组(或链表)结构,当前存在 的每个进程都要占用其中一项。
  • 在读写文件之前,首先要打开文件,检査其访问权限。若权限许可,系统将返回一个小整数,称作 文件描述符(file descriptor

系统调用

  • 在UNIX中,fork是唯一可以在POSIX中创建进程的途径。它创建一个原有进程的精确副本,包括所有的 文件描述符、寄存器等内容。
  • 在fork之后,原有的进程及其副本(父与子)就分开了。在fork时,所有 的变量具有一样的值,虽然父进程的数据被复制用以创建子进程,但是其中一个的后续变化并不会影响到另一个。
  • (由父进程和子进程共享的程序正文,是不可改变的。)fork调用返回一个值,在子进程中该 值为零,并且在父进程中等于子进程的进程标识符(Process IDentifier, PID)O使用返回的PID,就可 以在两个进程中看出哪一个是父进程,哪一个是子进程。
  • 多数情形下,在fork之后,子进程需要执行与父进程不同的代码。这里考虑shell的情形。它从终端 读取命令,创建一个子进程,等待该子进程执行命令,在该子进程终止时,读入下一条命令。
  • 为了等待子进程结束,父进程执行waitpid系统调用,它只是等待,直至子进程终止(若有多个子进程的话,则直
    至任何一个子进程终止)。waitpid可以等待一个特定的子进程,或者通过将第一个参数设为-1的方式,
  • 等待任何一个老的子进程。在waitpid完成之后,将把第二个参数statloc所指向的地址设置为子进程的退 出状态(正常或异常终止以及退出值)。
  • 有各种可使用的选项,它们由第三个参数确定。例如,如果没有已经退出的子进程则立即返回。

shell如何使用fork。

  • 在键入一条命令后,shell调用fork创建一个新的进程。这个子进程必须执行用户的命令。
  • 通过使用execve系统调用可以实现这一点,这个系统调用会引起其整个核心映像被一个文件所替代,该文件由第一个参数给定。
  • 实际上,该系统调用自身是exec系统调用,但是若干个 不同的库过程使用不同的参数和稍有差别的名称调用该系统调用。在这里,我们把它们都视为系统调用。

进程的创建

4种主要事件会导致进程的创建:

  1. 系统初始化。
  2. 正在运行的程序执行了创建进程的系统调用。
  3. 用户请求创建一个新进程。
  4. 一个批处理作业的初始化。
  • 在请求到达时唤醒该进程以便服务该请求。停留在后台处理诸如电子邮件、Web页面、新闻、打 印之类活动的进程称为守护进程

重点:写时复制

  • 进程创建之后,父进程和子进程有各自不同的地址空间。
  • 如果其中某个进程在其地址空间中修改了一个字,这个修改对其他进程而言是不可见的。
  • 在UNIX中,子进程的初始地址空间是父进程的一个副本,但是这里涉及两个不同的地址空间,不可写的内存区是共享的。
  • 某些 UNIX的实现使程序正文在两者间共享,因为它不能被修改。或者,子进程共享父进程的所有内存,但这种情况下内存通过写时复制(copy-on-write)共享,
  • 这意味着一旦两者之一想要修改部分内存,则这块内存首先被明确地复制,以确保修改发生在私有内存区域。
  • 再次强调,可写的内存是不可以共享的。 但是,对于一个新创建的进程而言,确实有可能共享其创建者的其他资源,诸如打开的文件等

进程的状态

  1. 运行态(该时刻进程实际占用CPU)。
  2. 就绪态(可运行,但因为其他进程正在运行而暂时停止)。
  1. 阻塞态(除非某种外部事件发生, 否则进程不能运行)。

线程

  • 每个进程有一个地址空间和一个控制线程
    多线程的作用

  • Web服务器可以把获得大量访问的页面集合保存在内存中,避免到磁盘去调入这些页面,从而改善性能。这梓的一种页面集合称为高速缓存

  • 进程中的不同线程不像不同进程之间那样存在很大的独立性。

  • 所有的线程都有完全一样的地址空间,这意味着它们也共享同样的全局变量。

  • 每个线程的堆栈有一帧,供各个被调用但是还没有从中返回的过程使用。

  • 在该栈帧中存放了相应过程的局部变量以及过程调用完成之后使 用的返回地址

  • 在多线程的情况下,进程通常会从当前的单个线程开始。这个线程有能力通过调用一个库函数(如 thread_create)创建新的线程。
  • thread_create的参数专门指定了新线程要运行的过程名。这里,没有必要 对新线程的地址空间加以规定,因为新线程会自动在创建线程的地址空间中运行。
  • 有时,线程是有层次的,它们具有一种父子关系,但是,通常不存在这样一种关系,所有的线程都是平等的。
  • 不论有无层次关系,创建线程通常都返回一个线程标识符,该标识符就是新线程的名字。
  • 当一个线程完成工作后,可以通过调用一个库过程(如thread_exit)退出。该线程接着消失,不再
  • 可调度。在某些线程系统中,通过调用一个过程,例如threadJoin, 一个线程可以等待一个(特定)线 程退出。
  • 这个过程阻塞调用线程直到那个(特定)线程退出。 在这种情况下,线程的创建和终止非常类 似于进程的创建和终止,并且也有着同样的选项。
  • 另一个常见的线程调用是thread_yielci,它允许线程自动放弃CPU从而让另一个线程运行。这样一个 调用是很重要的,因为不同于进程,(线程库)无法利用时钟中断强制线程让出CPU。
  • 所以设法使线程行为"高尚"起来,并且随着时间的推移自动交出CPU,以便让其他线程有机会运行,就变得非常重要
  • 有的调用允许某个线程等待另一个线程完成某些任务,或等待一个线程宣称它已经完成了有关的工作等。

进程之间的通信

  1. 竞争条件:在一些操作系统中,协作的进程可能共享一些彼此都能读写的公用存储区
  2. 临界区:对共享内存进行访问的程序片段
1) 任何两个进程不能同时处于其临界区。 
2) 2) 不应对CPU的速度和数量做任何假设。
3) 临界区外运行的进程不得阻塞其他逬程。
4) 不得使进程无限期等待进入临界区。

信号量

  • 使用一个整型变量来累计唤醒次数叫信号量
  • down和up (分别为一般化后的sleep和wakeup)。对一信号量执行down操作,则是检査其值是否大于0。
  • 若该值大于0,则将其值减1 (即用掉一个保存的唤醒信号)并继续;若该值为0,则进程将睡眠,
  • 而且此时down操作并未结束。检査数值、修改变量值以及可能发生的 睡眠操作均作为一个单一的、不可分割的原子操作完成。
  • 保证一旦一个信号量操作开始,则在该操作完成或阻塞之前,其他进程均不允许访问该信号量。
  • 这种原子性对于解决同步问题和避免竞争条件是绝对必要的。
  • 所谓原子操作,是指一组相关联的操作要么都不间断地执行,要么都不执行。
posted @ 2021-07-17 14:39  xiaoff  阅读(60)  评论(0编辑  收藏  举报