linux进程间通信如何加锁

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了linux进程间通信如何加锁相关的知识,希望对你有一定的参考价值。

参考技术A

进程间通信有一种[共享内存]方式,大家有没有想过,这种通信方式中如何解决数据竞争问题?我们可能自然而然的就会想到用锁。但我们平时使用的锁都是用于解决线程间数据竞争问题,貌似没有看到过它用在进程中,那怎么办?

关于进程间的通信方式估计大多数人都知道,这也是常见的面试八股文之一。

个人认为这种面试题没什么意义,无非就是答几个关键词而已,更深入的可能面试官和面试者都不太了解。

关于进程间通信方式我之前在【这篇文章】中有过介绍,感兴趣的可以移步去看哈。

进程间通信有一种[共享内存]方式,大家有没有想过,这种通信方式中如何解决数据竞争问题?

我们可能自然而然的就会想到用锁。但我们平时使用的锁都是用于解决线程间数据竞争问题,貌似没有看到过它用在进程中,那怎么办?

我找到了两种方法,信号量和互斥锁。

直接给大家贴代码吧,首先是信号量方式:

代码中的MEOW_DEFER,它内部的函数会在生命周期结束后触发。它的核心函数其实就是下面这四个:

具体含义大家应该看名字就知道,这里的重点就是sem_init中的pshared参数,该参数为1表示可在进程间共享,为0表示只在进程内部共享。

第二种方式是使用锁,即pthread_mutex_t,可是pthread_mutex不是用作线程间数据竞争的吗,怎么能用在进程间呢?

可以给它配置一个属性,示例代码如下:

它的默认属性是进程内私有,但是如果给它配置成PTHREAD_PROCESS_SHARED,它就可以用在进程间通信中。

相关视频推荐

360度无死角讲解进程管理,调度器的5种实现

Linux进程间通信-信号量、消息队列和共享内存

学习地址:C/C++Linux服务器开发/后台架构师【零声教育】-学习视频教程-腾讯课堂

需要C/C++ Linux服务器架构师学习资料加qun812855908获取(资料包括 C/C++,Linux,golang技术,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK,ffmpeg 等),免费分享

完整代码如下:

我想这两种方式应该可以满足我们日常开发过程中的大多数需求。

锁的方式介绍完之后,可能很多朋友自然就会想到原子变量,这块我也搜索了一下。但是也不太确定C++标准中的atomic是否在进程间通信中有作用,不过看样子boost中的atomic是可以用在进程间通信中的。

其实在研究这个问题的过程中,还找到了一些很多解决办法,包括:

Disabling Interrupts

Lock Variables

Strict Alternation

Peterson\'s Solution

The TSL Instruction

Sleep and Wakeup

Semaphores

Mutexes

Monitors

Message Passing

Barriers

这里就不过多介绍啦,大家感兴趣的可以自行查阅资料哈。

概述Linux进程间通信方式

  在现代计算机上,一个任务的完成,往往需要多个进程协调,这时进程间如何交流就成了必须解决的问题。实现进程间通信(IPC)有很多方法,下面简单介绍一下各个通讯方式的原理,不讲具体代码实现。

管道

管道一般指无名管道(还有另一种叫有名管道),是Unix系统最古老的进程通信方式。管道通信有以下特点:

  • 管道是半双工的,单向的,同一时刻只能允许一方向另一方发送;

  • 管道只能在有父子进程,或者兄弟进程之间使用;

创建管道的函数原型:int pipe(int fd[2]);

管道利用两个标识符进行读写控制,fd[0]用于控制读,fd[1]用于控制写。如下图,管道创建后所有标识符都打开状态,如要实现A向B传递数据,则需要关闭A的fd[0],关闭B的fd[1]。

值得一提的是,管道概念在linux的shell中也用到,管道符\'|\',也是利用了管道的抽象,进行输出重定向,将一条命令的输出传递给另一条命令作为其输入。

有名管道FIFO

和上面的无名管道类似,不过有名管道可以在无关的进程之间交换信息。

