Linux环境下,进程地址空间相互独立,每个进程各自有不同的用户地址空间。任何一个进程的全局变量在另一个进程中都看不到,所以进程和进程之间不能相互访问,要交换数据必须通过内核,在内核中开辟一块缓冲区,进程1把数据从用户空间拷到内核缓冲区,进程2再从内核缓冲区把数据读走,内核提供的这种机制称为进程间通信(IPC,InterProcessCommunication)。今天我们来看一下,Linux下常见的六大IPC通信方式。

1、信号

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

  • (1)注册信号处理函数
    #include<signal.h>/*typedefvoid (*sighandler_t)(int);  sighandler_t signal(int signum,sighandler_thandler);*/void (*signal(intsignum, void (*handler)(int)))(int);  //SIG_IGN && SIG_DFLint sigaction(int signum, const struct sigaction*act,struct sigaction *oldact);
复制代码
  • (2)发送信号
    #include<signal.h>intkill(pid_t pid,int sig); //#include<sys/types.h> intraise(intsig);            //kill(getpid(),sig);unsignedint alarm(unsigned int seconds); //(#include<unistd.h>) seconds秒后,向进程本身发送SIGALRM信号。
复制代码
  • (3)信号集 信号集被定义为:
    typedef struct {unsigned long sig[_NSIG_WORDS];}sigset_t;* intsigaddset(sigset_t *set,int sig);* intsigemptyset(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()函数:
    int pipe(int pipefd[2]);
复制代码

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

  • (3)命名管道:FIFO
intmkfifo(const char *fifo_name, mode_t mode);
复制代码

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

3、信号量(Semaphores)

System V的信号量集表示的是一个或多个信号量的集合。内核为每个信号量集维护一个semid_ds数据结构,而信号量集中的每个信号量使用一个无名结构体表示,这个结构体至少包含以下成员:

    struct{unsigned short semval;//信号量值,总是>=0pid_t sempid;  //上一次操作的pid…};
复制代码
  • (1)创建或访问信号量
    intsemget(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(intsemid,int semnum,int cmd,union semun arg);
复制代码

对semid信号量集合执行cmd操作;cmd常用的两个值是:SETVAL初始化第semnum个信号量的值为arg.val;IPC_RMID删除信号量。

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

4、共享内存

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

  • (1)创建或访问共享内存
    intshmget(key_t key,size_t size,int shmflg);
复制代码
  • (2)附加共享内存到进程的地址空间
    void*shmat(int shmid,const void *shmaddr,int shmflg);// shmaddr通常为NULL,由系统选择共享内存附加的地址;shmflg可以为SHM_RDONLY
复制代码
  • (3)从进程的地址空间分离共享内存
    * intshmdt(const void *shmaddr); //shmaddr是shmat()函数的返回值
复制代码
  • (4)控制共享内存
    intshmctl(int shmid,int cmd,struct shmid_ds *buf);struct shmid_ds{structipc_perm shm_perm;…};
复制代码

cmd的常用取值有:
(a)IPC_STAT获取当前共享内存的shmid_ds结构并保存在buf中
(b)IPC_SET使用buf中的值设置当前共享内存的shmid_ds结构
(c)IPC_RMID删除当前共享内存

5、消息队列

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

  • (1)创建或访问消息队列
   int msgget(key_t key,int msgflg);
复制代码
  • (2)操作消息队列
    intmsgsnd(int msqid,const void *msg,size_t nbytes,int msgflg);
复制代码

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

  • (3)控制消息队列
    int msgctl(intmsqid,int cmd,struct msqid_ds *buf);struct msqid_ds{struct ipc_permmsg_perm;…};复制代码

6、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)创建套接字
int socket(int  domain,int  type,int  protocol);
复制代码

对于SOCK_STREAM和SOCK_DGRAM而言,分别只有一种协议支持这种类型的套接字。因此protocol可以为0,表示默认的协议。

  • (2) 绑定套接字
int bind(int  sockfd,const  struct sockaddr *addr,socklen_t addrlen);//将无名套接字sockfd与addr绑定(bind)
复制代码
  • (3)监听套接字
int listen(int  sockfd,int  backlog);//backlog限定了等待服务的队列的最大长度
复制代码
  • (4)等待接受连接
int accept(int  sockfd,struct  sockaddr *addr,socklen_t  *addrlen);
复制代码

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

  • (5)建立连接(客户端)
int connect(int  sockfd,const  struct sockaddr *addr,socklen_t addrlen);
复制代码

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

  • (6)数据传输
ssize_t send(int sockfd,const void *buf,size_t  len,int  flags);ssize_t recv(int sockfd, void *buf, size_t len,int flags);
复制代码
  • (7)关闭套接字
int close(int  fd);
复制代码
  • (8)主机字节序和网络字节序的转换
#include <netinet/in.h>unsigned long int  htonl(unsigned  long int  hostlong);  //host to  network,longunsigned short int  htons(unsigned  short int  hostshort);unsigned long  int ntohl(unsigned long int  netlong);unsigned short int  ntohs(unsigned  short int  netshort);// long型函数用来转换sockaddr_in.in_addr.s_addr;
// short型函数用来转换sockaddr_in.sin_port。
复制代码

Linux 常见的六大 IPC 通信方式相关推荐

  1. Linux IPC通信方式

    Linux进程间通信(IPC)有几种方式,下面将将简单的简述一下: 一.管道(pipe) 管道是Linux支持的最初IPC方式,管道可分为无名管道,有名管道等. (一)无名管道,它具有几个特点: 1) ...

  2. linux常见的ipc,IPC(进程间通信)几种常用的方法

    linux进程间通信(IPC)有几种方式,下面将将简单的简述一下: 一.管道(pipe) 管道是Linux支持的最初IPC方式,管道可分为无名管道,有名管道等. (一)无名管道,它具有几个特点: 1) ...

  3. linux学习---linux基于文件的IPC(匿名管道pipe,命名管道mkfifo,普通文件,socket文件)

    常用的IPC分为两个类别,一是基于文件,而是基于内存 基于文件的分别有匿名管道,有名管道,普通的文件共享,socket文件 基于内存的有普通内存共享(本文章没有介绍),共享内存,共享信号量,消息队列 ...

  4. 几种常见进程间通信(IPC)方式之共享存储

    几种常见进程间通信(IPC)方式-共享存储 前言 进程间通信是指在不同进程之间传播或交换信息,在Linux环境下,进程地址空间相互独立,每个进程各自有不同的用户地址空间,进程之间不能相互访问.必须通过 ...

  5. linux常用ipc技术,LINUX系统编程之IPC

    LINUX系统编程之IPC(Inter Processes Communication) 一.信号 1.信号的产生 软件中断,异步通信,ctrl+c,kill函数,kill命令,硬件异常(段错误),软 ...

  6. linux的进程与线程通信方式,Linux的进程/线程间通信方式总结

    Linux系统中的进程间通信方式主要以下几种: 同一主机上的进程通信方式 * UNIX进程间通信方式: 包括管道(PIPE), 有名管道(FIFO), 和信号(Signal) * System V进程 ...

  7. Linux进程通信中IPC对象——IPC_PRIVATE与ftok

    在linux中,可以使用IPC对象来进行进程间通信.IPC对象存在于内核中,多进程可以操作同一个IPC对象.每个IPC对象都有一个唯一的编号,该编号是由系统分配的.那么不同的进程如何知道这个编号,进而 ...

  8. linux攻击端口,Linux 常见攻击端口封杀表

    Linux常见攻击端口封杀表 # Vi /etc/sysconfig/iptables # INPUT -A INPUT -p tcp --dport 135 -j REJECT -A INPUT - ...

  9. linux搜索有哪些文件夹,Linux常见几个查找命令

    以下命令是很久以前学习Linux,网上查到的命令自己做了下总结.记录在这里方便以后查看. Linux常见五种查找命令 1.which which命令的作用是,在PATH变量指定的路径中,搜索某个系统命 ...

