第七章 进程环境

引言

main函数是如何被调用的?
命令行参数是如何传送给执行程序的?
典型的存储器布局是什么样式?
如何分配另外的存储空间?
进程如何使用环境变量?
进程终止的不同方式?

main函数

main函数的原型是:

int main(int argc, char *argv[ ]) ;

argc:命令行参数的数目。
argv:指向各个参数的指针所构成的数组。

进程终止

(1) 正常终止:
(a) 从main返回。
(b) 调用exit。
© 调用_exit。
(2) 异常终止:
(a) 调用abort。
(b) 由一个信号终止。

exit和_exit函数

  exit和_exit函数用于正常终止一个程序:_exit立即进入内核,exit则先执行一些清除处理(包括调用执行各终止处理程序,关闭所有标准I / O流等),然后进入内核。

#include <stdlib.h>
void exit(int status) ;#include <unistd.h>
void _exit (int status) ;

atexit函数

  按照ANSIC的规定,一个进程可以登记多至32个函数,这些函数将由exit自动调用。我们称这些函数为终止处理程序(exit handler),并用atexit函数来登记这些函数。
  e x i t以登记这些函数的相反顺序调用它们。同一函数如若登记多次,则也被调用多次。

C程序的启动和终止


  注意,内核使程序执行的唯一方法是调用一个e x e c函数。
  进程自愿终止的唯一方法是显式/隐式地(调用exit)调用_exit。

命令行参数

  当执行一个程序时,调用exec的进程可将命令行参数传递给该新程序。这是UNIX shell的一部分常规操作。

环境表

  每个程序都接收到一张环境表。与参数表一样,环境表也是一个字符指针数组,其中每个指针包含一个以null结束的字符串的地址。全局变量environ则包含了该指针数组的地址。
extern char **environ;

C程序的存储空间布局


  正文段:这是由CPU执行的机器指令部分。通常,正文段是可共享的,所以即使是经常执行的程序在存储器中也只需有一个副本,另外,正文段常常是只读的,以防止程序由于意外事故而修改其自身的指令。
  初始化数据段
  非初始化数据段
  栈:自动变量以及每次函数调用时所需保存的信息都存放在此段中。每次函数调用时,其返回地址、以及调用者的环境信息(例如某些机器寄存器)都存放在栈中。然后,新被调用的函数在栈上为其自动和临时变量分配存储空间。通过以这种方式使用栈, C函数可以递归调用。
  堆:通常在堆中进行动态存储分配。

共享库

  共享库使得可执行文件中不再需要包含常用的库函数,而只需在所有进程都可存取的存储区中保存这种库例程的一个副本。程序第一次执行或者第一次调用某个库函数时,用动态连接方法将程序与共享库函数相连接。这样减少了每个可执行文件的长度,但增加了一些运行时间开销。共享库的另一个优点是可以用库函数的新版本代替老版本而无需对使用该库的程序重新连接编辑。

动态内存分配

  ANSI C说明了三个用于存储空间动态分配的函数。
(1) malloc。分配指定字节数的存储区。此存储区中的初始值不确定。
(2) calloc。为指定长度的对象,分配能容纳其指定个数的存储空间。该空间中的每一位( bit )都初始化为0。
(3) realloc。更改以前分配存储区的长度(增加或减少)。当增加长度时,可能需将以前分配存储区的内容移到另一个足够大的区域,而新增区域内的初始值则不确定。

第八章 进程控制

进程标识

  每个进程都有一个非负整型的唯一进程ID。

fork函数

  一个现存进程调用 fork 函数是 UNIX 内核创建一个新进程唯一方法

 pid_t fork(void);//返回值:子进程中返回0,父进程中返回子进程ID,出错返回-1

  该函数被调用一次,但返回两次。两次返回的区别是子进程的返回值是0,而父进程的返回值则是新子进程的进程ID。
子进程和父进程继续执行 fork 之后的指令。子进程是父进程的复制品。例如,子进程获得父进程数据空间、堆和栈的复制品。注意,这是子进程所拥有的拷贝。父、子进程并不共享这些存储空间部分。

文件共享

  fork的一个特性是所有由父进程打开的描述符都被复制到子进程中。父、子进程每个相同的打开描述符共享一个文件表项。
  在fork之后处理文件描述符有两种常见的情况:
