linux 进程之间的通信方式

进程间的通信方式(IPC):

1)管道( pipe ):管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。

2)有名管道 (named pipe) : 有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信。

3)信号量( semophore ) : 信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。

4)消息队列( message queue ) : 消息队列是由消息的链表,存放在内核中并有消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。

5) 信号 ( sinal ) : 信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。信号是进程间通信机制中唯一的异步通信机制

6) 共享内存( shared memory ) :共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的 IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号量配合使用,来实现进程间的同步和通信。

7)套接字( socket ) : 套解口也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同主机间的进程通信。

线程间的通信通信方式:

1.锁机制:包括互斥锁、条件变量、读写锁

   互斥锁提供了以排他方式防止数据结构被并发修改的方法。

   读写锁允许多个线程同时读共享数据,而对写操作是互斥的。

   条件变量可以以原子的方式阻塞进程,直到某个特定条件为真为止。对条件的测试是在互斥锁的保护下进行的。条件变量始终与互斥锁一起使用。

2.信号量机制(Semaphore):包括无名线程信号量和命名线程信号量

3.信号机制(Signal):类似进程间的信号处理

线程间的通信目的主要是用于线程同步,所以线程没有像进程通信中的用于数据交换的通信机制。

 

*******************************************************************************

 

1)无名管道

管道是一个进程连接数据流到另一个进程的通道,它通常是用作把一个进程的输出通过管道连接到另一个进程的输入。

举个例子,在shell中输入命令:ls -l | grep string,我们知道ls命令(其实也是一个进程)会把当前目录中的文件都列出来,但是它不会直接输出,而是把本来要输出到屏幕上的数据通过管道输出到grep这个进程中,作为grep这个进程的输入,然后这个进程对输入的信息进行筛选,把存在string的信息的字符串(以行为单位)打印在屏幕上。

 

2)有名管道

命名管道也被称为FIFO文件,它是一种特殊类型的文件,它在文件系统中以文件名的形式存在,但是它的行为却和之前所讲的没有名字的管道(匿名管道)类似。

由于Linux中所有的事物都可被视为文件,所以对命名管道的使用也就变得与文件操作非常的统一,也使它的使用非常方便,同时我们也可以像平常的文件名一样在命令中使用。

创建命名管道:mkfifo

#include <sys/types.h>  

#include <sys/stat.h>  

int mkfifo(const char *filename, mode_t mode); 

注意是创建一个真实存在于文件系统中的文件,filename指定了文件名,而mode则指定了文件的读写权限

访问有名管道: open

注意:1、就是程序不能以O_RDWR模式打开FIFO文件进行读写操作,而其行为也未明确定义,因为如一个管道以读/写方式打开,进程就会读回自己的输出,同时我们通常使用FIFO只是为了单向的数据传递。2、就是传递给open调用的是FIFO的路径名,而不是正常的文件。

打开FIFO文件通常有四种方式:

open(const char *path, O_RDONLY); 

open(const char *path, O_RDONLY | O_NONBLOCK);

open(const char *path, O_WRONLY);

open(const char *path, O_WRONLY | O_NONBLOCK); 

open调用的阻塞是什么一回事呢?很简单,对于以只读方式(O_RDONLY)打开的FIFO文件,如果open调用是阻塞的(即第二个参数为O_RDONLY),除非有一个进程以写方式打开同一个FIFO,否则它不会返回;如果open调用是非阻塞的的(即第二个参数为O_RDONLY | O_NONBLOCK),则即使没有其他进程以写方式打开同一个FIFO文件,open调用将成功并立即返回。

 

3)消息队列

KEY值的创建

key_t ftok(const char *pathname, int proj_id);

功能:生成一个KEY值

参数:pathname 路径名

proj_id 任意值

返回值: 成功 key 值

失败 -1

创建和访问一个消息队列:int msgget(key_t, key, int msgflg)。

程序必须提供一个key来命名某个特定的消息队列。msgflg是一个权限标志,表示消息队列的访问权限,它与文件的访问权限一样。msgflg可以与IPC_CREAT做或操作,表示当key所命名的消息队列不存在时创建一个消息队列,如果key所命名的消息队列存在时,IPC_CREAT标志会被忽略,而只返回一个标识符。

它返回一个以key命名的消息队列的标识符(非零整数),失败时返回-1

 

把消息添加到消息队列中:

int msgsend(int msgid,const void *msg_ptr, size_t msg_sz, int msgflg) 

msgid是由msgget函数返回的消息队列标识符。

msg_ptr是一个指向准备发送消息的指针,但是消息的数据结构却有一定的要求,指针 msg_ptr所指向的消息结构一定要是以一个长整型成员变量开始的结构体,接收函数将用这个成员来确定消息的类型。所以消息结构要定义成这样:

struct msgbuf {

long mtype; 消息类型

char mtext[1]; 消息正文

};

msg_sz是消息正文的长度。

msgflg用于控制当前消息队列满或队列消息到达系统范围的限制时将要发生的事情(

0 阻塞发送)。

如果调用成功,消息数据的一分副本将被放到消息队列中,并返回0,失败时返回-1.

从消息队列中读取消息:

int msgrcv(int msgid, void *msg_ptr, size_t msg_st, long int msgtype, int msgflg);  

msgid, msg_ptr, msg_st的作用也函数msgsnd函数的一样。

