Linux IPC总结

本文详细解析了Linux系统中IPC机制的三种形式:SystemV IPC、BSD IPC和POSIX IPC,以及Socket通信的基本概念和实现原理。重点讨论了信号、管道、信号量、共享内存和消息队列等工具的使用方法,并通过代码示例展示了如何在实际应用中进行跨进程通信。同时,文章还介绍了Socket通信中的域名、类型和协议,以及如何创建、命名、监听和接受连接的步骤,帮助开发者掌握Linux环境下进程间高效协作的关键技术。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

IPC进程间通信(Inter-Process Communication)就是指多个进程之间相互通信,交换信息的方法。Linux IPC基本上都是从Unix平台上继承而来的。主要包括最初的Unix IPC,System V IPC以及基于Socket的IPC。另外,Linux也支持POSIX IPC。

System V,BSD,POSIX

    System V是Unix操作系统最早的商业发行版之一。它最初由AT&T(American Telephone & Telegraph)开发,最早在1983年发布。System V主要发行了4个版本,其中SVR4(System V Release 4)是最成功的版本。BSD(Berkeley Software Distribution,有时也被称为Berkeley Unix)是加州大学于1977至1995年间开发的。在19世纪八十年代至九十年代之间,System V和BSD代表了Unix的两种主要的操作风格。它们的主要区别如下:

    系统                      System V           BSD
    root脚本位置            /etc/init.d/       /etc/rc.d/
    默认shell                 Bshell             Cshell
    文件系统数据            /etc/mnttab     /etc/mtab
    内核位置                  /UNIX             /vmUnix
    打印机设备                lp                  rlp
    字符串函数                memcopy       bcopy
    终端初始化设置文件    /etc/initab       /etc/ttys
    终端控制                  termio            termios

    Linux系统的操作风格往往介于这两种风格之间。

    POSIX(Portable Operating System Interface [for Unix])是由IEEE(Institute of Electrical and Electronics Engineers,电子电气工程协会)开发的。现有的大部分Unix都遵循POSIX标准,而Linux从一开始就遵循POSIX标准。

最初的Unix IPC

1、信号

    信号是Unix/Linux系统在一定条件下生成的事件。信号是一种异步通信机制,进程不需要执行任何操作来等待信号的到达。信号异步通知接收信号的进程发生了某个事件,然后操作系统将会中断接收到信号的进程的执行,转而去执行相应的信号处理程序。

    (1)注册信号处理函数
        #include <signal.h>
        /*typedef void (*sighandler_t)(int);  sighandler_t signal(int signum,sighandler_t handler);*/
        * void (*signal(int signum, void (*handler)(int)))(int);  //SIG_IGN && SIG_DFL
        * int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);

    (2)发送信号
        #include <signal.h>
        * int kill(pid_t pid,int sig); //#include <sys/types.h> 
        * int raise(int sig);            //kill(getpid(),sig);
        * unsigned int alarm(unsigned int seconds); //(#include <unistd.h>) seconds秒后,向进程本身发送SIGALRM信号。

    (3)信号集
        信号集被定义为:typedef struct {unsigned long sig[_NSIG_WORDS];} sigset_t;
        * int sigaddset(sigset_t *set,int sig);
        * int sigemptyset(sigset_t *set);