(1) 父进程等待子进程完成。在这种情况下,父进程无需对其描述符做任何处理。当子进程终止后,它曾进行过读、写操作的任一共享描述符的文件位移量已做了相应更新。
(2) 父、子进程各自执行不同的程序段。在这种情况下,在 fork 之后,父、子进程各自关闭它们不需使用的文件描述符,并且不干扰对方使用的文件描述符。这种方法是网络服务进程中经常使用的。

fork的两种用法

(1)一个父进程希望复制自己,使父、子进程同时执行不同的代码段。这在网络服务进程中是常见的——父进程等待委托者的服务请求。当这种请求到达时,父进程调用fork,使子进程处理此请求。父进程则继续等待下一个服务请求。
(2)一个进程要执行一个不同的程序。

竞态条件

  当多个进程都企图对共享数据进行某种处理,而最后的结果又取决于进程运行的顺序时,则我们认为这发生了竞态条件(race condition)。

exec函数

  用fork函数创建子进程后,子进程往往要调用一种 exec 函数以执行另一个程序。当进程调用一种 exec 函数时,该进程完全由新程序代换,而新程序则从其main函数开始执行。因为调用exec并不创建新进程,所以前后的进程ID并未改变。exec只是用另一个新程替换了当前进程的正文、数据、堆和栈段。

第九章 进程关系

进程组

  每个进程除了有一进程ID之外,还属于一个进程组。进程组是一个或多个进程的集合。每个进程组有一个唯一的进程组ID。
  每个进程组有一个组长进程。进程组ID等于组长进程ID。
  进程组组长可以创建一个进程组,创建该组中的进程,然后终止。只要在某个进程组中有一个进程存在,则该进程组就存在,这与其组长进程是否终止无关。从进程组创建开始到其中最后一个进程离开为止的时间区间称为进程组的生命期。某个进程组中的最后一个进程可以终止,也可以参加另一个进程组。

对话期

  对话期(session)是一个或多个进程组的集合。通常是由shell的管道线将几个进程编成一组的。

控制终端

第十章 信号

信号的概念

  信号是软件中断,信号提供了一种处理异步事件的方法。

信号的处理方式

(1)忽略此信号。
(2)捕捉信号。为了做到这一点要通知内核在某种信号发生时,调用一个用户函数。
(3)执行系统默认动作。注意,对大多数信号的系统默认动作是终止该进程。

signal函数

void (*signal (int signo , void(*func)(int))) (int);

  signal函数的原型说明此函数要求两个参数,返回一个函数指针,而该指针所指向的函数无返回值(void)。第一个参数signo是一个整型数,第二个参数是函数指针,它所指向的函数需要一个整型参数,无返回值。用一般语言来描述也就是要向信号处理程序传送一个整型参数,而它却无返回值。signal的返回值则是指向以前的信号处理程序的指针。
  当执行一个程序时,所有信号的状态都是系统默认或忽略。
  当一个进程调用fork时,其子进程继承父进程的信号处理方式。

第十四章 高级I/O

非阻塞I/O

非阻塞I/O使我们可以调用不会永远阻塞的I/O操作,例如open,read和write。如果这种操作不能完成,则立即出错返回,表示该操作如继续执行将继续阻塞下去。

记录锁

当两个人同时编辑一个文件时,其后果将如何呢?在很多UNIX系统中,该文件的最后状态取决于写该文件的最后一个进程。
但是对于有些应用程序,例如数据库,有时进程需要确保它正在单独写一个文件。为了向进程提供这种功能,较新的UNIX系统提供了记录锁机制。
记录锁(recordlocking)的功能是:一个进程正在读或修改文件的某个部分时,可以阻止其他进程修改同一文件区。对于UNIX,“记录”这个定语也是误用,因为UNIX内核根本没有使用文件记录这种概念。一个更适合的术语可能是“区域锁”,因为它锁定的只是文件的一个区域(也可能是整个文件)。

I/O多路转接

I/O多路转接(I/Omultiplexing)。其基本思想是:先构造一张有
关描述符的表,然后调用一个函数,它要到这些描述符中的一个已准备好进行I/O时才返回。在返回时,它告诉进程哪一个描述符已准备好可以进行I/O。

