Linux系统编程——进程间通信(一)

基本操作命令:

 ps -ajx/-aux/-ef 查看进程间状态/的相互关系

 top 动态显示系统中的进程

 nice 按照指定的优先级运行 /renice 改变正在运行的进程的优先级

 kill -9杀死进程

 jobs 查看后台进程数

 

进程的结构、类型、状态、模式

0.Linux中进程包括三段:

 (1)数据段。存放的是全局变量,常量以及动态内存的数据空间。

 (2)正文段。存放的是程序中的代码。

 (3)堆栈段。存放的是函数的返回地址,函数的参数以及局部变量。

 进程类型:交互进程,既可以在前台运行,也可以在后台运行。该类进程是由shell控制和运行的。

                 批处理进程,不属于某个终端,被提交到一个队列中以便顺序执行

                 守护进程:系统开始时执行,系统关闭时结束。

1.系统中专用的进程:

 (1)ID为0的进程是调度进程,通常称为交换进程,该进程是内核的一部分,它并不执行任何磁盘上的程序,因此也被称为系统进程。

 (2)ID为1 的进程是init进程,init进程通常读与系统有关的初始化文件,并将系统引导到一个状态。init进程绝不会终止,init进程是所有孤儿进程的父进程。

 (3)ID为2的进程是页守护进程,此进程负责支持虚拟存储系统的分页操作。

  2.子进程是父进程的副本,子进程获得父进程的数据空间,堆和栈的副本。这是子进程拥有的独立的空间。父子进程并不共享这些存储空间,父子进程共享正文段。fork之后到底是先执行父进程还是子进程是不一定的,取决于内核所使用的调度算法,如果要求父子进程之间相互同步,则要求进程间的通信。

    vfork也能创建进程,但是不能产生父进程的副本,当子进程需要改变内存中的数据时,才拷贝父进程。

  父进程与子进程的区别:

        fork的返回值不同;

   进程ID不同;

   父进程设置的文件锁不会被子进程继承;

   子进程未处理的闹钟被清除;

   子进程未处理的信号集设置为空集。

        (1)孤儿进程:对于所有父进程终止的所有进程,他们的父进程都变为init进程。init无论何时只要有一个进程终止,init就会调用一个wait函数取得其终止状态。这样就防止了系统中好多个僵尸进程。

        (2)僵尸进程:子进程结束,父进程没有收尸。

        (3)进程间数据共享。

exec函数族:当进程认为自己不能为系统和用户做出任何贡献的时候,就可以调用exec函数族,让自己执行新的程序。如果某个进程在执行一个程序的时候想执行另一个程序,则首先创建子进程,在子进程中间调用exec函数。在用exec函数是要加上出错判断。

传统的进程间通信

(1)无名管道:无名管道是半双工的,数据只能想一个方向流动,具有固定的读端和写端;无名管道只能用于父子进程或者兄弟进程(具有亲缘关系的进程)之间。

          无名管道单独构成一种独立的文件系统;管道是基于文件描述符的通信方式,当一个管道建立时,它会创建两个文件描述符,其中fd[0] 固定于读管道,fd[1] 固定于写管道。管道只存在于内存中。

管道的读写:

          管道是一种文件,因此对管道的读写同read()和write()函数。

          一个进程向管道中写得内容被管道的另一端进程读出。写入的内容每次都添加在管道的尾端,并且每次都是从缓冲区的头部读出数据。

          当父子进程对管道进程读写的时候,如果父进程执行写操作,则关闭相应的读管道的文件描述符;而子进程进行写操作,它将关闭相应的读操作。

          当管道为空的时候,读操作会阻塞,向管道中写数据的时候Linux操作系统不保证写入的原子操作,管道中只要有空闲区域写进程会试图向管道中写入数据。如果读操作不读走缓冲区的数据,则写操作一直会阻塞。只有在读管道存在时,写管道才有意义,不

然则会受到系统发出的SIGPIPE信号(管道破裂)。应用程序可以处理该信号,也可以忽略(默认动作则是终止程序)。

