zoukankan      html  css  js  c++  java
  • # 2017-2018-1 20155319 课堂实践及补充

    2017-2018-1 20155319 课堂实践及补充

    研究Linux下IPC机制:原理,优缺点,每种机制至少给一个示例 
    - 共享内存
    - 管道
    - FIFO
    - 信号
    - 消息队列
    

    共享内存

    • 共享内存可以说是最有用的进程间通信方式,也是最快的IPC形式。采用共享内存通信的一个显而易见的好处是效率高,因为进程可以直接读写内存,而不需要任何数据的拷贝。对于像管道和消息队列等通信方式,则需要在内核和用户空间进行四次的数据拷贝,而共享内存则只拷贝两次数据[1]:一次从输入文件到共享内存区,另一次从共享内存区到输出文件。实际上,进程之间在共享内存时,并不总是读写少量数据后就解除映射,有新的通信时,再重新建立共享内存区域。而是保持共享区域,直到通信完毕为止,这样,数据内容一直保存在共享内存中,并没有写回文件。共享内存中的内容往往是在解除映射时才写回文件的。因此,采用共享内存的通信方式效率是非常高的。

    代码示例:

    #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;
     }
    

    管道

    • 管道实际是用于进程间通信的一段共享内存,创建管道的进程称为管道服务器,连接到一个管道的进程为管道客户机。一个进程在向管道写入数据后,另一进程就可以从管道的另一端将其读取出来。
    管道的特点:
    1、管道是半双工的,数据只能向一个方向流动;需要双方通信时,需要建立起两个管道;
    2、只能用于父子进程或者兄弟进程之间(具有亲缘关系的进程)。比如fork或exec创建的新进程,在使用exec创建新进程时,需要将管道的文件描述符作为参数传递给exec创建的新进程。当父进程与使用fork创建的子进程直接通信时,发送数据的进程关闭读端,接受数据的进程关闭写端。
    3、单独构成一种独立的文件系统:管道对于管道两端的进程而言,就是一个文件,但它不是普通的文件,它不属于某种文件系统,而是自立门户,单独构成一种文件系统,并且只存在与内存中。
    4、数据的读出和写入:一个进程向管道中写的内容被管道另一端的进程读出。写入的内容每次都添加在管道缓冲区的末尾,并且每次都是从缓冲区的头部读出数据。
    

    代码示例:

    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

    • 命名管道是一种特殊类型的文件,它在系统中以文件形式存在。这样克服了管道的弊端,他可以允许没有亲缘关系的进程间通信。

    创建管道的两个系统调用原型:

    #include <sys/types.h> 
    #include <sys/stat.h> 
    int mkfifo(const char *filename,mode_t mode); //建立一个名字为filename的命名管道,参数mode为该文件的权限(mode%~umask),若成功则返回0,否则返回-1,错误原因存于errno中。
    eg.mkfifo( "/tmp/cmd_pipe", S_IFIFO | 0666 );
    

    server:

    #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;  
    }  
    

    client:

    #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;  
    }  
    

    信号

    • 信号机制是unix系统中最为古老的进程之间的通信机制,用于一个或几个进程之间传递异步信号。信号可以有各种异步事件产生,比如键盘中断等。shell也可以使用信号将作业控制命令传递给它的子进程。
    #include <sys/types.h>   
    #include <signal.h>   
    void (*signal(int sig,void (*func)(int)))(int); //用于截取系统信号,第一个参数为信号,第二个参数为对此信号挂接用户自己的处理函数指针。返回值为以前信号处理程序的指针。  
    eg.int ret = signal(SIGSTOP, sig_handle); 
    

    消息队列

    • 消息队列是内核地址空间中的内部链表,通过linux内核在各个进程直接传递内容,消息顺序地发送到消息队列中,并以几种不同的方式从队列中获得,每个消息队列可以用IPC标识符唯一地进行识别。内核中的消息队列是通过IPC的标识符来区别,不同的消息队列直接是相互独立的。每个消息队列中的消息,又构成一个独立的链表。
      消息队列克服了信号承载信息量少,管道只能承载无格式字符流。

    代码实验:

    #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) ;  
    }
    
  • 相关阅读:
    ASP.NET ValidationGroup 属性和CssClass 属性
    sql 在将 nvarchar 值 转换成数据类型 int 时失败。
    select 1 from table
    OLEDB和ODBC的区别
    ASP.NET 读数据库绑定到 TreeView 递归方式
    用python做网页抓取与解析入门笔记[zz]
    win8.1安装驱动出现“文件的哈希值不在指定的目录”的解决办法[zz]
    Linux磁盘IO监控[zz]
    硬盘内部硬件结构和工作原理详解[zz]
    fcitx五笔的安装[zz]
  • 原文地址:https://www.cnblogs.com/rhl20155319/p/7900987.html
Copyright © 2011-2022 走看看