select函数

传向select的参数告诉内核:
(1)我们所关心的描述符。
(2)对于每个描述符我们所关心的条件(是否读一个给定的描述符?是否想写一个给定的描述符?是否关心一个描述符的异常条件?)。
(3)希望等待多长时间(可以永远等待,等待一个固定量时间,或完全不等待)。

从select返回时,内核告诉我们:
(1)已准备好的描述符的数量。
(2)哪一个描述符已准备好读、写或异常条件。

poll函数

poll函数类似于select,但是其调用形式则有所不同。与select不同,poll不是为每个条件构造一个描述符集,而是构造一个pollfd结构数组,每个数组元素指定一个描述符编号以及对其所关心的条件。

异步I/O

使用s e l e c t和p o l l可以实现异步I / O。关于描述符的状态,系统并不主动告诉我们任何信息,我们需要主动地进行查询(调用s e l e c t或p o l l)。

第十五章 进程间通信

进程之间相互通信的其他技术—IPC(InterProcessCommunication)

管道

管道有两种限制;
(1)它们是半双工的。数据只能在一个方向上流动。
(2)它们只能在具有公共祖先的进程之间使用。通常,一个管道由一个进程创建,然后该进程调用fork,此后父、子进程之间就可应用该管道。

管道是由调用pipe函数而创建的。intpipe(intfiledes[2]);
经由参数filedes返回两个文件描述符:filedes[0]为读而打开,filedes[1]为写而打开。filedes[1]的输出是filedes[0]的输入。
通常,调用pipe的进程接着调用fork,这样就创建了从父进程到子进程或反之的IPC通道。

FIFO

FIFO有时被称为命名管道。管道只能由相关进程使用,它们共同的祖先进程创建了管道。但是,通过FIFO,不相关的进程也能交换数据。FIFO是一种文件类型,创建FIFO类似于创建文件。

消息队列

消息队列是消息的链接表,存放在内核中并由消息队列标识符标识。我们将称消息队列为“队列”,其标识符为“队列ID”。

信号量

信号量与已经介绍过的IPC机构(管道、FIFO以及消息列队)不同。它是一个计数器,用于多进程对共享数据对象的存取。
为了获得共享资源,进程需要执行下列操作:
(1)测试控制该资源的信号量。
(2)若此信号量的值为正,则进程可以使用该资源。进程将信号量值减1,表示它使用了一个资源单位。
(3)若此信号量的值为0,则进程进入睡眠状态,直至信号量值大于0。若进程被唤醒后,它返回至(第(1)步)。
当进程不再使用由一个信息量控制的共享资源时,该信号量值增1。如果有进程正在睡眠等待此信号量,则唤醒它们。
为了正确地实现信息量,信号量值的测试及减1操作应当是原子操作。为此,信号量通常是在内核中实现的。

共享存储

允许两个或多个进程共享一给定的存储区。因为数据不需要在客户机和服务器之间复制,所以这是最快的一种IPC。
使用共享存储的唯一窍门是多个进程之间对一给定存储区的同步存取。若服务器将数据放入共享存储区,则在服务器做完这一操作之前,客户机不应当去取这些数据。通常,信号量被用来实现对共享存储存取的同步。

