Linux——进程间通信

来源:互联网 发布:php round 编辑:程序博客网 时间:2024/06/03 23:00

Linux是通过许多相互联系的、相对简单的进程搭建起整个系统,因此进程间通信(Inter-ProcessCommunication,IPC)对于其是很重要的。

进程间通信方式

  • 管道(pipe),包括无名管道与有名管道。可以把一个程序的输出连接到另外一个程序的输入。有名管道:有名管道:无名管道的升级版,可以让两个不相关的进程实现相互通信,以FIFO文件的形式存在于文件系统中。
  • 信号(signal)。信号是在软件层次上对中断进制的一种模拟。用于通知接收进程有事件发生,严格来说,信号这种手段并不是专为进程间通信而设计,它也用于内核与进程间的通信。一个进程收到一个信号与处理器收到一个中断请求效果上是一样的。
  • 消息队列。消息队列是消息的链接表,包括Posix消息队列和SystemV消息队列。它克服了前面两种通信方式中信息量有限的缺点,具有写权限的进程可以向消息队列中按照一定的规则添加新消息,对消息队列有读权限的进程则可以从消息队列中读取消息。
  • 共享内存。是一种最高效的进程间通信方式,它使得多个进程可以访问同一块内存空间。不同进程可以及时看到对方进程对共享内存数据的更新。不过这种通信机制需要依靠某种同步进制,如互斥锁和信号量等。
  • 套接字(socket)。主要用于不同主机间的进程间通信。

管道

无名管道

       无名通道是Linux中原始的管道通信方法具有亲缘关系的进程之间,可以通过系统调用建立起一个单向的通信管道。但是它是半双工模式,只能由父进程建立,所以对子进程是静态的,管道相当于被看做一种特殊的文件,不属于任何文件系统,只存在于内存中,一个进程写内容进管道,另一个进程从管道读内容。

有名管道

       有名管道是无名管道的升级,它可以让两个不相关的进程实现相互通信,以FIFO文件的形式存在于文件系统中因此两个进程可以把它当做普通文件一样进行读写操作,注意的是FIFO遵循先进先出(First-in First-out)

管道API

mkfifo

    头文件

#include <sys/types.h>#include <sys/stat.h>


   函数原型

int mkfifo(const char *pathname,    /*要创建管道的路径*/               mode_t mode);        /*管道文件的读写权限*/

       当使用open()打开FIFO文件时,O_NONBLOCK标志会有影响:

1.当使用O_NONBLOCK标志时,打开FIFO的读取操作会立刻返回,但是若还没有其他进程打开FIFO文件时,则写入的操作会返回ENXIO错误代码。

2.若没有使用O_NONBLOCK标志,打开FIFO的读取操作会等到其他进程打开FIFO文件并写入时才会正常返回,同样地,打开FIFO的写入操作会等到其他进程打开FIFO文件并读取时才会正常返回。

   返回值

成功:0失败:-1

popen

   头文件

#include <stdio.h>

   函数原型

FILE* popen(const char* command, const char* type);

       popen()会调用fork()创建子进程,然后在子进程中执行command命令,参数type可使用'r'代表读,'w'代表写,依照type的值,popen会建立管道连接到子进程的标准输入或输出设备,然后返回一个文件指针。随后进程就可以使用这个指针对子进程的标准输入或输出设备进行操作。

   返回值

成功:文件指针失败:NULL

pclose

   头文件

#include <stdio.h>

   函数原型

int pclose(FILE *stream);
      pclose用来关闭popen所建立的管道及文件指针,参数stream为popen()返回的文件指针。


   返回值

成功:返回子进程的结束状态失败:-1

pipe

   头文件

#include <unistd.h>

   函数原型

int pipe(int filedes[2]);
      pipe建立管道,并将文件描述由参数filedes数组返回。filedes[0]为管道里的读取端,filedes[1]为管道里的写入端。

   返回值

成功:0失败:-1

信号

       信号是进程间通信机制中唯一的异步通信方式。信号时间的发生有两个来源:软件来源(如系统调用kill、raise)和硬件来源(比如按下鼠标、键盘),一个信号的完整生命周期为:信号产生、信号注册、信号注销、执行信号处理函数。

Linux的信号列表


操作符号的意义:

       A    默认为终止进程

       B    默认为忽略此信号

       C    默认为内存倾泻

       D    默认为暂停进程执行

       E    此信号不可拦截

       F    此信号不可忽略

       G    此信号非POSIX标准


       进程有三种方式来相应一个信号。

       1.忽略信号

       面对信号不做任何处理。但是SIGKILL和SIGSTOP不能忽略。

       2.捕捉信号

       定义信号处理函数。当信号发生时,执行相应的处理函数。

       3.执行默认操作

       执行Linux对信号规定的默认操作。

发送信号的函数:kill、raise

捕获信号的函数:alarm、pause