有名管道的创建函数原型如下:int mkfifo(const char *_path,mode_t mode);管道文件的path就是它的名字,Linux上一切都是文件,有名管道也不例外,它是设备文件。

利用有名管道进行通信的步骤大概是这样:

创建一个有名管道文件 -> 进程A用文件write()操作发送消息到管道 -> 进程B用文件read()操作从管道读取消息。

信号

像我们常用ctrl+c来结束当前进程,就是通过shell发送了一个SIGINT信号。linux一共提供了64个信号,它们的编号从1到64。使用$man 7 signal命令可以查看每种信号的名称、编号、作用。

发送端如何发送信号

  • 硬件产生信号,如我们键盘组合命令可以产生信号,硬件的故障也可能产生信号;

  • 软件发送信号,最常用的信号发送函数为kill,指定信号类别和目标进程id就可以发送信号。除了kill,还有sigqueue、raise等也能产生信号。

接收端如何处理信号

  • 忽略信号,不对信号做任何处理;

  • 执行默认处理程序,每个信号都有自己默认的处理函数;

  • 执行指定的处理函数,我们可以将信号和自定义的处理函数进行绑定(也可以说是注册一种信号处理函数),这样接收端会优先执行自定义的信号处理函数。

如何自定义处理函数

  • signal(int signum,sighandler_t handler);自定义一个handler,绑定到signum信号上。

  • sigaction(int signum,struct sigaction * act,struct sigaction * oldact);也可以用来注册。

消息队列

和分布式应用的消息队列类似,消息队列是异步通信的一种机制。和数据结构学到的队列一样,我们可以创建一个消息队列,发送端发送消息入队,读取端按需从队列读取信息。这样的通讯是异步的,不会造成进程阻塞。

简单归纳下消息队列和管道通信的区别:

  • 消息队列是异步通信,管道是同步通信,会使进程阻塞;

  • 消息队列可以在不相干进程间通信,匿名管道只能在近亲进程间通信;

  • 消息队列存放在内存中,而有名管道存放在磁盘上,因此消息队列存取速度快;匿名管道在内核缓冲区理论最快,不过使用限制很多;

  • 因为消息队列存在内存里,因此是以块式存取,而管道都是流式存取。

信号量

在PV操作和锁中已经学到过,信号量是用于并发时资源互斥和同步的一种机制。信号量本身不能传递数据,通常搭配共享内存一同使用。

顾名思义,信号量指的就是信号(资源)的数量。

假如资源数是1,那么信号量初始值s=1;当没有进程访问资源时,即资源空闲时,s=1;这时有一个进程申请访问,执行一次P操作,信号量减一,s=0;紧接着又来了一个进程想用资源,因为s<=0,资源非空闲,它就要等;等到上一个进程用完了,退出时会执行V操作,将信号量加一,这时等待使用的进程发现,s=1了,他就可以进入使用了。

共享内存

顾名思义,即进程之间共享一段内存区域。共享内存理论上是最快的IPC方法,因为其直接对内存的数据进行操作。多个进程不能同时读写一个数据,需要进行同步,而共享内存的系统调用中并未实现同步,需要我们借助信号量机制实现同步。因此共享内存常常和信号量结合使用。

Socket套接字

Socket是否还有印象?在学习计算机网络时我们经常遇见它,它是一种在不同主机的进程间进行网络通信的机制。除了这种网络套接字外,还有一种叫IPC套接字,特指用于同一主机进程间通信的方式。在这几种IPC之中,套接字通信是功能最强大的,也是最为复杂的一种。

 

本文仅仅是对进程间通信方式的总结概述,并未详尽。文中如有错误之处,望网友指正。

以上是关于linux进程间通信如何加锁的主要内容,如果未能解决你的问题,请参考以下文章

31互斥锁与进程间通信

linux系统进程间通信方式:管道

[ Linux ] 进程间通信介绍 管道

互斥锁与进程间通信

Linux系统编程--进程间通信 ---管道篇

Linux进程间通信