#include <unistd.h>

int pipe(int fd[2])

(2)有名管道:不同于有名管道之处在于它提供一个路径名与之关联,有名管道可以使不相关的进程之间进行通信。以FIFO的文件形式存在于文件系统中。

         注意:FIFO严格遵循先进先出,对管道及FIFO的读总是从开始处返回数据,对他们的写则是把数据添加到末尾。不支持lseek()等文件操作。

#include <sys/types.h>

#include <sys/stat.h>

int mkfifo(const char * pathname, mode_t mode)

         该函数的第一个参数是一个普通的路径名,也就是创建后FIFO的名字。第二个参数与打开普通文件的open()函数中的mode 参数相同。如果mkfifo的第一个参数是一个已经存在的路径名时,会返回EEXIST错误,所以一般典型的调用代码首先会检查是否返回该错误,如果确实返回该错误,那么只要调用打开FIFO的函数就可以了。一般文件的I/O函数都可以用于FIFO,如close、read、write等等

        无名管道由一个在基本文件系统存储设备上的INODE,一个与其相连的内存INODE,两个打开文件控制块(分别对应管道的信息发送端和信息接收端)及其所属进程的描述信息来标识,在系统执行PIPE(P)命令行之后生成。并在P[0]中返回管道的读通道打开文件描述等,在P[1]中返回管道的写通道打开文件描述符。从结构上看,无名管道没有文件路径名,不占用文件目录项,因此文件目录结构中的链表不适用于这种文件,它只是存在于打开文件结构中的一个临时文件,随其所依附的进程的生存而生存,当进程终止时,无名管道也随之消亡。

        送入管道的信息一旦被读进程取用就从管道中消失了,读写操作之间符合先进先出的队列原则。

        管道文件是进程间通信的工具,为了尽量少的占用系统存储资源,一般系统均将其限制为最大长度为4096(PIPSIZ)字节的小型文件。当欲写入的消息超过4096字节时,就产生了读、写进程之间的同步问题。首先写操作查找PIPE文件中当前指针的偏移量F-OFFSET,然后从此位置开始尽量写入信息,当长度达到4096字节时,系统控制写进程进入睡眠状态,一直等待读进程取走全部信息时,文件长度指针置0,写进程才被唤醒继续工作。

        为防止多个进程同时读写一个管道文件而产生混乱,在管道文件的INODE标志字I-FLAY项中设置了ILOCK标志项,以设置软件锁的方式实现多进程间对管道文件的互斥使用。

无名管道存在着如下两个严重的缺点。

        第一,无名管道只能用于连接具有共同祖先的进程。

        第二,无名管道是依附进程而临时存在的。所以后来推出了一种无名管道的变种-有名管道,它常被称为FIFO。有名管道除继承了无名管道的所有特性优点之外,还屏弃了无名管道的两个缺点。

        首先,FIFO是一种永久性的机构,它具有普通的UNIX系统文件名。在系统下可利用MKNOD命令建立永久的管道,除非刻意删除它,否则它将一直保持在系统中。

        其次,正是由于有名管道以“文件名”来标识,所以只要事先约定某一特定文件名,那样所有知道该约定的服务进程,不论它们之间是否有亲属关系,都可以便利地利用管道进行通信。

       通过下面的命令可以创建一个命名管道:

       /etc/mknod pipe_name p 

       其中“pipe_name”是要创建的命名管道的名字,参数p 必须出现在命名管道名字之后。 

   named pipes(命名管道)管道具有很好的使用灵活性,表现在:

   1) 既可用于本地,又可用于网络。

   2) 可以通过它的名称而被引用。

   3) 支持多客户机连接。

   4) 支持双向通信。

   5) 支持异步重叠I/O操作。

 

无名管道与有名管道的异同:

 

            1.通信方式;

 

            2.进程是否相关;

 

            3.存在区域不同;

 

            4.存在周期不同;

 

            相同点:

 

            1.都是先进先出;            

 

            2.管道的容量相同;

 

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。