2011-10-24 10:08:52 lqf785435771 阅读数 1292

linux进程间同步的机制/方式和进程间通信的方法

Linux进程同步的方式:信号量、阻塞、异步IO、事件、变量锁

 Linux下进程通信的七种方法:管道(pipe),命名管道(FIFO),内存映射(mapped memeory),消息队列(message queue),共享内存(shared memory),信号(signal),套接字(Socket).
(1) 管道(pipe):管道允许一个进程和另一个与它有共同祖先的进程之间进行通信;
(2) 命名管道(FIFO):类似于管道,但是它可以用于任何两个进程之间的通信,命名管道在文件系统中有对应的文件名。命名管道通过命令mkfifo或系统调用mkfifo来创建;
(3) 信号(signal):信号是比较复杂的通信方式,用于通知接收进程有某种事情发生,除了用于进程间通信外,进程还可以发送信号给进程本身;Linux除了支持UNIX早期信号语义函数signal外,还支持语义符合POSIX.1标准的信号函数sigaction(实际上,该函数是基于BSD的,BSD即能实现可靠信号机制,又能够统一对外接口,用sigaction函数重新实现了signal函数的功能);
(4) 内存映射(mapped memory):内存映射允许任何多个进程间通信,每一个使用该机制的进程通过把一个共享的文件映射到自己的进程地址空间来实现它;
(5) 消息队列(message queue):消息队列是消息的连接表,包括POSIX消息对和System V消息队列。有足够权限的进程可以向队列中添加消息,被赋予读权限的进程则可以读走队列中的消息。消息队列克服了信号承载信息量少,管道只能成该无格式字节流以及缓冲区大小受限等缺点;
(6) 共享内存 (shared memory):它使得多个进程可以访问同一块内存空间,是最快的可用IPC形式。这是针对其他通信机制运行效率较低而设计的。它往往与其他通信机制,如信号量结合使用,以达到进程间的同步及互斥;
(7) 套接字(Socket):它是更为通用的进程间通信机制,可用于不同机器之间的进程间通信。起初是由UNIX系统的BSD分支开发出来的,但现在一般可以移植到其他类UNIX系统上:Linux和System V的变种都支持套接字;

 

 

2016-05-23 16:34:05 xixihaha331 阅读数 1714

    有时看网上的博客自己会纳闷,进程间的同步机制和线程间的同步机制分别是什么?

    其实进程之间是不用同步机制的,因为进程之间资源不共享,不需要同步机制来对所谓的临界资源进行保护,所以通常我们只讨论进程间的通信机制,有时候进程间的通信也称为进程间的同步,有管道,有名管道,信号量,消息队列,共享内存,socket, poll, epoll等,其实还有文件I/O,只是效率太慢不被采用,但是也能够实现进程之间的通信。

    由于一个进程内的所有的线程之间资源共享,所以多个线程可以同时对某个数据资源进行操作。所以为了保护临界资源只能被一个线程使用,因此采用线程间的同步机制。有:互斥量,条件变量,信号量,读写锁,自旋锁,屏障等机制。

    下面是自己对条件变量的一点总结

     利用信号量对临界资源进行加锁,解锁保护,可以保证某一时刻只有一个线程拿到锁对临界区进行操作,但是通常情况下会将互斥量和条件变量结合使用,这样可以解决一些不必要的麻烦。例如:

    (1)由于线程之间是异步进行的,所以每个线程得到锁的概率依线程的优先级而决定。所以说哪个线程下次能得到锁是不一定的,那么如果想让特定的线程去对资源进行操作可以利用条件变量,让系统立即调度拥有特定条件变量的线程,从而避免了多线程竞争。

    (2)如果一个线程得到锁,那么其他的线程得不到锁而处于忙等状态,等到这个线程释放锁,但是这个线程又再次得到锁,释放锁,就这样不停的加锁,解锁,从而形成死锁。但是条件变量可以有效的避免这种死锁的情况。

    (3)由于一个线程得到锁,其他的线程则处于忙等状态,不停的去查询锁有没有被释放,这样浪费ie大量的cpu资源,利用条件变量则使得不到锁的线程处于阻塞状态,等到有锁的时候去通知线程,这样效率高。

    条件变量最重要的一个函数的解析:

    pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex)

这个函数会先阻塞自身线程,然后解锁,直到别的线程拿到锁对临界资源进行操作,等到某一条件用pthread_cond_signal()唤醒对应的条件变量。则被阻塞的线程继续从阻塞的那块进行加锁,并且完成后面的代码。

