zoukankan      html  css  js  c++  java
  • 20155339 《信息安全系统设计》第十周课下作业-IPC

    20155339 《信息安全系统设计》第十周课下作业-IPC

    共享内存

    • 共享内存是在多个进程之间共享内存区域的一种进程间的通信方式,由IPC为进程创建的一个特殊地址范围,它将出现在该进程的地址空间中。

    • 使用共享内存,不同进程可以对同一块内存进行读写。

    • 优点:由于所有进程对共享内存的访问就和访问自己的内存空间一样,而不需要进行额外系统调用或内核操作,同时还避免了多余的内存拷贝,所以,这种方式是效率最高、速度最快的进程间通信方式。

    • 缺点:内核并不提供任何对共享内存访问的同步机制,即引发读写问题。

    • Linux下一般一个page大小是4k。

    • 创建共享内存空间后,内核将不同进程虚拟地址的映射到同一个页面:所以在不同进程中,对共享内存所在的内存地址的访问最终都被映射到同一页面。

    • 共享内存的使用过程可分为 创建->连接->使用->分离->销毁 这几步。

    • 下图展示了共享内存的工作机制:

    • 使用帮助文档,查看共享内存的创建函数,如下:

    • 由上图可知:分配共享内存使用shmget函数:

    使用方法如下:int shmget(key_t key, size_t size, int shmflg)

    • 由于帮助文档不怎么看得懂,又找到了下图进行学习:

    • 创建后,为了使共享内存可以被当前进程使用,必须紧接着进行连接操作。使用函数shmat(SHared Memory ATtach),参数传入通过shmget返回的共享内存id。

    • shamt函数:连接共享内存标识符为shmid的共享内存,连接成功后把共享内存区对象映射到调用进程的地址空间,随后可像本地空间一样访问。函数原型:void *shmat(int shmid, const void *shmaddr, int shmflg)。返回附加好的共享内存地址。

    • shmat函数:断开共享内存连接。

    • shmctl函数:共享内存管理。

    • 代码练习(读写数据):

    
    #include <stdio.h>
    #include <sys/ipc.h>
    #include <sys/shm.h>
    #include <sys/types.h>
    #include <unistd.h>
    #include <string.h>
    typedef struct{
        char name[8];
        int age;
    } people;
    int main(int argc, char** argv)
    {
        int shm_id,i;
        key_t key;
        char temp[8];
        people *p_map;
        char pathname[30] ;
     
        strcpy(pathname,"/tmp") ;
        key = ftok(pathname,0x03);
        if(key==-1)
        {
            perror("ftok error");
            return -1;
        }
        printf("key=%d
    ",key) ;
        shm_id=shmget(key,4096,IPC_CREAT|IPC_EXCL|0600); 
        if(shm_id==-1)
        {
            perror("shmget error");
            return -1;
        }
        printf("shm_id=%d
    ", shm_id) ;
        p_map=(people*)shmat(shm_id,NULL,0);
        memset(temp, 0x00, sizeof(temp)) ;
        strcpy(temp,"test") ;
        temp[4]='0';
        for(i = 0;i<3;i++)
        {
            temp[4]+=1;
            strncpy((p_map+i)->name,temp,5);
            (p_map+i)->age=0+i;
        }
        shmdt(p_map) ;
        return 0 ;
    }
    
    
    
    #include <stdio.h>
    #include <string.h>
    #include <sys/ipc.h>
    #include <sys/shm.h>
    #include <sys/types.h>
    #include <unistd.h>
    typedef struct{
        char name[8];
        int age;
    } people;
    int main(int argc, char** argv)
    {
        int shm_id,i;
        key_t key;
        people *p_map;
        char pathname[30] ;
     
        strcpy(pathname,"/tmp") ;
        key = ftok(pathname,0x03);
        if(key == -1)
        {
            perror("ftok error");
            return -1;
        }
        printf("key=%d
    ", key) ;
        shm_id = shmget(key,0, 0);   
        if(shm_id == -1)
        {
            perror("shmget error");
            return -1;
        }
        printf("shm_id=%d
    ", shm_id) ;
        p_map = (people*)shmat(shm_id,NULL,0);
        for(i = 0;i<3;i++)
        {
            printf( "name:%s
    ",(*(p_map+i)).name );
            printf( "age %d
    ",(*(p_map+i)).age );
        }
        if(shmdt(p_map) == -1)
        {
        
            perror("detach error");
            return -1;
        }
        return 0 ;
    }
    
    
    • 运行结果

    • 可看出读和写两段代码都是在同一段地址上进行的。

    管道

    • 管道实际是用于进程间通信的一段共享内存,创建管道的进程称为管道服务器,连接到一个管道的进程为管道客户机。一个进程在向管道写入数据后,另一进程就可以从管道的另一端将其读取出来。

    • 管道是半双工的,数据只能向一个方向流动;需要双方通信时,需要建立起两个管道。

    • 只能用于父子进程或者兄弟进程之间(具有亲缘关系的进程)。比如fork或exec创建的新进程,在使用exec创建新进程时,需要将管道的文件描述符作为参数传递给exec创建的新进程。

    • 数据由缓冲区的尾部写入,头部读出。先进先出原则。

    • PIPE函数:

    • 代码练习

    #include<stdio.h>
    #include<unistd.h>
    #include<stdlib.h>
    #include<string.h>
    #include<wait.h>
    #include<sys/types.h>
    int main(int argc ,char *argv[])
    {
     int pipefd[2],result;
     char buf[1024];
     int flag=0;
     pid_t pid;
     result= pipe(pipefd);//创建一个管道
     if(result==-1){
      perror("pipe error:");
      exit(EXIT_FAILURE);
     }
     pid=fork();//创建一个子进程
     if(pid==-1)
     {
      perror("fork  error:");
      exit(EXIT_FAILURE);
     }
     else if(pid==0){
      if((close(pipefd[1]))==-1)//close write only read
      {
       perror("close write  error:");
       exit(EXIT_FAILURE);
      }
      while(1){ //循环读取数据
       read(pipefd[0],buf,1024);//最多读取1024个字节
       printf("read from pipe :  %s
    ",buf);
       if(strcmp(buf,"quit")==0){// if 读取到的字符串是exit 这是 
                        //父进程会接受到一个终止进程的信号,父进程会回收子进程的资                   // 源等
       exit(EXIT_SUCCESS);
       }    
      }     
     }else{
      //close read only write
      if((close(pipefd[0]))==-1){
       perror("close read error:");
       exit(EXIT_FAILURE);
      } 
      while(1)//循环写入内容
      { 
       waitpid(pid,NULL,WNOHANG);//等待子进程退出
       if(flag==1) 
        exit(0);
       scanf("%s",buf);
       write(pipefd[1],buf,strlen(buf)+1);//具体写多少个字节
       if(strcmp(buf,"quit")==0){ 
        flag=1;
        sleep(1);//让子进程完全退出。
       }
      }
     }
     return 1;
    }
    
    

    FIFO(命名管道)

    • 我觉得是管道的升级版,因为命名管道是一种特殊类型的文件,它在系统中以文件形式存在。这样克服了管道的弊端,他可以允许没有亲缘关系的进程间通信。
    • 操作方法只要创建了一个命名管道然后就可以使用open、read、write等系统调用来操作。
    • 代码练习:
    
    #include <unistd.h>  
    #include <stdlib.h>  
    #include <fcntl.h>  
    #include <limits.h>  
    #include <sys/types.h>  
    #include <sys/stat.h>  
    #include <stdio.h>  
    #include <string.h>  
      
    int main()  
    {  
        const char *fifo_name = "/tmp/my_fifo";  
        int pipe_fd = -1;  
        int data_fd = -1;  
        int res = 0;  
        const int open_mode = O_WRONLY;  
        int bytes_sent = 0;  
        char buffer[PIPE_BUF + 1];  
      
        if(access(fifo_name, F_OK) == -1)  
        {  
            //管道文件不存在  
            //创建命名管道  
            res = mkfifo(fifo_name, 0777);  
            if(res != 0)  
            {  
                fprintf(stderr, "Could not create fifo %s
    ", fifo_name);  
                exit(EXIT_FAILURE);  
            }  
        }  
      
        printf("Process %d opening FIFO O_WRONLY
    ", getpid());  
        //以只写阻塞方式打开FIFO文件,以只读方式打开数据文件  
        pipe_fd = open(fifo_name, open_mode);  
        data_fd = open("Data.txt", O_RDONLY);  
        printf("Process %d result %d
    ", getpid(), pipe_fd);  
      
        if(pipe_fd != -1)  
        {  
            int bytes_read = 0;  
            //向数据文件读取数据  
            bytes_read = read(data_fd, buffer, PIPE_BUF);  
            buffer[bytes_read] = '';  
            while(bytes_read > 0)  
            {  
                //向FIFO文件写数据  
                res = write(pipe_fd, buffer, bytes_read);  
                if(res == -1)  
                {  
                    fprintf(stderr, "Write error on pipe
    ");  
                    exit(EXIT_FAILURE);  
                }  
                //累加写的字节数,并继续读取数据  
                bytes_sent += res;  
                bytes_read = read(data_fd, buffer, PIPE_BUF);  
                buffer[bytes_read] = '';  
            }  
            close(pipe_fd);  
            close(data_fd);  
        }  
        else  
            exit(EXIT_FAILURE);  
      
        printf("Process %d finished
    ", getpid());  
        exit(EXIT_SUCCESS);  
    }  
    
    
    
    #include <unistd.h>  
    #include <stdlib.h>  
    #include <stdio.h>  
    #include <fcntl.h>  
    #include <sys/types.h>  
    #include <sys/stat.h>  
    #include <limits.h>  
    #include <string.h>  
      
    int main()  
    {  
        const char *fifo_name = "/tmp/my_fifo";  
        int pipe_fd = -1;  
        int data_fd = -1;  
        int res = 0;  
        int open_mode = O_RDONLY;  
        char buffer[PIPE_BUF + 1];  
        int bytes_read = 0;  
        int bytes_write = 0;  
        //清空缓冲数组  
        memset(buffer, '', sizeof(buffer));  
      
        printf("Process %d opening FIFO O_RDONLY
    ", getpid());  
        //以只读阻塞方式打开管道文件,注意与fifowrite.c文件中的FIFO同名  
        pipe_fd = open(fifo_name, open_mode);  
        //以只写方式创建保存数据的文件  
        data_fd = open("DataFormFIFO.txt", O_WRONLY|O_CREAT, 0644);  
        printf("Process %d result %d
    ",getpid(), pipe_fd);  
      
        if(pipe_fd != -1)  
        {  
            do  
            {  
                //读取FIFO中的数据,并把它保存在文件DataFormFIFO.txt文件中  
                res = read(pipe_fd, buffer, PIPE_BUF);  
                bytes_write = write(data_fd, buffer, res);  
                bytes_read += res;  
            }while(res > 0);  
            close(pipe_fd);  
            close(data_fd);  
        }  
        else  
            exit(EXIT_FAILURE);  
      
        printf("Process %d finished, %d bytes read
    ", getpid(), bytes_read);  
        exit(EXIT_SUCCESS);  
    }  
    
    
    • 运行结果:

    • 管道和命名管道有什么区别

    • 1.在命名管道中,管道可以是事先已经创建好的,而在管道中,管道已经在主进程里创建好了,然后在fork时直接复制相关数据或者是用exec创建的新进程时把管道的文件描述符当参数传递进去。
      2.一般来说FIFO和PIPE一样总是处于阻塞状态。如果不希望命名管道操作的时候发生阻塞,可以在open的时候使用O_NONBLOCK标志,以关闭默认的阻塞操作。

    信号

    • 一个信号的生命周期中有两个阶段:生成和传送。
    • 内核为进程生产信号,来响应不同的事件,这些事件就是信号源。主要的信号源如下:
      1.异常:进程运行过程中出现异常;
      2.其它进程:一个进程可以向另一个或一组进程发送信号;
      3.终端中断:Ctrl-C,Ctrl-/等;
      4.作业控制:前台、后台进程的管理;
      5.分配额:CPU超时或文件大小突破限制;
      6.通知:通知进程某事件发生,如I/O就绪等;
      7.报警:计时器到期。
    • 代码练习:
    
    #include <stdio.h>   
    #include <sys/types.h>   
    #include <stdlib.h>    
    #include <signal.h>    
        
    
    int main()    
    {   
        char buffer[100];    
      
        struct sigaction act;  
      
        if(sigaction(SIGINT,&act, NULL) == -1)  
        {  
            printf("sigaction error exit now
    ");  
            exit(0);  
        }  
      
        while(1)  
        {  
            fgets(buffer,sizeof(buffer),stdin);  
            printf("%s
    ",buffer);  
        }  
      
        return 0;    
    }
    
    

    消息队列

    • 消息队列是内核地址空间中的内部链表,通过linux内核在各个进程直接传递内容,消息顺序地发送到消息队列中,并以几种不同的方式从队列中获得,每个消息队列可以用IPC标识符唯一地进行识别。
    • 内核中的消息队列是通过IPC的标识符来区别,不同的消息队列直接是相互独立的。
    • 每个消息队列中的消息,又构成一个独立的链表。
    • Linux的消息队列(queue)实质上是一个链表,它有消息队列标识符(queue ID)。
    • msgget创建一个新队列或打开一个存在的队列;
    • msgsnd向队列末端添加一条新消息;
    • msgrcv从队列中取消息,取消息是不一定遵循先进先出的, 也可以按消息的类型字段取消息。
    • 代码练习:
    
    #include<stdio.h>
    #include<stdlib.h>
    #include<sys/msg.h>
    #include<string.h>
    #include<unistd.h>
    #define MAX_TEXT 1024
    #define MSG_SIZE 512
    struct msg_st{
        long int msg_type; //消息类型
        char text[MAX_TEXT];//消息内容
    };
    int main()
    {
        struct msg_st data;
        char buf[MSG_SIZE];
        int msgid=msgget((key_t)2456,0666|IPC_CREAT);
        if(msgid==-1){
            perror("msgget");
            exit(1);
        }
        while(1){
            printf("接收:");
            if(msgrcv(msgid,(void*)&data,MAX_TEXT,1,0)==-1){
                perror("msgrcv");
                exit(2);
            }
            printf("%s
    ",data.text);
        }
        return 0;
    }
    
    
    
    #include<stdio.h>
    #include<sys/ipc.h>
    #include<stdlib.h>
    #include<sys/types.h>
    #include<sys/msg.h>
    #include<unistd.h>
    #include<string.h>
    #define MAX_TEXT 512
    #define MSG_SIZE 512
    struct msg_st
    {
        long int msg_type;  //消息类型
        char text[MAX_TEXT];//消息内容
    };
    int main()
    {
        struct msg_st data;
        char buf[MSG_SIZE];
        //创建消息队列
        int msgid=msgget((key_t)2456,0666|IPC_CREAT);
        if(msgid==-1)
        {
            perror("msgget");
            exit(1);
        }
        printf("消息队列创建成功
    ");
        //发送消息
        while(1)
        {
            //从键盘输入发送的消息
            printf("发送:");
            fgets(buf,MSG_SIZE,stdin);
            data.msg_type=1;
            strcpy(data.text,buf);
            //将消息发送到消息队列
            if(msgsnd(msgid,(void*)&data,MAX_TEXT,0)==-1){
                perror("msgsnd");
                exit(1);
            }   
        }
        return 0;
    }
    
    
    • 运行截图

    参考链接

  • 相关阅读:
    双向链表

    一个简单makefile
    内存管理
    队列
    postgresql表结构查询sql
    postgresql 命令
    NSMutableDictionary 中使用setValueForKey 和 setObjectForKey有什么区别?
    ViewController 生命周期
    程序启动过程
  • 原文地址:https://www.cnblogs.com/pingcpingcuo/p/8013217.html
Copyright © 2011-2022 走看看