设置信号的处理函数:signal

发送信号API

kill

   头文件

#include <signal.h>#include <sys/types.h>

   函数原型

int kill(pid_t pid, int sig);
       kill()用来传递参数sig指定的信号给参数pid指定的进程。参数pid有几种情况

       pid > 0, 将信号传给进程号为pid的进程。

       pid  = 0, 将信号发送给和进程号为pid的进程在同一个进程组的进程。

       pid = -1, 将信号发送给系统内的所有进程。

       pid < 0, 将信号发送给组进程号为pid绝对值的所有进程。

   返回值

成功:0失败:-1

raise

   头文件

#include <signal.h>
   函数原型

int raise(int sig)
       raise()用来将参数sig指定的信号发送给当前进程,如kill(getpid(), sig)。

   返回值

成功:0失败:-1

捕获信号API

alarm

   头文件

#include <unistd.h>
   函数原型

unsigned int alarm(unsigned int second);
      alarm()用来设置信号SIGALRM在参数second指定的时间后发送给目前进程。如果参数second为0,则之前设置的闹钟会取消,并将剩下的时间返回。

   返回值

返回之前闹钟剩余秒数若未设置闹钟则返回0

pause

   头文件

#include <unistd.h>
   函数原型

int pause(void);
       pause()会让当前进程暂停,进入休眠状态,直到被信号中断。
  返回值

-1

设置信号处理API

signal

   头文件

#include <signal.h>
   函数原型

void(*signal(int signum, void (*handler))(int))(int);
      signal()会以参数signum指定的信号编号来设置改信号的处理函数,当指定信号到达时就转到handler指定的函数进行处理。在信号到达并跳转handler指定的函数处理后,系统会自动换回原先预设的信号处理方式。如果handler不是函数指针,则必须为下列两个参数之一:

       SIG_IGN    忽略该信号

       SIG_DFL    采用系统默认方式处理信号

   返回值

成功:返回先前的信号处理函数指针失败:SIG_ERR(-1)

共享内存

      使用共享内存的关键在于如何在多个进程之间对给定的内存空间进行同步访问。一般使用信号量来实现这一目的。共享内存的实现有三个步骤:

      1.创建共享内存,使用函数shmget,从内存中获取一段共享内存。

      2.映射共享内存,使用函数shmat,把这段共享内存映射到具体的进程空间。

      3.撤销共享内存,使用函数shmdt。

共享内存API

shmget

  头文件

#include <sys/ipc.h>#include <sys/shm.h>

    函数原型

int shmget(key_t key, int size, int shmflg);

       shmget()用来获得参数key所关联的共享内存识别代号。如果key为IPC_PRIVATE则会创建新的共享内存,其大小由参数size决定。如果key不为IPC_PRIVATE,也不是已建立共享内存的IPC key,那么系统会视参数shmflg是否有IPC_CREAT位(shmflg & IPC_CREAT为真)来决定IPC key为参数key的共享内存。如果参数shmflg包含了IPC_CREAT和IPC_EXCL位,而无法依照参数key来建立共享内存,则表示该内存已存在此外shmflg也用来决定共享内存的存取权限,与open()一样。

    返回值

成功:共享内存识别代号失败:-1

shmat

    头文件

#include <sys/types.h>#include <sys/shm.h>

    函数原型

void *shmat(int shmid, const void *shmaddr, int shmflg);

       shmat()用来将参数shmid指向的共享内存和目前进程连接。参数shmid为欲连接共享内存的识别代号,参数shmaddr有几种情况:

       1.shmaddr为0,自动选择一个地址。

       2.shmaddr不为0,参数shmflg也无SHM_RND标志位,则以参数shmaddr为连接地址。

       3.shmaddr不为0,参数shmflg设置了SHM_RND标志位,则参数shmaddr自动调整为SHMLBA的整数倍。

      shmflg还可设置SHM_READONLY,表示该共享内存为只读。同时需要注意几点:在fork()之后,子进程将继承已连接的共享内存地址;在exec()之后,已连接的共享内存地址会自动脱离;在结束进程后,已连接的共享内存地址会自动脱离。

    返回值

成功:已连接好的地址失败:-1


shmdt

    头文件

#include <sys/types.h>#include <sys/shm.h>

    函数原型

int shmdt(const void *shmaddr);

      shmdt()用来将先前连接好的共享内存地址脱离目前进程,参数shmaddr为shmat()的返回值。

    返回值

成功:0失败:-1

消息队列         

       消息队列就是一个消息链表,消息可以顺序地发送到队列中,并以几种不同的方式从队列中获取。消息队列的实现包括创建或打开消息队列、添加消息、读取消息和控制消息队列分别使用msgget、msgsnd、msggrcv、msgctl。

消息队列API

msgget

   头文件

#include <sys/types.h>#include <sys/ipc.h>#include <sys/msg.h>

   函数原型