例子:

    线程一负责打印奇数,线程二负责打印偶数,数值value是临界资源,只能允许某个时刻只能有一个线程对其进行操作。


#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <iostream>

using namespace std;
#define MAXNUM (10)

pthread_t tid[2];
pthread_mutex_t mutex;
pthread_cond_t ji, ou;
int value = 1;

void *pthread_ji(void *arg)
{
    
    int result;
    result = pthread_mutex_lock(&mutex);
    if(result != 0){
        return (void *)result;
    }
    while(value < MAXNUM){
        if(value % 2 == 1){
            printf("argc[%d]:%d\n", value, value);
            sleep(1);
            value++;
            pthread_cond_signal(&ou);
        }else{
            pthread_cond_wait(&ji, &mutex);     //当value值不为奇数时,走到这个分支,线程一阻塞,并且释放锁,直到
        }                                                           //第二个线程拿到锁并且对value进行操作,value符和条件为奇数时,此时
    }                                                               //用pthread_cond_signal(&ji)通知线程一,线程一再次拿到锁继续从阻塞的

                                                                    //的代码执行。

   pthread_mutex_unlock(&mutex);
}

void *pthread_ou(void *arg)
{
    int result;
    result = pthread_mutex_lock(&mutex);
    if(result != 0){
       return (void *)result;
    }
    while(value < MAXNUM){
        if(value % 2 == 0){
            printf("argc[%d]:%d\n", value, value);
            sleep(1);
            value++;
            pthread_cond_signal(&ji);
        }else{
            pthread_cond_wait(&ou, &mutex);
        }
    }
    pthread_mutex_unlock(&mutex);
}

int main(int argc, char *argv[])
{
    pthread_mutex_init(&mutex, NULL);
    pthread_cond_init(&ji, NULL);
    pthread_cond_init(&ou, NULL);
    pthread_create(&tid[0], NULL, pthread_ji, NULL);
    sleep(1);
    pthread_create(&tid[1], NULL, pthread_ou, NULL);

    pthread_join(tid[0], NULL);
    pthread_join(tid[1], NULL);
    pthread_mutex_destroy(&mutex);

}

    注意这里有个问题,线程可能在阻塞于pthread_cond_wait调用期间被取消,则由于没有解锁而造成死锁的情况,为了解决这个问题我们可以利用线程取消处理机制。

      int pthread_cancel(pthread_t tid)

比如,多个线程同时完成一个任务,但是有个线程最早完成,则最早完成的这个线程可以用线程取消函数。

这个函数只有在两种情况下被调用,其他情况是不会被调用的:

    (1)线程被别的线程取消。

    (2)线程自己终止调用pthread_exit()。

   


 

2018-05-14 09:21:31 Fly_as_tadpole 阅读数 150