2、管道(Pipe)

    管道用来连接不同进程之间的数据流。

    (1)在两个程序之间传递数据的最简单的方法是使用popen()和pclose()函数:
        #include <stdio.h>
        FILE *popen(const char *command, const char *open_mode);
        int pclose(FILE *stream);

    popen()函数首先调用一个shell,然后把command作为参数传递给shell。这样每次调用popen()函数都需要启动两个进程;但是由于在Linux中,所有的参数扩展(parameter expansion)都是由shell执行的,这样command中包含的所有参数扩展都可以在command程序启动之前完成。

    (2)pipe()函数:
        #include <unistd.h>
        int pipe(int pipefd[2]);

    popen()函数只能返回一个管道描述符,并且返回的是文件流(file stream),可以使用函数fread()和fwrite()来访问。pipe()函数可以返回两个管道描述符:pipefd[0]pipefd[1],任何写入pipefd[1]的数据都可以从pipefd[0]读回;pipe()函数返回的是文件描述符(file descriptor),因此只能使用底层的read()和write()系统调用来访问。pipe()函数通常用来实现父子进程之间的通信。

    (3)命名管道:FIFO
        #include <sys/types.h>
        #include <sys/stat.h>
        int mkfifo(const char *fifo_name, mode_t mode);

    前面两种管道只能用在相关的程序之间,使用命名管道可以解决这个问题。在使用open()打开FIFO时,mode中不能包含O_RDWR。mode最常用的是O_RDONLY,O_WRONLY与O_NONBLOCK的组合。O_NONBLOCK影响了read()和write()在FIFO上的执行方式。

    PS:要想查看库函数用法,最可靠的资料来自Linux manual page:

    $sudo apt-get install manpages-dev

    $man 3 function_name

System V IPC

    System V IPC指的是AT&T在System V.2发行版中引入的三种进程间通信工具:(1)信号量,用来管理对共享资源的访问 (2)共享内存,用来高效地实现进程间的数据共享 (3)消息队列,用来实现进程间数据的传递。我们把这三种工具统称为System V IPC的对象,每个对象都具有一个唯一的IPC标识符(identifier)。要保证不同的进程能够获取同一个IPC对象,必须提供一个IPC关键字(IPC key),内核负责把IPC关键字转换成IPC标识符。   

    System V IPC具有相似的语法,一般操作如下:

    (1)选择IPC关键字,可以使用如下三种方式:

       a)IPC_PRIVATE。由内核负责选择一个关键字然后生成一个IPC对象并把IPC标识符直接传递给另一个进程。
       b)直接选择一个关键字。
       c)使用ftok()函数生成一个关键字。

    (2)使用semget()/shmget()/msgget()函数根据IPC关键字key和一个标志flag创建或访问IPC对象。如果key是IPC_PRIVATE;或者key尚未与已经存在的IPC对象相关联且flag中包含IPC_CREAT标志,那么就会创建一个全新的IPC对象。

    (3)使用semctl()/shmctl()/msgctl()函数修改IPC对象的属性。

    (4)使用semctl()/shmctl()/msgctl()函数和IPC_RMID标志销毁IPC实例。

    System V IPC为每个IPC对象设置了一个ipc_perm结构体并在创建IPC对象的时候进行初始化。这个结构体中定义了IPC对象的访问权限和所有者:

    struct ipc_perm{
       uid_t uid;   //所有者的用户id
       gid_t gid;   //所有者的组id
       uid_t cuid;  //创建者的用户id
       gid_t cgid;  //创建者的组id
       mode_t mode; //访问模式
       …
    
};

    shell中管理IPC对象的命令是ipcs、ipcmk和ipcrm。

1、信号量(Semaphores)

    System V的信号量集表示的是一个或多个信号量的集合。内核为每个信号量集维护一个semid_ds数据结构,而信号量集中的每个信号量使用一个无名结构体表示,这个结构体至少包含以下成员:
    struct{
        unsigned short semval;//信号量值,总是>=0
        pid_t sempid;  //上一次操作的pid
       …
    
};

    #include <sys/types.h>
    #include <sys/ipc.h>
    
