1-4_基本概念_进程间通信及同步
内存映射
调用系统函数mmap()的进程,会在其虚拟地址空间中创建一个新的内存映射。
映射分为两类:
1、文件映射:将文件的部分区域映射入调用进程的虚拟内存。映射一旦完成,对文件映射内容的访问则转化为对相应内存区域的字节操作。映射页面会按需自动从文件中加载。
2、相映成趣的是并无文件与之对应的匿名映射,其映射页面的内容会被初始化为0。
有某一进程所映射的内存可以与其他进程的映射共享。达成共享的方式有二:
其一是两个进程都对某一文件的相同部分加以映射
其二是由fork()创建的子进程自父进程处继承映射。
当两个或多个进程共享的页面相同时,进程之一对页面内容的改动是否为其他进程所见呢?这取决于创建映射时所传入的标志参数。若传入标志参数为私有,则某进程对映射内容的修改对于其他进程是不可见的,而且这些改动也不会真的落实到文件上;若传入标志为共享,对映射内容的修改就会为其他进程所见,并且这些修改也会造成对文件的改动。
内存映射用途很多,其中包括:以可执行文件的相应段来初始化进程的文本段、内存(内容填充为0)分配、文件I/O(即映射内存I/O)以及进程间通信(通过共享映射)。
进程间通信(IPC)机制
读写磁盘文件中的信息是进程间通信的方法之一。可是,对许多程序来说,这种方法既慢又缺乏灵活性。因此,Linux提供了丰富的进程间通信(IPC)机制。
信号(signal),用来表示事件的发生。
管道(亦即shell用户所熟悉的“|”操作符)和FIFO,用于在进程间传递数据。
套接字(socket),供同一台主机或是联网的不同主机上所运行的进程之间传递数据。
文件锁定,为防止其他进程读取或更新文件内容,允许某进程对文件的部分区域加以锁定。
消息队列,用于在进程间交换消息(数据包)。
信号量(semaphore),用来同步进程动作。
共享内存,允许两个及两个以上进程共享一块内存。当某进程改变了共享内存的内容时,其他所有进程会立即了解到这一变化。
信号
尽管将信号视为IPC的方法之一,但其在其他方面的广泛应用则更为普遍。
人们往往将信号称为“软件中断”。进程收到信号,就意味着某一事件或异常情况的发生。信号的类型很多,每一种分别标识不同的事件或情况。采用不同的整数来标识各种信号类型,并以SIGxxxx形式的符号名加以定义。
内核、其他进程(只要具有相应的权限)或进程自身均可向进程发送信号。
发生下列情况之一时,内核可向进程发送信号:
用户键入中断字符(通常为Control-c)
进程的子进程之一已经终止。
由进程设定的定时器(告警时钟)已经到期。
进程尝试访问无效的内存地址。
在shell中,可使用kill命令向进程发送信号。在程序内部,系统调用kill()可提供相同功能。
收到信号时,进程会根据信号采取如下动作之一:
忽略信号。
被信号“杀死”
先挂起,之后再被专门信号唤醒。
就大多数信号类型而言,程序可选择不采取默认的信号动作,而是忽略信号(当信号的默认处理行为并非忽略此信号时,会派上用场)或建立自己的信号处理器。
信号处理器是由程序员定义的函数,会在进程收到信号时自动调用,根据信号的产生条件执行相应动作。
信号从产生直至送达至进程期间,一直处于挂起状态。通常,系统会在接收进程下次调度时,将处于挂起状态的信号同时送达。如果接收进程正在运行,则会立即将信号送达。然而,程序可以将信号纳入所谓“信号屏蔽”以求阻塞该信号。如果产生的信号处于“信号屏蔽”之列,那么此信号将一直保持挂起状态,直至解除对该信号的组设。