说出你所知道的各类linux系统的各类同步机制(重点),什么是死锁?如何避免死锁

  linux系统的通讯机制,主要是指进程间通讯,其实通讯就是进程同步的手段。如果问进程间同步,见问题7,这里要说的linux系统的同步机制是讲线程间的同步。 
  简单总结一下。更多资料参考《unix环境高级编程》、《操作系统》。 
  互斥量 
  首先是最基础的加锁原语,互斥量。既确保同一时间只有一个线程访问数据,通过在访问共享资源前对互斥量加锁,阻塞其他试图再次加锁的线程知道互斥锁被释放。互斥的具体实现有多种方法,例如开关中断,使用原子的机器指令。 
  读写锁 
  与互斥量类似,不过允许更高的并行性。读写锁有三种状态,读模式的加锁,写模式的加锁,不加锁状态。一次只有一个线程可以占有写模式的读写锁,但是可以多个线程可以同时占用读模式的读写锁。既读模式下可以共享,写模式下互斥。一般一个线程试图以读模式获取锁时,读写锁通常会阻塞随后的读模式锁请求。 
  条件变量 
  互斥量是加锁原语,条件变量属于等待原语,用于等待某个条件成立后唤醒阻塞的线程。条件变量与互斥量一起使用,条件本身由互斥量保护。Java Object内置了条件变量wait(),notify(),notifyAll()。    
  pthread_cond_wait(),pthread_cond_signal(),pthread_cond_broadcast(Unix),从函数的命名就可以看出其大致作用。 
  根据陈硕的总结,条件变量的正确使用方式: 
  对于wait端: 
  1.必须与mutex一起使用。 
  2.在mutex已上锁时才能调用wait()。 
  3.把判断布尔条件和wait()放到while循环中。 
  第三个条件主要是为了防止spurious wakeup,既虚假唤醒。因为pthread_conf_wait能被除了pthread_cond_signal(),pthread_cond_broadcast外的其他信号唤醒。需要再wait后再次检查,同时也是为了避免错过一次条件变量后永远的等待下去。 
  对于signal端: 
  1.一定不要在mutex已经上锁的情况下调用signal。 
  2.在signal之前一般要修改布尔表达式。 
  3.修改布尔表达式通常用mutex保护。 
  4.注意区分signal和broadcast:“broadcast通常用于表明状态变化,signal通常用于表示资源可用”。 
  自旋锁 
  自旋锁与互斥量类似,但它不是通过休眠使进程阻塞,二是在获取锁之前一直处于忙等。既一直占用CPU资源直到锁被释放。 
  屏障 
  屏障主要用于多个线程之间的并行工作的协调。屏障允许每个线程等待,直到所有的合作线程都达到某个点,然后从该点继续执行。 
  信号量 
  这个在《unix环境高级编程》中没有提及,在《操作系统》中有论述。 
  信号量可作用与进程间合作,以及多线程的同步。 
  一个进程可以被迫在某一个位置停止,直到接收到某一个信号。为了发信号,需要使用一个称为信号量的特殊变量,可以看做一个具有整数值得变量。其中只允许信号量取0和1的称为二元信号量。非二元信号量常称为计数信号量或一般信号量。 
  一般在信号量上定义三个操作: 
  1.一个信号量可以初始化成非负数。 
  2.semWait操作使信号量减1。如果值变为负数,则执行semWait的进程或线程被阻塞,否则继续执行。 
  3.semSignal操作使信号量加1。如果值<=0,则被semWait阻塞的进程被解除阻塞。 
  信号量需要队列保存阻塞在信号量上等待的进程。至于进程按什么顺序移除,最公平的是先进先出,采用此策略的为强信号量。没有规定顺序的为弱信号量。 
  互斥量和二元信号量的主要区别在于互斥量的加锁和解锁必须由同一线程分别对应使用,信号量可以由一个线程释放,另一个线程得到。至于用于互斥和用于同步的说法,十分牵强。 
  陈硕关于信号量的建议是不用。 
  因为可用条件变量加互斥量完全代替,另外还需要担心计数值需要和自己的数据长度常常保持一致的问题。 
  死锁 
  死锁大概已经被讲烂了,我也不想再搬运了。坚持使用Scoped Locking,死锁的时候好检测。 

  

   补充一个坑爹坑爹坑爹坑爹的问题:系统如何将一个信号通知到进程?

     前面讲过信号了,为什么又问一遍?这个问题有什么特殊的地方么? 
     内核给进程发送信号,是在进程所在的进程表项的信号域设置对应的信号的位。

        更多:http://blog.csdn.net/joejames/article/details/37960873 

2016-08-17 09:31:14 new_life_sjtu 阅读数 413

该文章学习翻译Boost Interprocess 模块中synchronization_mechanisms_overview 一节,欢迎交流指正,谢谢。

命名Named和匿名Anonymous同步机制

Boost interprocess进程间同步提供了两种同步对象:

命名工具: 两个进程试图创建命名工具时,必须使用相同的名字创建/打开该对象。这类似于一个进程使用fstream 创建一个命名文件,另一个进程需要使用fstream 以相同的名称打开该文件,每个进程使用不同的对象访问公共资源, 但两者使用了相同的基础资源。

匿名工具:由于这类工具没有名称,两个进程必须使用通过共享内存区域(shared memory)或者内存映射文件(memory mapped files)使用同一个工具对象,这类似于传统的线程同步方式,两个进程使用同一个对象,但与同一进程的不同线程共享全局变量和堆内存的同步方式并不相同,来自两个进程的不同线程只能通过将共享工具对象映射至内存区域使用。

每种方式都有各自的优势和劣势:
命名工具: 简单、易于实现,因为两个进程并不需要创建共享的内存区域来构造该同步机制。

匿名工具: 在使用内存映射对象时能够被序列化值磁盘, 从而自动获得持久性的同步工具,用户可以创建一个同步工具至内存映射文件,重启系统后再次映射至该文件, 仍然可以使用该同步工具而不引起任何问题,这是命名工具无法实现的。