#include <sys/sem.h>
    (1)创建或访问信号量
        * int semget(key_t key,int nsems,int flag); 
    nsems指定信号量集中信号量的个数,如果只是获取信号量集的标识符(而非新建),那么nsems可以为0。flag的低9位作为信号量的访问权限位,类似于文件的访问权限;如果flag中同时指定了IPC_CREAT和IPC_EXCL,那么如果key已与现存IPC对象想关联的话,函数将会返回EEXIST错误。例如,flag可以为IPC_CREAT|0666。

    (2)控制信号量集
        * int semctl(int semid,int semnum,int cmd,union semun arg);
    对semid信号量集合执行cmd操作;cmd常用的两个值是:SETVAL初始化第semnum个信号量的值为arg.val;IPC_RMID删除信号量。

    (3)对一个或多个信号量进行操作
        * int semop(int semid,struct sembuf *sops,unsigned nsops);
        * struct sembuf{
              unsigned short sem_num;  //信号量索引
              short   sem_op;     //对信号量进行的操作,常用的两个值为-1和+1,分别代表P、V操作
              short   sem_flag;   //比较重要的值是SEM_UNDO:当进程结束时,相应的操作将被取消;同时,如果进程结束时没有释放资源的话,系统会自动释放
           
};

2、共享内存

    共享内存允许两个或多个进程共享一定的存储区,因为不需要拷贝数据,所以这是最快的一种IPC。

    #include <sys/ipc.h>
    
#include <sys/shm.h>
    (1)创建或访问共享内存
        * int shmget(key_t key,size_t size,int shmflg);

    (2)附加共享内存到进程的地址空间
        * void *shmat(int shmid,const void *shmaddr,int shmflg);//shmaddr通常为NULL,由系统选择共享内存附加的地址;shmflg可以为SHM_RDONLY

    (3)从进程的地址空间分离共享内存
        * int shmdt(const void *shmaddr); //shmaddr是shmat()函数的返回值

    (4)控制共享内存
        * int shmctl(int shmid,int cmd,struct shmid_ds *buf);
        * struct shmid_ds{
              struct ipc_perm shm_perm;
              …
          
}; 
    cmd的常用取值有:(a)IPC_STAT获取当前共享内存的shmid_ds结构并保存在buf中(2)IPC_SET使用buf中的值设置当前共享内存的shmid_ds结构(3)IPC_RMID删除当前共享内存

3、消息队列

    消息队列保存在内核中,是一个由消息组成的链表。

    #include <sys/types.h>
    #include <sys/ipc.h>
    
#include <sys/msg.h>
    (1)创建或访问消息队列
    * int msgget(key_t key,int msgflg);

    (2)操作消息队列
        * int msgsnd(int msqid,const void *msg,size_t nbytes,int msgflg);
    msg指向的结构体必须以一个long int成员开头,作为msgrcv()的消息类型,必须大于0。nbytes指的是msg指向结构体的大小,但不包括long int部分的大小
        * ssize_t msgrcv(int msqid,void *msg,size_t nbytes,long msgtype,int msgflg);
    如果msgtype是0,就返回消息队列中的第一个消息;如果是正整数,就返回队列中的第一个该类型的消息;如果是负数,就返回队列中具有最小值的第一个消息,并且该最小值要小于等于msgtype的绝对值。

    (3)控制消息队列
        * int msgctl(int msqid,int cmd,struct msqid_ds *buf);
        * struct msqid_ds{
              struct ipc_perm msg_perm;
              …
           };

Socket
  套接字(Socket)是由Berkeley在BSD系统中引入的一种基于连接的IPC,是对网络接口(硬件)和网络协议(软件)的抽象。它既解决了无名管道只能在相关进程间单向通信的问题,又解决了网络上不同主机之间无法通信的问题。

  套接字有三个属性:域(domain)、类型(type)和协议(protocol),对应于不同的域,套接字还有一个地址(address)来作为它的名字。

  域(domain)指定了套接字通信所用到的协议族,最常用的域是AF_INET,代表网络套接字,底层协议是IP协议。对于网络套接字,由于服务器端有可能会提供多种服务,客户端需要使用IP端口号来指定特定的服务。AF_UNIX代表本地套接字,使用Unix/Linux文件系统实现。

  IP协议提供了两种通信手段:流(streams)和数据报(datagrams),对应的套接字类型(type)分别为流式套接字和数据报套接字。流式套接字(SOCK_STREAM)用于提供面向连接、可靠的数据传输服务。该服务保证数据能够实现无差错、无重复发送,并按顺序接收。流式套接字使用TCP协议。数据报套接字(SOCK_DGRAM)提供了一种无连接的服务。该服务并不能保证数据传输的可靠性,数据有可能在传输过程中丢失或出现数据重复,且无法保证顺序地接收到数据。数据报套接字使用UDP协议。

  一种类型的套接字可能可以使用多于一种的协议来实现,套接字的协议(protocol)属性用于指定一种特定的协议。