int msgget(key_t key, int msgflg);

       msgget()用来取得参数key所关联的消息队列识别代号。如果key为IPC_PRIVATE则会创建新的消息队列。如果key不为IPC_PRIVATE,也不是已建立消息队列的IPC key,那么系统会视参数msgflg是否有IPC_CREAT位(shmflg & IPC_CREAT为真)来决定IPC key为参数key消息队列。如果参数msgflg包含了IPC_CREAT和IPC_EXCL位,而无法依照参数key来建立消息队列,则表示该消息队列已存在此外msgflg也用来决定消息队列的存取权限,与open()一样。

   返回值

成功:消息队列识别代号失败:-1


msgsnd

   头文件

#include <sys/types.h>#include <sys/ipc.h>#include <sys/msg.h>

   函数原型

int msgsnd(int msqid, struct msgbuf *msgp, int msgsz, int msgflg);


       msgsnd()用来将参数msgp指定的信息传送至参数为msqid的消息队列中,参数msgp为msgbuf结构:

struct msgbuf{    long mtype,          /*信息的种类*/    cahr mtext[LEN];     /*消息正文,LEN由用户指定*/}
       参数msgsz为信息数据的长度,即mtext的长度。参数msgflg可以设为IPC_NOW\AIT,即如果有队列已满或其他一些情况无法马上发送消息,则立即返回,若设为0,则msgsnd调用阻塞直到条件满足为止。

    返回值

成功:0失败:-1


msgrcv

   头文件

#include <sys/types.h>#include <sys/ipc.h>#include <sys/msg.h>

   函数原型

int msgsnd(int msqid, struct msgbuf *msgp, int msgsz, long msgtype, int msgflg);


       msgsnd()用来从参数为msqid的消息队列中读取消息并将其存于参数msgp指定的msgbuf中,参数msgp为msgbuf结构:

struct msgbuf{    long mtype,          /*信息的种类*/    cahr mtext[LEN];     /*消息正文,LEN由用户指定*/}
       参数msgsz为信息数据的长度,即mtext的长度。参数msgtype用来指定所要读取的消息种类:

       msgtype=0       接收消息队列中的第一个消息

       msgtype>0       接收消息队列中的第一个类型为msgtype的消息

       msgtype<0       接收消息队列中的第一个类型不小于msgtype绝对值的消息

       参数msgflg也有几种取值:

       MSG_NOERROR              若返回的消息比msgsz字节多,则会被截断为字节大小为msgsz的消息

       IPC_NOWAT                     如果有队列已满或其他一些情况无法马上发送消息,则立即返回

       0                                        调用阻塞直到条件满足为止


    返回值

成功:0失败:-1


msgctl

   头文件

#include <sys/types.h>#include <sys/ipc.h>#include <sys/msg.h>

   函数原型

int msgctl(int msqid, int cmd, struct msqid_ds *buf);


       msgctl()提供了几种方式来控制消息队列的运作。参数msqid为欲控制消息队列的识别代号,参数cmd为控制的操作:

IPC_STAT                   把消息队列的msqid_ds结构数据复制到buf中

IPC_SET                     将参数buf所指结构msqid_ds中的msg_perm.uid、msg_perm.gidmsg_perm.mode和msg_qbytes参数复制到消息队列中的msqid_ds结构中。msqid_ds结构如下:

struct msqid_ds{    struct ipc_perm msg_perm,     struct msg *msg_first,               /*指向队列中的第一个消息*/    struct msg *msg_last,                /*指向队列中的最后一个消息*/    time_t msg_stime,                    /*最后一次用msgsnd送入消息的时间*/    time_t msg_rtime,                    /*最后一次用msgrc获取消息的时间*/    time_t msg_ctime,                    /*最后一次更改此消息队列的时间*/    struct wait_queue *wwait,                struct wait_queue *rwait,    unsigned short int msg_cbytes,       /*目前消息队列中存放的字节数*/    unsigned short int msg_qnum,         /*消息队列中的消息个数*/    unsigned short int msg_qbytes,       /*消息队列所能存放的最大字符数*/    ipc_pid_t msg_lspid,                 /*最后一个使用msgsnd送入消息的进程pid*/    ipc_pid_t msg_lrpid                  /*最后一个使用msgrc获取消息的进程pid*/};

       msg_perm的结构如下:

struct ipc_perm{    key_t key,                            /*此消息的IPC key*/    unsigned short int uid,               /*此消息队列所属用户识别码*/    unsigned short int gid,               /*此消息队列所属组识别码*/    unsigned short int cuid,              /*建立消息队列的用户识别码*/    unsigned short int cgid,              /*建立消息队列的组识别码*/    unsigned short int mode,              /*此消息队列的读写权限*/    unsigned short int seq                /*序号*/};

    返回值

成功:0失败:-1



0 0