最新文章

  1. php操作MySQL
  2. JMeter记录篇2——性能测试基础(2)
  3. @Import注解使用及源码分析
  4. 19. GD32F103C8T6入门教程-adc使用教程6-外部中断线11触发adc0
  5. c#和javascript分别轻松实现计算24点
  6. 类似clover的软件_Clover 我的电脑里的书签栏
  7. 【图像重建】基于matlab迭代步长自适应图像超分辨重建【含Matlab源码 048期】
  8. CAPL如何组装自定义报文
  9. 一元线性回归(Python)
  10. 【路径规划】基于蚁群算法求解带时间窗车辆路径问题(VRPTW)matlab代码
  11. 微信小程序点击事件绑定及传参详解
  12. 接入交换机下pc获取不了ip问题处理
  13. Ubuntu中如何安装安装QQ
  14. 云班课python测试答案_智慧职教云课堂APPPython程序设计题目答案
  15. 【渝粤题库】陕西师范大学292251 公司金融学Ⅰ 作业(高起专)
  16. mysql 主键 默认索引_mysql 主键和默认 设为索引的规则
  17. 漫画中国式项目管理重点总结
  18. Android 监听来电广播
  19. 1072:鸡尾酒疗法(C C++)
  20. java gmail 发送邮件_Java通过Gmail发送电子邮件

热门文章

  1. PHP封装返回Ajax字符串和JSON数组
  2. 希尔排序(Shell's Sort)的C语言实现
  3. Unity Time的使用
  4. CodeWarrior 初探(1) USBDM 芯片检测
  5. 你必须会的启发式搜索算法--A*算法
  6. C/C++服务器开发的必备利器–libconfig
  7. 【position也可以很复杂】当弹出层遇上了鼠标定位(下)
  8. Centos 安装Docker
  9. iOS控件之UILabel
  10. Types of intraclass correlation coefficience (ICC)