1
2
  1. <span style="color:#339933">#include <sys/types.h></span>  
  2. <span style="color:#339933">#include <sys/socket.h></span>  

  (1) 创建套接字

1
  1. <span style="color:#993333">int</span> socket<span style="color:#009900">(</span><span style="color:#993333">int</span> domain<span style="color:#339933">,</span><span style="color:#993333">int</span> type<span style="color:#339933">,</span><span style="color:#993333">int</span> protocol<span style="color:#009900">)</span><span style="color:#339933">;</span>  

  对于SOCK_STREAM和SOCK_DGRAM而言,分别只有一种协议支持这种类型的套接字。因此protocol可以为0,表示默认的协议。
  每种套接字域都有一种对应的套接字地址。对于AF_UNIX和AF_INET,套接字地址分别为sockaddr_un和sockaddr_in,位于<sys/un.h>和<sys/in.h>中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
  1. <span style="color:#993333">struct</span> sockaddr_un<span style="color:#009900">{</span>   
  2.     sa_family_t sun_family<span style="color:#339933">;</span> <span style="color:#666666; font-style:italic">//AF_UNIX</span>  
  3.     <span style="color:#993333">char</span> sun_path<span style="color:#009900">[</span><span style="color:#009900">]</span><span style="color:#339933">;</span>    <span style="color:#666666; font-style:italic">//pathname</span>  
  4. <span style="color:#009900">}</span><span style="color:#339933">;</span>  
  5.    
  6. <span style="color:#993333">struct</span> sockaddr_in<span style="color:#009900">{</span>   
  7.     <span style="color:#993333">short</span> <span style="color:#993333">int</span> sin_family<span style="color:#339933">;</span> <span style="color:#666666; font-style:italic">//AF_INET </span>  
  8.     <span style="color:#993333">unsigned</span> <span style="color:#993333">short</span> <span style="color:#993333">int</span> sin_port<span style="color:#339933">;</span>   
  9.     <span style="color:#993333">struct</span> in_addr sin_addr<span style="color:#339933">;</span>  
  10.     <span style="color:#666666; font-style:italic">//...  </span>  
  11. <span style="color:#009900">}</span><span style="color:#339933">;</span>  
  12.    
  13. <span style="color:#993333">struct</span> in_addr<span style="color:#009900">{</span>   
  14.     <span style="color:#993333">unsigned</span> <span style="color:#993333">long</span> <span style="color:#993333">int</span> s_addr<span style="color:#339933">;</span><span style="color:#666666; font-style:italic">//可以使用inet_addr()把IP地址字符串转换成</span>  
  15.         <span style="color:#666666; font-style:italic">//  网络字节序(network byte order,使用大端模式)的二进制数据</span>  
  16. <span style="color:#009900">}</span><span style="color:#339933">;</span>  

  (2) 命名套接字

1
  1. <span style="color:#993333">int</span> bind<span style="color:#009900">(</span><span style="color:#993333">int</span> sockfd<span style="color:#339933">,</span><span style="color:#993333">const</span> <span style="color:#993333">struct</span> sockaddr <span style="color:#339933">*</span>addr<span style="color:#339933">,</span>socklen_t addrlen<span style="color:#009900">)</span><span style="color:#339933">;</span><span style="color:#666666; font-style:italic">//将无名套接字sockfd与addr绑定(bind)</span>  

  (3) 监听套接字

1
  1. <span style="color:#993333">int</span> listen<span style="color:#009900">(</span><span style="color:#993333">int</span> sockfd<span style="color:#339933">,</span><span style="color:#993333">int</span> backlog<span style="color:#009900">)</span><span style="color:#339933">;</span><span style="color:#666666; font-style:italic">//backlog限定了等待服务的队列的最大长度</span>  

  (4) 等待接受连接

1
  1. <span style="color:#993333">int</span> accept<span style="color:#009900">(</span><span style="color:#993333">int</span> sockfd<span style="color:#339933">,</span><span style="color:#993333">struct</span> sockaddr <span style="color:#339933">*</span>addr<span style="color:#339933">,</span>socklen_t <span style="color:#339933">*</span>addrlen<span style="color:#009900">)</span><span style="color:#339933">;</span>  

  当客户端程序尝试连接sockfd套接字时,accept返回一个新的套接字与客户端进行通信。如果addr不是NULL,那么客户端的地址将会保存在addr所指向的结构体中;调用accept()前必须先将addrlen初始化为addr所指向结构体的大小,accept()返回以后,addrlen将会被设置成客户端套接字地址结构体的实际大小。然后,通过对accept()返回的套接字执行read()和write()操作即可实现与客户端的简单的通信。

  (5) 建立连接(客户端)

1
  1. <span style="color:#993333">int</span> connect<span style="color:#009900">(</span><span style="color:#993333">int</span> sockfd<span style="color:#339933">,</span><span style="color:#993333">const</span> <span style="color:#993333">struct</span> sockaddr <span style="color:#339933">*</span>addr<span style="color:#339933">,</span>socklen_t addrlen<span style="color:#009900">)</span><span style="color:#339933">;</span>  

  connect()在无名套接字sockfd和addr之间建立连接。addr指向的结构体中可以包含服务器的IP地址和端口号等信息。

  (6) 数据传输

1
2
  1. ssize_t send<span style="color:#009900">(</span><span style="color:#993333">int</span> sockfd<span style="color:#339933">,</span><span style="color:#993333">const</span> <span style="color:#993333">void</span> <span style="color:#339933">*</span>buf<span style="color:#339933">,</span>size_t len<span style="color:#339933">,</span><span style="color:#993333">int</span> flags<span style="color:#009900">)</span><span style="color:#339933">;</span>  
  2. ssize_t recv<span style="color:#009900">(</span><span style="color:#993333">int</span> sockfd<span style="color:#339933">,</span> <span style="color:#993333">void</span> <span style="color:#339933">*</span>buf<span style="color:#339933">,</span> size_t len<span style="color:#339933">,</span><span style="color:#993333">int</span> flags<span style="color:#009900">)</span><span style="color:#339933">;</span>  

  (7) 关闭套接字

1
  1. <span style="color:#993333">int</span> close<span style="color:#009900">(</span><span style="color:#993333">int</span> fd<span style="color:#009900">)</span><span style="color:#339933">;</span>  

  (8) 主机字节序和网络字节序的转换

1
2
3
4
5
  1. <span style="color:#339933">#include <netinet/in.h></span>  
  2. <span style="color:#993333">unsigned</span> <span style="color:#993333">long</span> <span style="color:#993333">int</span> htonl<span style="color:#009900">(</span><span style="color:#993333">unsigned</span> <span style="color:#993333">long</span> <span style="color:#993333">int</span> hostlong<span style="color:#009900">)</span><span style="color:#339933">;</span>  <span style="color:#666666; font-style:italic">//host to network,long</span>  
  3. <span style="color:#993333">unsigned</span> <span style="color:#993333">short</span> <span style="color:#993333">int</span> htons<span style="color:#009900">(</span><span style="color:#993333">unsigned</span> <span style="color:#993333">short</span> <span style="color:#993333">int</span> hostshort<span style="color:#009900">)</span><span style="color:#339933">;</span>  
  4. <span style="color:#993333">unsigned</span> <span style="color:#993333">long</span> <span style="color:#993333">int</span> ntohl<span style="color:#009900">(</span><span style="color:#993333">unsigned</span> <span style="color:#993333">long</span> <span style="color:#993333">int</span> netlong<span style="color:#009900">)</span><span style="color:#339933">;</span>  
  5. <span style="color:#993333">unsigned</span> <span style="color:#993333">short</span> <span style="color:#993333">int</span> ntohs<span style="color:#009900">(</span><span style="color:#993333">unsigned</span> <span style="color:#993333">short</span> <span style="color:#993333">int</span> netshort<span style="color:#009900">)</span><span style="color:#339933">;</span>  

  long型函数用来转换sockaddr_in.in_addr.s_addr;short型函数用来转换sockaddr_in.sin_port。

