zoukankan      html  css  js  c++  java
  • IPC学习

    课下作业-IPC

    要求:

    研究Linux下IPC机制:原理,优缺点,每种机制至少给一个示例,提交研究博客的链接

    • 共享内存

    • 管道

    • FIFO

    • 信号

    • 消息队列

    共享内存

    • 共享内存可以说是最有用的进程间通信方式,也是最快的IPC形式。采用共享内存通信的一个显而易见的好处是效率高,因为进程可以直接读写内存,而不需要任何数据的拷贝。对于像管道和消息队列等通信方式,则需要在内核和用户空间进行四次的数据拷贝,而共享内存则只拷贝两次数据[1]:一次从输入文件到共享内存区,另一次从共享内存区到输出文件。实际上,进程之间在共享内存时,并不总是读写少量数据后就解除映射,有新的通信时,再重新建立共享内存区域。而是保持共享区域,直到通信完毕为止,这样,数据内容一直保存在共享内存中,并没有写回文件。共享内存中的内容往往是在解除映射时才写回文件的。因此,采用共享内存的通信方式效率是非常高的。
    • Linux的2.2.x内核支持多种共享内存方式,如mmap()系统调用,Posix共享内存,以及系统V共享内存。linux发行版本如Redhat 8.0支持mmap()系统调用及系统V共享内存,但还没实现Posix共享内存,本文将主要介绍mmap()系统调用及系统V共享内存API的原理及应用。

    实例:

    #include<stdio.h>
    #include<unistd.h>
    
    int main()
    {
     int fd[2];  // 两个文件描述符
     pid_t pid;
     char buff[20];
    
     if(pipe(fd) < 0)  // 创建管道
         printf("Create Pipe Error!
    ");
    
     if((pid = fork()) < 0)  // 创建子进程
        printf("Fork Error!
    ");
     else if(pid > 0)  // 父进程
     {
         close(fd[0]); // 关闭读端
         write(fd[1], "hello world
    ", 12);
    }
     else
     {
         close(fd[1]); // 关闭写端
        read(fd[0], buff, 20);
         printf("%s", buff);
     }
    
     return 0;
     }
    

    管道

    • 管道是Linux支持的最初Unix IPC形式之一,具有以下特点:
    • 管道是半双工的,数据只能向一个方向流动;需要双方通信时,需要建立起两个管道;
    • 只能用于父子进程或者兄弟进程之间(具有亲缘关系的进程);
    • 单独构成一种独立的文件系统:管道对于管道两端的进程而言,就是一个文件,但它不是普通的文件,它不属于某种文件系统,而是自立门户,单独构成一种文件系统,并且只存在与内存中。
    • 数据的读出和写入:一个进程向管道中写的内容被管道另一端的进程读出。写入的内容每次都添加在管道缓冲区的末尾,并且每次都是从缓冲区的头部读出数据。

    实例:

    static int    pfd1[2], pfd2[2];
    
    void TELL_WAIT(void)
    {
    if (pipe(pfd1) < 0 || pipe(pfd2) < 0)
        printf("pipe error
    ");
    }
    
    void TELL_PARENT(pid_t pid)
    {
    if (write(pfd2[1], "c", 1) != 1)
        printf("write error
    ");
    }
    
    void WAIT_PARENT(void)
    {
    char    c;
    
    if (read(pfd1[0], &c, 1) != 1)
        printf("read error
    ");
    
    if (c != 'p')
    {
        printf("WAIT_PARENT: incorrect data
    ");
        return ;
    }
    
    }
    
    void TELL_CHILD(pid_t pid)
    {
    if (write(pfd1[1], "p", 1) != 1)
        printf("write error
    ");
    }
    
    void WAIT_CHILD(void)
    {
    char    c;
    
    if (read(pfd2[0], &c, 1) != 1)
        printf("read error
    ");
    
    if (c != 'c')
    {
        printf("WAIT_CHILD: incorrect data
    ");
        return ;
    }
    }
    

    FIFO

    • FIFO有时被称为命名管道,未命名的管道只能在两个相关的进程之间使用,而且这两个相关的进程还要有一个共同的祖先进程。但是,通过FIFO,不相关的进程之间也能交换数据。
    • FIFP的创建:
    #include <sys/stat.h>
    
    int mkfifo(const char* path, mode_t mode);
    
    int mkfifoat(int fd, const char* path, mode_t mode);
    

    实例:

    ser.c:

    #include <stdio.h>  
    #include <errno.h>  
    #include <sys/types.h>  
    #include <sys/stat.h>  
    #include <unistd.h>  
    #include <fcntl.h>  
    #include <string.h>  
    #include <stdlib.h>  
    
    #define MAXLINE 1024  
    #define FIFO1 "/tmp/fifo.1"  
    #define FIFO2 "/tmp/fifo.2"  
    
    void Perror(const char *s)  
    {  
    perror(s);  
    exit(EXIT_FAILURE);  
    }  
    
    void server(int readfd, int writefd)  
    {  
    /* send msg  */  
    int i = 0;  
    for (i; i<3; i++) {  
        char buff[MAXLINE] = {0};  
        sprintf(buff, "hello world %d", i);  
        int n = write(writefd, buff, strlen(buff));  
        sleep(1);  
    }  
    char buff[MAXLINE] = {0};  
    int n = read(readfd, buff, MAXLINE);  
    if (n > 0) {  
        printf("read from client:%s
    ", buff);  
    }  
    }  
    
    int main()  
    {  
    int readfd, writefd;  
    
    /* create two FIFO; OK if they already exist */  
    if ((mkfifo(FIFO1, 0777) < 0) && (errno != EEXIST))  
        Perror("can't create FIFO1");  
    if ((mkfifo(FIFO2, 0777) < 0) && (errno != EEXIST)) {  
        unlink(FIFO1); /* rm FIFO1 */  
        Perror("can't create FIFO2");  
    }  
    printf("create fifo success
    ");  
    
    /* 要注意open的顺序 */  
    readfd = open(FIFO2, O_RDONLY, 0);  
    writefd = open(FIFO1, O_WRONLY, 0);  
    printf("open fifo success
    ");  
      
    /* 让FIFO在进程结束后自动删除 */  
    unlink(FIFO1);  
    unlink(FIFO2);  
    
    server(readfd, writefd);  
    
    return 0;  
    }  
    

    cli.c:

    #include <stdio.h>  
    #include <errno.h>  
    #include <sys/types.h>  
    #include <sys/stat.h>  
    #include <unistd.h>  
    #include <fcntl.h>  
    #include <string.h>  
    #include <stdlib.h>  
    
    #define MAXLINE 1024  
    #define FIFO1 "/tmp/fifo.1"  
    #define FIFO2 "/tmp/fifo.2"  
    
    void Perror(const char *s)  
    {  
    perror(s);  
    exit(EXIT_FAILURE);  
    }  
    
    void client(int readfd, int writefd)  
    {  
    /* read msg  */  
    int i = 0;  
    for (i; i<3; i++) {  
        char buff[MAXLINE] = {0};  
        int n = read(readfd, buff, MAXLINE);  
        if (n > 0) {  
            printf("read from server:%s
    ", buff);  
        }  
    }  
    char *buff = "goodby server";  
    write(writefd, buff, strlen(buff));  
    }  
    
    int main()  
    {  
    int readfd, writefd;  
    
    /* create two FIFO; OK if they already exist */  
    if ((mkfifo(FIFO1, 0777) < 0) && (errno != EEXIST))  
        Perror("can't create FIFO1");  
    if ((mkfifo(FIFO2, 0777) < 0) && (errno != EEXIST)) {  
        unlink(FIFO1); /* rm FIFO1 */  
        Perror("can't create FIFO2");  
    }  
    
    /* 要注意open的顺序 */  
    writefd = open(FIFO2, O_WRONLY);  
    readfd = open(FIFO1, O_RDONLY);  
    
    client(readfd, writefd);  
    
    return 0;  
    }  
    

    信号

    • 信号是在软件层次上对中断机制的一种模拟,在原理上,一个进程收到一个信号与处理器收到一个中断请求可以说是一样的。信号是异步的,一个进程不必通过任何操作来等待信号的到达,事实上,进程也不知道信号到底什么时候到达。
    • 信号是进程间通信机制中唯一的异步通信机制,可以看作是异步通知,通知接收信号的进程有哪些事情发生了。信号机制经过POSIX实时扩展后,功能更加强大,除了基本通知功能外,还可以传递附加信息。

    实例:

    #include <signal.h>
    #include <sys/types.h>
    #include <unistd.h>
    void new_op(int,siginfo_t*,void*);
    int main(int argc,char**argv)
    {
    struct sigaction act;
    int sig;
    sig=atoi(argv[1]);
    sigemptyset(&act.sa_mask);
    act.sa_flags=SA_SIGINFO;
    act.sa_sigaction=new_op;
    if(sigaction(sig,&act,NULL) < 0)
    {
    printf("install sigal error
    ");
    }
    while(1)
    {
    sleep(2);
    printf("wait for the signal
    ");
    }
    }
    void new_op(int signum,siginfo_t *info,void *myact)
    {
    printf("receive signal %d", signum);
    sleep(5);
    }
    

    消息队列

    • 消息队列是消息的链表,存放在内核中并由消息队列标识符标识。在某个进程往一个队列写入消息之前,并不需要另外某个进程在该队列上等待消息的到达。这跟管道和FIFO是相反的,对后两者来说,除非读出者已存在,否则先有写入者是没有意义的。

    实例:

    #include <stdio.h>  
    #include <stdlib.h>  
    #include <string.h>  
    #include <sys/ipc.h> //包含ftok  
    #include <sys/msg.h>  
    #include <sys/types.h>  
    #include <sys/fcntl.h>  
    
    #define MSG_W 0200  
    #define BUF_SIZE 512  
    
    typedef struct msgbuf  
    {  
    long mtype ;  
    char mdata[BUF_SIZE] ;  
    } mymsg_t ;  
    
    int   
    main(int argc, char** argv)  
    {  
    int            mqid ;    //消息队列的描述符  
    size_t         msglen ;  //消息的长度  
    long           msgtype ; //消息的类型  
    mymsg_t*  ptr ;     //消息结构的指针  
    
    //用户未按格式输入  
    if (argc != 3)  
        puts("usage: send <pathname> <type>") ;  
    
    msgtype = atoi(argv[2]) ;  
    
    //获取已存在消息队列的描述符  
    mqid = msgget(ftok(argv[1], 0), MSG_W) ;  
    
    //构造一条消息  
    ptr = calloc(sizeof(long) + msglen, sizeof(char)) ;  
    ptr->mtype = msgtype ;  
    snprintf(ptr->mdata, BUF_SIZE, "Hi,Boy~") ;  
    
    //发送消息  
    msglen = strlen(ptr->mdata) ;  
    msgsnd(mqid, ptr, msglen, 0) ;  
    
    exit(0) ;  
    }
  • 相关阅读:
    20200209 ZooKeeper 3. Zookeeper内部原理
    20200209 ZooKeeper 2. Zookeeper本地模式安装
    20200209 Zookeeper 1. Zookeeper入门
    20200206 尚硅谷Docker【归档】
    20200206 Docker 8. 本地镜像发布到阿里云
    20200206 Docker 7. Docker常用安装
    20200206 Docker 6. DockerFile解析
    20200206 Docker 5. Docker容器数据卷
    20200206 Docker 4. Docker 镜像
    Combining STDP and Reward-Modulated STDP in Deep Convolutional Spiking Neural Networks for Digit Recognition
  • 原文地址:https://www.cnblogs.com/fixedl/p/7900681.html
Copyright © 2011-2022 走看看