命名工具和匿名工具的主要接口差异在于其构造函数,通常匿名工具只有一种构造方法,而命名工具有多种构造方法,且构造时通过第一个特殊参数表示创建、打开、创建或打开共同的基础资源。

using namespace boost::interprocess;

//Create the synchronization utility. If it previously exists, throws an error
NamedUtility(create_only, ...)

//Open the synchronization utility. If it does not previously exist, it's created.
NamedUtility(open_or_create, ...)

//Open the synchronization utility. If it does not previously exist, throws an error.
NamedUtility(open_only, ...)
using namespace boost::interprocess;

//Create the synchronization utility.
AnonymousUtility(...)

Boost interprocess 提供了一下几种同步工具:(系列文章中逐渐学习翻译)

  • 互斥量Mutexes (named and anonymous)
  • 条件变量Condition variables (named and anonymous)
  • 信号量Semaphores (named and anonymous)
  • 升级互斥量Upgradable mutexes
  • 文件锁File locks
2012-11-17 21:03:55 ssdsafsdsd 阅读数 1754

 linux进程间通信简介

      进程是一个独立的资源分配单位,不同进程之间的资源是相互独立的,没有关联,不能在一个进程中直接访问另一个进程中的资源。但是,进程不是孤立的,不同的进程之间需要信息的交换以及状态的传递,因此需要进程间数据传递、同步与异步的机制。

      这些机制并不是由哪些进程来进行直接管理,而是由操作系统进行管理。linux中提供了大量的进程间通信机制来实现同一个主机间的进程间的通信。同时,对于网络间的主机进行通信的机制,linux也同时提供。看下面的图

一、总体简介

【1】同主机进程间数据交互机制:无名管道(PIPE),有名管道(FIFO),消息队列(message queue)和共享内存(share memory)。

【2】同主机进程间同步机制:信号量(semaphore)

【3】同主机进程间异步机制:信号(signal)

【4】网络主机间数据交互机制:套接字(socket)

二、unix进程间通信机制

1、无名管道PIPE,只能用于具有亲缘关系(父子进程)间的通信,无名管道在通信双方退出后自动消失。

2、有名管道:克服了无名管道的瞬时性的问题,采用管道文件来实现同一主机间的任意两个进程间的数据的传递。

3、信号:信号其实是一种软中断机制,采用一种异步的机制,在信号发生时处理特定的时间,但是要注意的是,这同硬件中的中断,还是有一定区别的。

三、system Ⅴ进程间通信

1、消息队列:消息队列主要用来实现两个进程间少量的数据的通信,并且接收方可以根据消息的类型,选择接收消息。http://blog.csdn.net/ssdsafsdsd/article/details/8192143

 

2、信号量:信号量是一种进程间的同步进制。对于二元信号量,可以实现两个进程间的同步,对于广泛的信号量,可以表示资源的使用量。

http://blog.csdn.net/ssdsafsdsd/article/details/8204997

3、共享内存:共享内存进制可以实现两个进程间的大数据量的通信,其在内存中专门开辟出一个独立的内存,然后映射到各自的进程之中,进行数据的传输,其通信效率较高。

http://blog.csdn.net/ssdsafsdsd/article/details/8205016

     对于system Ⅴ进程间的通信机制,可以使用ipcs命令进程查看,如下

四、不同主机之间的网络通信

      这部分主要是网络编程的知识,也就是linux网络socket编程,在这里不做重点的介绍。但是同样给出关于socket编程的本博客中出现的代码的几个链接。

      1、使用AF_UNIX实现本机数据流通信示例(TCP协议)

       2、使用AF_INET实现UDP点对点通信示例(UDP协议)

五、对于各种通信机制的实例

      在本blog中,没有对各种通信机制进行详细的讲解,读者想学习可以去找本书,基本讲linux编程的书籍都会讲这个,但是对于每种的通信机制我几乎都写了文章,主要以实例的形式进行出现。在看完基础内容后,把这个例子看懂,会有一定收获。对于已经整理好的内容,我会把链接写在上面的分类后面,对于还没时间整理或者写的部分,没有给出链接,可以在我的文章里面找下。整理好后,我会把链接加上去。例如,对于进程间信号量的学习,我给出了一个使用信号量的例子来实现生产者,消费者问题。这些例子有的来自我所看的书中,也有些我我修改后的,或者自己写的。

 

OVER!!!!!!!!!!

Linux进程同步机制

阅读数 951

浅谈linux下多进程编程及其同步机制

博文 来自: smilestone322

linux进程间通信

阅读数 1193

没有更多推荐了,返回首页