总结:


System V IPC API

 

1,消息队列 链接

int ftok(const char *pathname, int prj_id);

int msgget(key_t key,int msgflag);

int msgsnd(int msqid,const void *msgp,size_t msgsz,int msgflg);

int msgrcv(int msqid,void *msgp,size_t msgsz,long msgtyp,int msgflg);

 

2,信号量 链接

int semget(key_t key,int nsems,int semflag);

int semctl(int semid,int semnum,int cmd,…);

int semop(int semid,struct sembuf *sops,unsigned nsops,struct timespec *timeout);

 

3,共享内存 链接

int shmget(key_t key,size_t size,int shmflag);

int shmctl(int shmid,int cmd,struct shmid_ds *buf);

  

POSIX IPC API

1,消息队列 链接

 #include <mqueue.h>
mqd_t mq_open (const char *name, int oflag, .../*mode_t mode ,struct mq_attr_t *attr */);

int mq_close (mqd_t mqdes);

int mq_unlink (const char *name);//name索引的文件的目录项链接stat.nlink减1;

int mq_getattr (mqd_t  mqdes, struct mq_attr * mqstat);

int mq_setattr (mqd_t __mqdes, const struct mq_attr * restrict  mqstat, struct mq_attr * restrict  omqstat);

ssize_t mq_receive (mqd_t mqdes, char *msg_ptr, size_t msg_len, unsigned int *msg_prio)  ;

int mq_send (mqd_t mqdes, const char *msg_ptr, size_t msg_len, unsigned int msg_prio)  ;

2,信号量 链接

sem_t * sem_open(const char * name,int oflag,mode_t mode,unsigned int value);

int sem_close(sem_t * sem)

int sem_close(const char *name)

int sem_wait(sem_t *sem),int sem_trywait(sem_t *sem)
int sem_getvalue(sem_t *sem,int *valp)
int sem_post(sem_t *sem)

3,共享内存 链接

第一种:

#include<sys/mman.h>

void *mmap(void *addr, size_t len, int port, int flags, int fd, off_t offset);

第二种:

#include<sys/mman.h>

int shm_open(const char* name, int oflag, mode_t mode);

int shm_unlink(const *name);


System V IPC 包括三种进程间通信方式:消息队列+信号量(又叫信号灯)+共享内存。
这三种IPC的几种结构又称为IPC对象,不同于管道和FIFO,管道和FIFO是基于文件系统的。在文件系统中可以看到某一个FIFO类型文件,但System V IPC是基于系统内核的,可以使用命令ipcs查看系统当前的IPC对象的状态。
 
1.IPC对象的概念
  IPC对象是活动在内核级别的一种进程间通信的工具,存在的IPC对象通过它的标识符来引用和访问,这个标识符是一个非负整数,它唯一的标识了一个IPC对象,这个IPC对象可以是三种类型中的任一种。
  IPC标识符与文件描述符不同,使用open函数打开一个文件时,返回的文件描述符的值为当前进程最小可用的文件描述符数组的下标。IPC对象删除或创建时相应的标识符的值会不断增加大最大的值,归零循环分配使用。linux系统中标识符被声明为整数,所以可能存在的最大标识符为65535.
  IPC的标识符只解决了内部访问一个IPC对象的问题,如何让多个进程都访问某一个特定的IPC对象还需要一个外部键(key),每一个IPC对象都于一个键相关联。这样就解决了多进程在一个IPC对象上汇合的问题。
  创建一个IPC对象时需要指定一个键值,类型为key_t,在中定义为一个长整型。键值到标识符的转换是由系统内核来维护的。当有了一个IPC对象的键值时,如何让多个进程知道这个键,有多种方法实现。
 