msgtype可以实现一种简单的接收优先级。如果msgtype为0,就获取队列中的第一个消息。如果它的值大于零,将获取具有相同消息类型的第一个信息。如果它小于零,就获取类型等于或小于msgtype的绝对值的第一个消息。

msgflg用于控制当队列中没有相应类型的消息可以接收时将发生的事情(0 阻塞接收)。

调用成功时,该函数返回放到接收缓存区中的字节数,消息被复制到由msg_ptr指向的用户分配的缓存区中,然后删除消息队列中的对应消息。失败时返回-1.

 

消息队列控制函数:

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

参数: msqid 消息队列标识符

cmd IPC_STAT 将消息的属性信息复制给第三个参数

IPC_SET 设置消息的属性信息给第三个参数

IPC_RMID 删除一个消息队列,第三个参数为NULL

返回值:成功 0

失败 -1

 

与命名管道相比,消息队列的优势在于,1、消息队列也可以独立于发送和接收进程而存在,从而消除了在同步命名管道的打开和关闭时可能产生的困难。2、同时通过发送消息还可以避免命名管道的同步和阻塞问题,不需要由进程自己来提供同步方法。3、接收程序可以通过消息类型有选择地接收数据,而不是像命名管道中那样,只能默认地接收。

 

4)信号量sem(有时也被称为信号灯)

作用:信号量的使用主要是用来保护共享资源,使得资源在一个时刻只有一个进程(线 程)所拥有。

信号量的值为正的时候,说明它空闲。所测试的线程可以锁定而使用它。若为0,说明

它被占用,测试的线程要进入睡眠队列中,等待被唤醒。

信号量与互斥锁之间的区别:

1. 互斥量用于线程的互斥,信号线用于线程的同步。  

这是互斥量和信号量的根本区别,也就是互斥和同步之间的区别。  

 

互斥:是指某一资源同时只允许一个访问者对其进行访问,具有唯一性和排它性。但互斥无法限制访问者对资源的访问顺序,即访问是无序的。  

同步:是指在互斥的基础上(大多数情况),通过其它机制实现访问者对资源的有序访问。在大多数情况下,同步已经实现了互斥,特别是所有写入资源的情况必定是互斥的。少数情况是指可以允许多个访问者同时访问资源  

 

2. 互斥量的加锁和解锁必须由同一线程分别对应使用,信号量可以由一个线程释放,另一个线程得到。

3. 互斥量值只能为0/1,信号量值可以为非负整数。  

 

信号量可以被抽象为五个操作:

1 初始化 sem_init 2 申请资源(P操作)sem_wait

3 释放资源(V操作)sem_post 4 摧毁信号量sem_destroy

5 获取当前信号量的值sem_getvalue

 

P操作:线程等待信号量,如果值大于0,则获得,值减一;如果只等于0,则一直线程进入睡眠状态,知道信号量值大于0或者超时。

V操作:执行释放信号量,则值加一;如果此时有正在等待的线程,则唤醒该线程

附加函数:sem _trywait 如果调用TryWait,线程并不真正的去获得信号量,还是检查信号量是否能够被获得,如果信号量值大于0,则TryWait返回成功;否则返回失败。  

 

5 互斥锁(mutex)

Mutex对象的值,只有0和1两个值。这两个值也分别代表了Mutex的两种状态。值为0, 表示锁定状态,当前对象被锁定,用户进程/线程如果试图Lock临界资源,则进入排队等待;值为1,表示空闲状态,当前对象为空闲,用户进程/线程可以Lock临界资源,之后Mutex值减1变为0

 

互斥锁操作:

1 初始化 pthread_mutex_init 2 上锁 pthread_mutex_lock

3 释放锁 pthread_mutex_unlock 3 摧毁锁 pthread_mutex_destroy

 

Mutex被创建时可以有初始值,表示Mutex被创建后,是锁定状态还是空闲状态。在同一个线程中,为了防止死锁,系统不允许连续两次对Mutex加锁(系统一般会在第二次调用立刻返回)。也就是说,加锁和解锁这两个对应的操作,需要在同一个线程中完成。  

 

6 共享内存(shm) :

共享内存允许两个不相关的进程访问同一个逻辑内存。

共享内存为在多个进程之间共享和传递数据提供了一种有效的方式。但是它并未提供同步机制,所以我们通常需要用其他的机制来同步对共享内存的访问。我们通常是用共享内存来提供对大块内存区域的有效访问,同时通过传递小消息来同步对该内存的访问。

在第一个进程结束对共享内存的写操作之前,并无自动的机制可以阻止第二个进程开始对它进行读取。对共享内存访问的同步控制必须由程序员来负责。

对共享内存的操作函数:

1 创建或者打开共享内存 shmget 2 映射共享内存 shmat

3 撤销共享内存 shmdt 4 删除共享内存 shmctl

 

映射共享内存详解 void *shmat(int shmid, const void *shmaddr, int shmflg)

参数:shmid shmget的返回值

shmaddr NULL 默认自动寻找

hmflg 0 可读可写

返回值:成功 共享内存区域的地址

失败 -1

7)套接字(socket):

源IP地址和目的IP地址以及源端口号和目的端口号的组合称为套接字。其用于标识客户端请求的服务器和服务

套接字,是支持TCP/IP网络通信的基本操作单元,可以看做是不同主机之间的进程进行双向通信的端点,简单的说就是通信的两方的一种约定,用套接字中的相关函数来完成通信过程。

非常非常简单的举例说明下:Socket=Ip address+ TCP/UDP + port。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zxz520zmg

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值