Unix环境高级编程——学习笔记相关推荐

  1. Unix环境高级编程学习笔记(七) 多线程

    线程概述 线程(thread)技术早在60年代就被提出,但真正应用多线程到操作系统中去,是在80年代中期,solaris是这方面的佼佼者.传统的Unix也支持线程的概念,但是在一个进程(process ...

  2. Unix环境高级编程学习笔记(一)

    第二章 文件I/O 1.文件描述符   对于内核而言,所有打开的文件都通过文件描述符引用,文件描述符是一个非负整数.   Unix shell使用文件描述符0表示标准输入,1表示标准输出,2表示标准出 ...

  3. Unix环境高级编程学习笔记(五)

    第七章 进程环境 1.main函数:int main(int argc, char *argv[]) 2.进程中止: 正常中止:(1)从main返回;(2)调用exit;(3)调用_exit或_Exi ...

  4. UNIX环境高级编程 学习笔记 第十六章 网络IPC:套接字

    socket的设计目标之一:同样的接口既可以用于计算机间通信,也可以用于计算机内通信.socket接口可采用许多不同的网络协议进行通信,本章讨论限制在因特网事实上的通信标准:TCP/IP协议栈. 套接 ...

  5. Unix环境高级编程学习笔记(二)

    第四章 文件和目录 本章将描述文件系统特征和文件性质 1.stat.fstat和lstat函数 原型:#include<sys/stat.h> int stat(const char* r ...

  6. 文件和目录(二)--unix环境高级编程读书笔记

    在linux中,文件的相关信息都记录在stat这个结构体中,文件长度是记录在stat的st_size成员中.对于普通文件,其长度可以为0,目录的长度一般为1024的倍数,这与linux文件系统中blo ...

  7. 《UNIX环境高级编程》笔记 第五章-标准IO库

    1. 流和FILE对象 在第三章的系统调用都是围绕文件描述符fd的.但是标准I/O库函数操作则是围绕流进行的.当使用标准I/O库打开或创建一个文件时,使用一个流与一个文件关联. 当打开一个流时,标准I ...

  8. 高级IO(一)--UNIX环境高级编程读书笔记

    在前面学习了文件IO,标准IO和终端IO,现在学习高级IO,UNIX中怎么有这么多的IO. 1.非阻塞IO 可以将系统调用分为两类:低速系统调用和其他.低速系统调用是可能会使进程永远阻塞的一类系统调用 ...

  9. 《UNIX环境高级编程》笔记 第十三章-守护进程

    1. 概念 守护进程(daemon)是生存期长的一种进程.它们常常在系统引导装入时启动,仅在系统关闭时才终止.因为它们没有控制终端,所以说它们是在后台运行的. Linux的大多数服务就是用守护进程实现 ...

  10. 《UNIX环境高级编程》笔记 第十九章-伪终端

    1. 概念 伪终端这个术语是指,对于一个应用程序而言,它看上去像一个终端,但事实上它并不是一个真正的终端.下图是伪终端的一个典型安排 通常,一个进程打开伪终端主设备,然后调用fork,子进程建立一个新 ...

最新文章

  1. php中this,self,parent三个关键字
  2. 使用PowerShell调用MTools分析MongoDB性能并发送邮件
  3. UVa 10055 - Hashmat the Brave Warrior
  4. ubuntu 安装bcompare
  5. ios Sqlite数据库增删改查基本操作
  6. 用matlab跑神经网络模型,怎样在matlab里建立一个BP神经网络模型?
  7. python 银行业务系统程序编程写_python多线程实现代码(模拟银行服务操作流程)
  8. sdut 数字三角形问题
  9. 1-9月欧洲新能源车份额上升 混动车注册量增加8.8%
  10. 贴吧用html标签,html标签3(转载)
  11. Ubuntu18.04下Tensorlayer安装
  12. Linux下C语言编程风格和规范
  13. python matrix用法_详解使用python绘制混淆矩阵(confusion_matrix)
  14. 【FI-AP】预付定金处理(Down payment)
  15. 需要使用新应用以打开此mswindowsstore解决办法
  16. 渥太华大学计算机专业,渥太华大学计算机专业解析
  17. php百度坐标转腾讯坐标,PHP实现腾讯与百度坐标转换
  18. ios swift 纯代码自定义view(控件) XYswitchView
  19. 无盘服务器磁盘缓存,网众无盘教程 教你挂盘设置缓存
  20. 孤独后厂村,IT人百态:30万互联网人跳不出的中国硅谷

热门文章

  1. C语言必背18个经典程序(含免费源码大全)
  2. JavaScript高级程序
  3. 内存卡无法格式化 修复
  4. 牛腩新闻发布系统-真假分页
  5. 王道考研操作系统笔记(第一章)附:王道考研408所有PPT和思维导图
  6. SAS9.4安装简易教程(保姆级)附带报错处理
  7. 20189221 2018-2019-2 《密码与安全新技术专题》课程报告总结
  8. 华为交换机vlan配置教程
  9. JavaScript函数的返回值
  10. 中国计算机学会(CCF)推荐中文科技期刊目录