2.IPC对象的系统命令
在shell下可以使用一些命令来操作IPC对象,
#ipcs -a

点击(此处)折叠或打开

  1. ------ Shared Memory Segments --------
  2. key shmid owner perms bytes nattch status
  3. 0x00000000 65536 root 600 393216 2 dest
  4. 0x00000000 98305 root 600 393216 2 dest
  5. 0x00000000 131074 root 600 393216 2 dest
  6. 0x00000000 163843 root 600 393216 2 dest
  7. 0x00000000 196612 root 600 393216 2 dest
  8. 0x00000000 229381 root 600 393216 2 dest
  9. 0x00000000 262150 root 600 393216 2 dest
  10. 0x00000000 294919 root 600 393216 2 dest
  11. 0x00000000 327688 root 600 393216 2 dest
  12. 0x00000000 360457 root 600 393216 2 dest
  13. 0x00000000 393226 root 600 393216 2 dest
  14. ------ Semaphore Arrays --------
  15. key semid owner perms nsems
  16. ------ Message Queues --------
  17. key msqid owner perms used-bytes messages
输出三种类型的IPC对象,共享内存,信号量,消息队列
key是IPC对象的外键,shmid标识的IPC对象的标识符。owner表示IPC所属的用户,perms表示权限。
可以使用命令ipcrm 删除一个IPC对象;
#ipcrm -m 393226


### Linux 下的进程间通信 (IPC) 机制 Linux 提供了多种用于实现进程间通信(Inter-Process Communication, IPC)的技术,这些技术允许同的进程共享数据并协同工作。以下是常见的几种 IPC 方法及其特点: #### 1. 管道 (Pipes) 管道是一种简单的单向通信机制,适用于具有亲缘关系的进程之间。匿名管道可以通过 `pipe()` 系统调用来创建[^4]。它提供了一个文件描述符对,其中一个用于写入数据,另一个用于读取数据。 ```c #include <unistd.h> int pipe(int fd[2]); ``` 如果需要跨无亲缘关系的进程,则可以使用命名管道(FIFO),通过 `mkfifo` 或者 `mknod` 创建[^5]。 #### 2. 消息队列 (Message Queues) 消息队列提供了更高级别的抽象来传递结构化数据。它可以由多个生产者和消费者访问,并支持优先级排队功能。传统 POSIX 消息队列可通过函数如 `msgget`, `msgrcv`, 和 `msgsnd` 来操作[^6]。 现代 Linux 还实现了系统 V 风格的消息队列以及 C++ 中 `<mqueue.h>` 定义的标准接口[^7]。 #### 3. 共享内存 (Shared Memory) 这是最快的 IPC 形式之一,允许多个进程映射同一块物理内存区域进行快速的数据交换。通常配合信号量一起使用以同步访问控制。System V 的 shm* 函数族或者 mmap() 调用都可以建立共享存储区[^8]。 ```c // 使用 System V 共享内存的一个例子 key_t key = ftok("/tmp/shm", 'R'); int shmid = shmget(key, sizeof(some_data_structure), IPC_CREAT | 0666); void *shm = shmat(shmid, NULL, 0); ``` #### 4. 套接字 (Sockets) 尽管套接字主要用于网络编程,但它同样能够处理本地机器上的 IPC 。UNIX 域套接字特别适合这种情况,因为它们比其他形式效率更高且延迟更低[^9]。 ```python import socket sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) ``` #### 5. 信号 (Signals) 虽然严格意义上算是真正的 “通信”,但是发送信号确实可以在某些场景下作为一种通知手段告知目标进程发生了特定事件[^10]。 --- ### 总结 每种方法都有其适用范围和局限性,在实际开发过程中应根据具体需求选择合适的方案。例如对于少量简单信息传输可以选择信号或消息队列;而对于大数据量则更适合采用共享内存加信号量的方式等等。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值