您的位置:首页 > 运维架构 > Linux

Linux下进程之间的通信

2017-11-12 10:32 120 查看
首先我们要了解进程间需要通信的原因

一、进程间通信原因:

(1)、数据传输

    一个进程需要将它的数据发送给另一个进程。

(2)、资源共享

    多个进程之间共享同样的资源。

(3)、通知事件

    一个进程需要向另一个或一组进程发送消息,通知

它们发生了某种事件。

(4)、进程控制

     有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另一个进程的所有操作,并能够及时知道它的状态改变。

二、进程间的通信方式:

Linux使用的进程间通信方式包括:
(1).无名管道(Pipe)及命名管道(Named pipe):无名管道可用于具有父子关系进程间的通信,命名管道用于无父子关系的进程之间通信。无父子关系的进程可将信息发送到某个命名管道中,并通过管道名读取信息。
(2).信号(Signal):信号是进程间高级的通信方式,用于通知其它进程有何种事件发生。此外,进程可以向自身发送信号,还可以获得Linux内核发出的信号。Linux支持UNIX系统早期信号函数sigal(),并从BSD引入了信号函数sigaction()。sigaction()函数不仅提供了更为有效的通信机制,并保持了接口的统一,已替代sigal()函数。

(3).报文(Message)队列:又称为消息队列。报文队列克服了信号的数据结构过于简单的问题,解决了管道数据流无格式和缓冲区长度受限等问题。
(4).共享内存:让多个进程访问同一个内存空间,适合于数据量极大和数据结构极为复杂的进程间通信。但这种方式牺牲了系统的安全性,所以通常与其它进程间通信形式混合使用,并避免以根用户权限执行。
(5).信号量(Semaphore):信号量是用于解决进程的同步和相关资源抢占而设计的。
(6).套接字(Socket):套接字是一种数据访问机制,不仅可用于进程间通信,还可用于网络通信。使用套接字最大的好处在于,Linux下的程序能快速移植到其它类UNIX平台上。
(7).D-Bus:D-Bus是一种高级的进程间通信机制,以前述机制为基础实现。它提供了丰富的接口和功能,简化了程序设计难度。

1.管道通信

(1).什么是管道?

    管道是单向的、先进先出的,它把一个进程的输出和另一个进程的输入连接在一起。一个进程(写进程)在管道的尾部写入数据,另一个进程(读进程)从管道的头部读出数据。

    数据被一个进程读出后,将被从管道中删除,其它读进程将不能再读到这些数据。管道提供了简单的流控制机制,进程试图读空管道时,进程将阻塞。同样,管道已经满时,进程再试图向管道写入数据,进程将阻塞。

(2).管道的创建

    管道包括无名管道和有名管道两种,前者用于父进程和子进程间的通信,后者可用于运行于同一系统中的任意两个进程间的通信。

    无名管道由pipe( )函数创建:

        int pipe(int filedis[2]);

     当一个管道建立时,它会创建两个文件描述符:filedis[0]
用于读管道,filedis[1]
用于写管道。



    关闭管道只需将这两个文件描述符关闭即可,可以使用普通的close函数逐个关闭。



    管道用于不同进程间通信。通常先创建一个管道,再通过fork函数创建一个子进程,该子进程会继承父进程所创建的管道。



    必须在系统调用fork()前调用pipe( ),否则子进程将不会继承文件描述符。

(3).命名管道:
    命名管道和无名管道基本相同,但也有不同点:无名管道只能由父子进程使用;但是通过命名管道,不相关的进程也能交换数据。

#include <sys/types.h>

#include <sys/stat.h>

int mkfifo(const char * pathname, mode_t mode)

pathname:FIFO文件名

mode:属性

一旦创建了一个FIFO,就可用open打开它,一般的文件访问函数(close、read、write等)都可用于FIFO。

当打开FIFO时,非阻塞标志(O_NONBLOCK)将对以后的读写产生如下影响:

(1)、没有使用O_NONBLOCK:访问要求无法满足时进程将阻塞。如试图读取空的FIFO,将导致进程阻塞。

(2)、使用O_NONBLOCK:访问要求无法满足时不阻塞,立刻出错返回,errno是ENXIO。

