zoukankan      html  css  js  c++  java
  • linux进程通信全面解析

     

    进程IPC 的 7种方式 

    linux下 进程通讯IPC的方式主要有以下7种:
    1.文件
    2.共享内存
    3.信号
    4.管道
    5.套接字
    6.消息列队
    7.信号量
     
    以下正文 中 一一 分析下:

    1.文件 ,记得 加文件锁 lockf.使用少,略去

    2.共享内存

        由于内存的保护机制的作用,进程不会简单地将自己的内存空间暴露给其他进程,并允许这些进程 进行读写,根据linux下的内存保护机制,进程中的一个指针指向的内存地址 是 一个虚拟地址,所以不会涉及到真实的物理地址,传递这个地址 到其他进程并不能达到 我们期望的结果。总之,一个虚拟地址,仅仅在创建它的那个进程中有意义。
        任何要被共享的内存都必须经过 显示的分配,而不是简单的 在堆上分配,
     

    POSIX 共享内存的api:

    函数     作用    
    shm_open     创建一个新的共享区域或者附加在已有的共享区域上,区域被其名字标识,函数返回各文件的描述符,类似于open系统调用    
    shm_unlink     对 shm_open 返回的文件描述符 共享区域进行释放,类似于使用 unlink系统调用 对文件进行操作,知道所有的进程都不再饮用该内存后才对其进行释放。    
    mmap     用于将一个文件 映射到 某一个内存区中,其中 也使用来shm_open返回的文件描述符,函数的返回值 是指向文件映射到内存地址的指针。mmap同样可以 使用纯文本文件 和一些 驱动程序的文件描述符
    munmap     用于释放mmap所映射的内存区域
    msync 同步存取一个映射区域 并将高速缓存的数据 回写到物理内存中,以便其他进程可以舰艇这些改变
    例子 程序:
    #include<stdio.h>
    #include<string.h>
    #include<stdlib.h>
    #include<unistd.h>
    #include<sys/file.h>
    #include<sys/mman.h>
    #include<sys/wait.h>
    void error_out(constchar* msg)
    {
    perror(msg);
    exit(EXIT_FAILURE);
    }
    int main(int argc,char*argv[])
    {
    int r;
    constchar*memname ="/mymen";
    constsize_t region_size = sysconf(_SC_PAGE_SIZE);
    //create the share memory region
    int fd = shm_open(memname,O_CREAT | O_TRUNC | O_RDWR,0666);
    if(fd ==-1)
    error_out("shm_open");
    //allocate some memory in the region ,
    r = ftruncate(fd,region_size);
    if(r !=0)
    error_out("ftruncate");
    //map the region into the memory
    void*ptr = mmap(0,region_size,PROT_READ | PROT_WRITE ,MAP_SHARED,fd,0);
    if(ptr == MAP_FAILED)
    error_out("mmap");
    //useness after mmap called
    close(fd);
    pid_t pid = fork();
    //child
    if(pid ==0)
    {
    u_long *d =(u_long*)ptr;
    *d =0xfdefdddd;
    exit(0);
    }
    //parent
    else{
    int status;
    waitpid(pid,&status,0);
    printf("child wrote %#1x
    ",*(u_long*)ptr);
    }
    //release memory
    //umap
    r = munmap(ptr,region_size);
    if(r !=0)
    error_out("munmap");
    r = shm_unlink(memname);
    if(r !=0)
    error_out("shm_ulink");
    return0;
    }
    gcc -o posix_shm posix_shm.c -lrt
    -lrt 不可缺少
     

    System V 共享内存的api

     
    函数     作用    
    shmget     创建一个新的共享区域或者已有的共享区域上(同shm_open    +一个内存申请函数)
    shmat     用于将一个文件 映射到内存区域中(同mmap)
    shmdt     用于释放所映射的内存区域(同munmap
        )
    shmct 对用多个用户,断开其对共享区域的连接(同shm_unlink)
    例子 ,同上 ,功能完全一样:
    说明:shmget函数 创建并分配了共享内存 区域,所以不需要 截断ftruncate 文件描述符的 步骤
    总体而言,system V的api更得我心啊!
    #include<stdio.h>
    #include<string.h>
    #include<stdlib.h>
    #include<unistd.h>
    #include <sys/ipc.h>
    #include<sys/shm.h>
    #include<sys/wait.h>
    void error_out(constchar* msg)
    {
    perror(msg);
    exit(EXIT_FAILURE);
    }
    int main(int argc,char*argv[])
    {
    int r;
    key_t mykey =12345678;
    constsize_t region_size = sysconf(_SC_PAGE_SIZE);
    //create the shared memory region
    int shmid = shmget(mykey,region_size,IPC_CREAT |0666);
    if(shmid ==-1)
    error_out("shmget");
    //map the region into memory
    void*ptr = shmat(shmid,NULL,0);
    if(ptr ==(void*)-1)
    error_out("shmat");
    //useness after mmap called
    pid_t pid = fork();
    //child
    if(pid ==0)
    {
    u_long *d =(u_long*)ptr;
    *d =0xfdefdddd;
    exit(0);
    }
    //parent
    else{
    int status;
    waitpid(pid,&status,0);
    printf("child wrote %#1x
    ",*(u_long*)ptr);
    }
    //release memory
    //umap
    r = shmdt(ptr);
    if(r ==-1)
    error_out("shmdt");
    //remove the shared memory region
    r = shmctl(shmid,IPC_RMID,NULL);
    if(r ==-1)
    error_out("shmctl");
    return0;
    }
    gcc -o sysv_shm sysv_shm.c
     

    3.信号

        可以使用,但是 很复杂,且容易出错,略去

    4.管道

            未命名管道(unnamed pipe)用于父子进程之间,
            命名管道 (fifo),用于一个计算机上平行的进程之间
    api说明:
        系统调用int pipe(int filedes[2]) ; 
        返回一对 文件描述符
     
    第一个文件描述符fd[0] 的作用 表示只读,第二个fd[1]表示 只写,
    mkfifo(char const *pathname,mode_t mode)用于创建一个 有名管道
     

    5.套接字 

    网络 编程 另讲
     

    6.消息列队

    同样有两套实现,posix和system V
    都采用 固定大小、基于优先级的消息,接受者必须确定消息的大小,并一次对消息进行读取,否则读取失败,而且要确保所用的进程遵循进程相同的消息结构

    system V api:

    函数 作用
    int msgget(key_t key,int msgflg); 创建或者附加一个消息列队,返回一个msgID
    int msgctl(int msgid,int cmd,struct msgid_ds *buf) 可以用于删除一个msgID,当buf参数为空的情况下,删除消息列队的cmd为ipc_rmid
    int msgsnd(int qid,void* msg,size_t msgsz,int msgflg) 发送()
    ssize_t msgrcv(int qid,void* msg,size_t msgsz,int msgflg) 接收()
    例子:

      

    #include<stdio.h>
    #include<string.h>
    #include<stdlib.h>
    #include<unistd.h>
    #include<sys/wait.h>
    #include<sys/stat.h>
    #include<sys/file.h>
    #include<sys/msg.h>
    #include<sys/ipc.h>
    struct message
    {
    longint mtype;
    char mtext[128];
    };
    void error_out(constchar* msg)
    {
    perror(msg);
    exit(EXIT_FAILURE);
    }
    //send msg
    int send_msg(int qid,int mtype,constchar text[])
    {
    struct message msg ={
    .mtype = mtype
    };
    strncpy(msg.mtext,text,sizeof(msg.mtext));
    int r = msgsnd(qid,&msg,sizeof(msg),0);
    if(r ==-1)
    error_out("msgsnd");
    return r;
    }
    //recv msg
    int recv_msg(int qid,int mtype,struct message* msg)
    {
    int r = msgrcv(qid,msg,sizeof(struct message),mtype,0);
    switch(r)
    {
    casesizeof(struct message):
    break;
    case-1:
    perror("msgrcv");
    break;
    default:
    printf("only recv %d bytes data
    ",r);
    break;
    }
    return r;
    }
    void producer(int mqid)
    {
    send_msg(mqid,1,"type 1 - first");
    send_msg(mqid,2,"type 2 - second");
    send_msg(mqid,1,"type 1 - thrid");
    }
    void consumer(int qid)
    {
    struct message msg;
    int r ;
    int i ;
    for(i =0;i <3;++i)
    {
    r = msgrcv(qid,&msg,sizeof(struct message),-2,0);
    printf("[%s]
    ",msg.mtext);
    }
    }
    int main(int argc ,char*argv[])
    {
    //create a private (unname) message queue
    int mqid;
    mqid = msgget(IPC_PRIVATE,S_IREAD | S_IWRITE);
    if(mqid ==-1)
    error_out("msgget");
    pid_t pid = fork();
    if(pid ==0)
    {
    consumer(mqid);
    exit(0);
    }
    else
    {
    int status;
    producer(mqid);
    wait(&status);
    }
    //remove the message queue
    int r = msgctl(mqid,IPC_RMID,0);
    if(r)
    perror("msgctl");
    return0;
    }
    gcc -o systemV_mq systemV-mgq.c  
     ./systemV_mq                     
    [type 1 - first]
    [type 1 - thrid]
    [type 2 - second]
     
    特别指出的是,在消费者 函数中将 msgrecv可以接受的消息类型指定为 -2 了,它表明可以接受 类型为 2 ,或者优先级更低的消息,而且该数值越低,优先级越高,则越先被接受,为了 使 接受和发送的次序保持一致,需要将该值设定为0;
    总之:当msgrecv的类型为非0 时,消息是按优先级高低进行接收的,
    • 正数,只接受 该类型的消息
    • 负数,只接受 小于或者等于 指定类型绝对值 的消息
     

    posix api : 略去

     

    7.信号量  semaphore

    用于 保持 并发进程的同步,信号量 类似于 并发进程的 交通信号灯。
    其本质 是 一个计数器,一种常用的用法是 为每个资源都分配一个信号量,所以,信号量计数的增量从来不会 大于1 ,注意 :是增量 或者减量 

       posix api:

    函数     作用    
    sem_t *sem = sem_open()     创建信号量 并初始化为0
    sem_post(sem    ) 信号量 +1    
    sem_wait(sem    ) 信号量 -1 

    system V  API

     
    函数     作用    
    semget 生成 信号量ID,
    semop       通过不同的op,增或者 减 信号量
    具体例子 可以百度下。
     
    基础知识可以参考:
     
     
    本文参考:《linux开发工具箱》
     
     
     
     
     
     
     
     
     
     
     
     
     
     
  • 相关阅读:
    Topcoder Arena插件配置和训练指南
    [POJ] 3277 .City Horizon(离散+线段树)
    C++ STL算法系列 unique
    C. Polycarpus' Dice
    Codeforces Round #298 (Div. 2) B. Covered Path
    Matlab 2012a 下载和安装教程
    hdu 5203
    ....
    乐观锁vs悲观锁, 集群vs分布式 , 微服务, 幂等性
    正向代理 vs 反向代理
  • 原文地址:https://www.cnblogs.com/Stultz-Lee/p/6709490.html
Copyright © 2011-2022 走看看