2.信号通信

    信号(signal)机制是Unix系统中最为古老的进程间通信机制,很多条件可以产生一个信号:

    (1)、当用户按某些按键时,产生信号。

    (2)、硬件异常产生信号:除数为0、无效的存储访问等等。这些情况通常由硬件检测到,将其通知内核,然后内核产生适当的信号通知进程,例如,内核对正访问一个无效存储区的进程产生一个SIGSEGV信号。

    (3)、进程用kill函数将信号发送给另一个进程。

    (4)、用户可用kill命令将信号发送给其他进程。

几个常用的信号含义如下所示:
¨SIGHUP:终端上发出的结束信号。
¨SIGINT:来自键盘的中断信号(CTRL+C)。
¨SIGQUIT:来自键盘的退出信号(CTRL +\)。
¨SIGFPE:浮点异常信号。
¨SIGKILL:该信号结束接收信号的进程。
¨SIGALRM:进程的定时器到期,发送该信号。
¨SIGTERM:kill发送出的信号。
¨SIGCHLD:标识子进程停止或结束的信号。
¨SIGSTOP:来自键盘(CTRL+Z)或调试程序的停止信号。

1).信号的发送
(1).kill()

格式:
#include <sys/types.h>

#include <signal.h>

int kill(pid_tpid,intsigno)

说明:kill向pid进程发送信号signo。调用成功返回0;否则,返回-1。其中pid参数值不同,接收进程不同,如表7.7所示。



(2).raise()

格式:

#include <signal.h>

intraise(intsigno)

说明:raise()向进程本身发送信号,参数为即将发送的信号值。调用成功返回0;否则,返回-1。

(3).alarm()

格式:
#include <unistd.h>

unsigned int alarm(unsigned
int seconds)

说明:alarm()专门为SIGALRM信号而设,在指定的时间seconds秒后,将向进程本身发送SIGALRM信号,又称为闹钟时间。进程调用alarm后,任何以前的alarm()调用都将无效。如果参数seconds为零,那么进程内将不再包含任何闹钟时间。如果调用alarm()前,进程中已经设置了闹钟时间,则返回上一个闹钟时间的剩余时间,否则返回0。

    如果进程要处理某一信号,就要在进程中接收该信号。
    接收信号主要用来确定信号值及进程针对该信号值得动作之间的映射关系,即进程将要处理哪个信号,该信号被传递给进程时,将执行何种操作。

2).信号的接收

   Linux主要有两个系统调用实现信号的接收,signal()和sigaction()。其中signal()在可靠信号系统调用的基础上实现,
是库函数。它只有两个参数,不支持信号传递信息,主要是用于前32种非实时信号的接收;

   sigaction()是较新的函数,有三个参数,支持信号传递信息,sigaction()优于signal(),主要体现在前者支持信号带有参数。

(1).signal()

格式:
#include <signal.h>

typedef  void (*sighandler_t)(int);

sighandler_t  signal(int signum, sighandler_t handler));

说明:signal()负责接收指定信号signum,并进行相应处理。第一个参数指定信号的值,第二个参数指定针对前面信号值的处理,如果signal()调用成功,返回最后一次为接收信号signum而调用signal()时的handler值;失败则返回SIG_ERR。

(2).sigaction()

格式:
#include <signal.h>

intsigaction(intsignum,conststructsigaction
*act,structsigaction *oldact);

说明:sigaction()用于改变进程接收到特定信号后的行为。该函数的第一个参数为信号的值,可以为除SIGKILL及SIGSTOP外的任何一个特定有效的信号。第二个参数是指向结构sigaction的一个实例的指针,在结构sigaction的实例中,指定了对特定信号的处理,可以为空,进程会以缺省方式对信号处理;第三个参数oldact指向的对象用来保存原来对相应信号的处理,可指定oldact为NULL。

3.共享内存

    共享内存是被多个进程共享的一部分物理内存。

   共享内存是进程间共享数据的一种最快的方法,一个进程向共享内存区域写入了数据,共享这个内存区域的所有进程就可以立刻看到其中的内容。



    创建共享内存:intshmget(key_t key,
size_t size,
intshmflg);
    获得共享内存的起始地址:void *
shmat(intshmid, const void *shmaddr,
intshmflg);
    从程序分离一块共享内存:int 
shmdt( const void *
shmaddr);
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: