• 20155321《信息安全系统设计》第十周 课下作业


    20155321《信息安全系统设计》第十周 课下作业——研究Linux下IPC机制

    共享内存

    • 共享内存:因为支持多个进程共享同一块内存空间,并且数据不需要在客户进程和服务器进程之间进行复制,因此共享内存可以使得通信速度十分快速

    • 共享内存示意图

    • 通过man命令查询相关的系统内核函数

      发现以下有五个函数与共享内存有关系,但通过man命令可知,对于最后一个shmop函数的解释就是shmat函数:

      • int shmget(key_t key, size_t size, int shmflg):共享内存的建立或者打开

      • void shmat(int shmid,const void shmaddr,int shmflg):将一个共享内存空间映射到调用进程的地址空间上,返回在进程地址空间中的地址。用户可以通过这个地址间接的访问共享内存

      • int shmdt(const void* shmaddr):将一个进程已经映射了的共享内存脱离进程地址空间

      • int shmctl(int shmid, int cmd, struct shmid_ds *buf):删除已创建好的共享内存

    • 例子:进程write将键盘上输入的字符串存储到共享内存,read进程将共享内存中的数据读出来

    • 运行代码

      • 生成共享内存
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/ipc.h>
    #include <sys/shm.h>
    #include <string.h>
    #include <errno.h>
    
    typedef struct _Teacher
    {
        char name[64];
        int age;
    }Teacher;
    
    int main(int argc, char *argv[])
    {
        int ret = 0;
        int    shmid;
        
        shmid = shmget(0x2234, sizeof(Teacher), IPC_CREAT | 0666); 
        if (shmid == -1)
        {
            perror("shmget err");
            return errno;
        }
        printf("shmid:%d 
    ", shmid);
        Teacher *p = NULL;
        //将共享内存段连接到进程地址空间
        p = shmat(shmid, NULL, 0);//第二个参数shmaddr为NULL,核心自动选择一个地址
        if (p == (void *)-1 )
        {
            perror("shmget err");
            return errno;
        }
        strcpy(p->name, "aaaa");
        p->age = 33;
        //将共享内存段与当前进程脱离
        shmdt(p);
        
        int num;
        scanf("%d", &num);
        if (num == 1)
        {
            //用于控制共享内存
            ret = shmctl(shmid, IPC_RMID, NULL);//IPC_RMID为删除内存段
            if (ret < 0)
            {
                perror("rmerrr
    ");
            }
        }                 
    
        return 0;    
    }
    
    
    • 获取共享内存
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/ipc.h>
    #include <sys/shm.h>
    #include <string.h>
    #include <errno.h>
    
    typedef struct _Teacher
    {
        char name[64];
        int age;
    }Teacher;
    
    int main(int argc, char *argv[])
    {
        int ret = 0;
        int    shmid;
        //shmid = shmget(0x2234, sizeof(Teacher), IPC_CREAT |IPC_EXCL | 0666); 
        //打开获取共享内存
        shmid = shmget(0x2234, 0, 0); 
        if (shmid == -1)
        {
            perror("shmget err");
            return errno;
        }
        printf("shmid:%d 
    ", shmid);
        Teacher *p = NULL;
        //将共享内存段连接到进程地址空间
        p = shmat(shmid, NULL, 0);
        if (p == (void *)-1 )
        {
            perror("shmget err");
            return errno;
        }
        
        printf("name:%s
    ", p->name);
        printf("age:%d 
    ", p->age);
        //将共享内存段与当前进程脱离
        shmdt(p);
        
        int num;
        scanf("%d", &num);
        if (num == 1)
        {
            pause();
        }                
        return 0;
    }
    
    
    
    • 运行截图

    管道

    • 管道:它是父进程和子进程间,或是子进程与子进程间单向的通讯机制,即一个进程发送数据到管道,另外一个进程从管道中读出数据。如果需要双向,或是多项通信机制,则需要建立两个或者多个管道

    • 例子

      • 向管道文件中写数据
    
    #include <unistd.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <string.h>
    #include <fcntl.h>
    #define FIFO_NAME "/tmp/myfifo2"                    //定义宏,指向管道文件位置
    #define TEN_MEG ( 1024*1024*10 )                      //文件缓冲区最大值
    
    
    int main( int argc, char* argv[  ] )
    {
    
      int res;
      int c;
      int pipe_fd;
      int open_mode = O_WRONLY;                         //设置读写权限
      int bytes_send = 0;
    
      if( access( FIFO_NAME, F_OK ) == -1 ) {       //F_OK : 检查是否有这个文件;  类似还有检查文件可读等,参见man中access中定义
        res = mkfifo( FIFO_NAME, 0777 );                  //创建管道文件,文件属性为0777,root可读写
        if( res != 0 ){                                 //管道文件不可重名
          printf( "Could not create fifo %s ", FIFO_NAME );
          exit( 1 );
        }
      }
    
      pipe_fd = open( FIFO_NAME, open_mode );             //打开管道,并设置打开权限
      if( pipe_fd !=-1 ){
    
        while( (c = getchar(  )) > 0 ){
          res = write( pipe_fd, &c, 1 );                       //向管道中写数据
          if( res == -1 ){
        perror( "write error" );
        close( pipe_fd );
        exit( 1 );
          }
        }
        close( pipe_fd );
      }
    
      
      return 0;
    }
    
    
    • 从管道文件中读数据
    
    #include <unistd.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <string.h>
    #include <fcntl.h>
    #include <sys/wait.h>
    #include <signal.h>
    
    #define FIFO_NAME "/tmp/myfifo2"                    //定义宏,指向管道文件的位置
    
    void read_pipe(){                       //子进程, 处理从管道中读数据
    
      int pipe_fd;
      int res;
      int c;
      int open_mode = O_RDONLY;                   //设置权限,为只读
      
      pipe_fd = open( FIFO_NAME, open_mode );              //打开管道文件,并设置打开权限,返回int
      
      if( pipe_fd!=-1 ){
        while(1){                     
          
          while( (res = read( pipe_fd, &c, 1 )) > 0 ){              //从管道读数据
        putchar( c );
          }
          fflush( stdout );
        }
      }else{
        exit( 1 );
      }
    
      close(pipe_fd);
    
    }
    
    void signal_handler( int n){                                 //受到子进程退出信号,结束子进程
      int child_status;
      wait( &child_status );
      printf( "child exited. " );
    }
    
    int main( int argc, char *argv[  ] )
    {
    
      int pid;
      int child_status;
      
      signal(SIGCHLD, signal_handler);                    //子进程退出时所发信号
      pid = fork();                                //创建子进程,使之读取管道数据
      int i = 0;
      switch(pid){
      case -1:
        printf("fork error");
        exit( 1 );
      case 0:
        read_pipe();
        exit( 0 );
      default:                              //做它自己无聊的事
        for(i;i<100;i++){
          printf("%d ", i);
          fflush( stdout );
          sleep(2);
        }
      }
    
    
      return 0;
    }
    
    
    • 运行截图

    FIFO

    • FIFO

    • 写入数据

    
    #include <stdio.h>  
    #include <stdlib.h>  
    #include <unistd.h>  
    #include <string.h>  
    #include <fcntl.h>  
    #include <limits.h>  
    #include <sys/types.h>  
    #include <sys/stat.h>  
      
    #define FIFO_NAME "/tmp/my_fifo"  
    #define BUFFER_SIZE PIPE_BUF  
    #define TEN_MEG (1024 * 1024 * 10)  
      
    int main()  
    {  
        int pipe_fd;  
        int res;  
        int open_mode = O_WRONLY;  
        int bytes_sent = 0;  
        char buffer[BUFFER_SIZE + 1];  
      
        /* 检查FIFO_NAME文件是否存在,如果不存在就创建它 */  
        if (access(FIFO_NAME, F_OK) == -1) {  
            /* mkfifo创建命名管道(即特殊类型的文件FIFO) */  
            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_OWRONLY
    ", getpid());  
      
        /* open函数以O_WRONLY方式打开FIFO文件,如果成功pipe_fd指向打开的文件 */  
        pipe_fd = open(FIFO_NAME, open_mode);  
        printf("Process %d result %d
    ", getpid(), pipe_fd);  
      
        if (pipe_fd != -1) {  
            /* */  
            while(bytes_sent < TEN_MEG) {  
                /* write函数从buffer指向的内存中写入BUFFER_SIZE个字节到pipe_fd文件中 
                 * 如果成功则返回实际写入的字节数 */  
                res = write(pipe_fd, buffer, BUFFER_SIZE);  
                if (res == -1) {  
                    fprintf(stderr, "Write error on pipe
    ");  
                    exit(EXIT_FAILURE);  
                }  
                bytes_sent += res;  
            }  
            (void)close(pipe_fd);  
        } else {  
            exit(EXIT_FAILURE);  
        }  
          
        printf("Process %d finished
    ", getpid());  
        exit(EXIT_SUCCESS);  
    }  
    
    
    • 从FIFO读取数据并丢弃
    
    #include <stdio.h>  
    #include <stdlib.h>  
    #include <unistd.h>  
    #include <string.h>  
    #include <fcntl.h>  
    #include <limits.h>  
    #include <sys/types.h>  
    #include <sys/stat.h>  
      
    #define FIFO_NAME "/tmp/my_fifo"  
    #define BUFFER_SIZE PIPE_BUF  
      
    int main()  
    {  
        int pipe_fd;  
        int res;  
        int open_mode = O_RDONLY;  
        char buffer[BUFFER_SIZE + 1];  
        int bytes_read = 0;  
      
        memset(buffer, '', sizeof(buffer));  
          
        printf("Process %d opening FIFO O_RDONLY
    ", getpid());  
        /* open函数打开FIFO_NAME文件,以open_mode的方式(即O_RDONLY) 
         * 如果成功,则返回文件描述符 */  
        pipe_fd = open(FIFO_NAME, open_mode);  
        printf("Process %d result %d
    ", getpid(), pipe_fd);  
      
        if (pipe_fd != -1) {  
            do {  
                /* read函数从pipe_fd指向的文件中读入BUFFER_SIZE个字节的数据到buffer指向的内存  
                 * 如果成功,返回实际读入数据的字节数 */  
                res = read(pipe_fd, buffer, BUFFER_SIZE);  
                bytes_read += res;  
            } while (res > 0);  
            (void)close(pipe_fd);  
        } else {  
            exit(EXIT_FAILURE);  
        }  
      
        printf("Process %d finished, %d bytes read
    ", getpid(), bytes_read);  
        exit(EXIT_SUCCESS);  
    }  
    
    
    • 运行截图

    信号

    • 信号处理流程

      • 信号诞生
      • 信号在进程中注册
      • 信号的执行和注销
    • 信号的发送

      • kill()
      • raise()
      • sigqueue()
      • alarm()
      • setitimer()
      • abort()
    • 例子

    
    #include <stdio.h>   
    #include <sys/types.h>   
    #include <stdlib.h>    
    #include <signal.h>    
        
    void func(int sig)     
    {    
        printf("I get a signal!
    ");    
    }    
      
    int main()    
    {   
        char buffer[100];    
      
        struct sigaction act;  
        act.sa_handler=func;  
        sigemptyset(&act.sa_mask);  
        act.sa_flags = 0;  
      
        if(sigaction(SIGINT,&act, NULL) == -1)  
        {  
            printf("sigaction error exit now
    ");  
            exit(0);  
        }  
      
        printf("pid:%ld
    ",(long)getpid());   
      
        while(1)  
        {  
            fgets(buffer,sizeof(buffer),stdin);  
            printf("buffer is:%s
    ",buffer);  
        }  
      
        return 0;    
    }
    
    
    • 运行截图

      运行中通过快捷键Ctrl+C中断进程

    消息队列

    • 消息队列:提供一种从一个进程向另一个进程发送一个数据块的方法。

    • 每个数据块都被认为含有一个类型,接收进程可以独立地接收含有不同类型的数据结构。可以通过发送消息来避免命名管道的同步和阻塞问题

    • 例子

      • 发送消息
    
    #include <unistd.h>  
    #include <stdlib.h>  
    #include <stdio.h>  
    #include <string.h>  
    #include <sys/msg.h>  
    #include <errno.h>  
      
    #define MAX_TEXT 512  
    struct msg_st  
    {  
        long int msg_type;  
        char text[MAX_TEXT];  
    };  
      
    int main()  
    {  
        int running = 1;  
        struct msg_st data;  
        char buffer[BUFSIZ];  
        int msgid = -1;  
      
        //建立消息队列  
        msgid = msgget((key_t)1234, 0666 | IPC_CREAT);  
        if(msgid == -1)  
        {  
            fprintf(stderr, "msgget failed with error: %d
    ", errno);  
            exit(EXIT_FAILURE);  
        }  
      
        //向消息队列中写消息,直到写入end  
        while(running)  
        {  
            //输入数据  
            printf("Enter some text: ");  
            fgets(buffer, BUFSIZ, stdin);  
            data.msg_type = 1;    //注意2  
            strcpy(data.text, buffer);  
            //向队列发送数据  
            if(msgsnd(msgid, (void*)&data, MAX_TEXT, 0) == -1)  
            {  
                fprintf(stderr, "msgsnd failed
    ");  
                exit(EXIT_FAILURE);  
            }  
            //输入end结束输入  
            if(strncmp(buffer, "end", 3) == 0)  
                running = 0;  
            sleep(1);  
        }  
        exit(EXIT_SUCCESS);  
    }  
    
    
    • 接收消息
    
    #include <unistd.h>  
    #include <stdlib.h>  
    #include <stdio.h>  
    #include <string.h>  
    #include <errno.h>  
    #include <sys/msg.h>  
      
    struct msg_st  
    {  
        long int msg_type;  
        char text[BUFSIZ];  
    };  
      
    int main()  
    {  
        int running = 1;  
        int msgid = -1;  
        struct msg_st data;  
        long int msgtype = 0; //注意1  
      
        //建立消息队列  
        msgid = msgget((key_t)1234, 0666 | IPC_CREAT);  
        if(msgid == -1)  
        {  
            fprintf(stderr, "msgget failed with error: %d
    ", errno);  
            exit(EXIT_FAILURE);  
        }  
        //从队列中获取消息,直到遇到end消息为止  
        while(running)  
        {  
            if(msgrcv(msgid, (void*)&data, BUFSIZ, msgtype, 0) == -1)  
            {  
                fprintf(stderr, "msgrcv failed with errno: %d
    ", errno);  
                exit(EXIT_FAILURE);  
            }  
            printf("You wrote: %s
    ",data.text);  
            //遇到end结束  
            if(strncmp(data.text, "end", 3) == 0)  
                running = 0;  
        }  
        //删除消息队列  
        if(msgctl(msgid, IPC_RMID, 0) == -1)  
        {  
            fprintf(stderr, "msgctl(IPC_RMID) failed
    ");  
            exit(EXIT_FAILURE);  
        }  
        exit(EXIT_SUCCESS);  
    }  
    
    
    • 运行截图
  • 相关阅读:
    C# 使用DateTime.TryParseExact将自定义日期类型转换成日期
    ASP.NET WebServices 因 URL 意外地以“/HelloWorld”结束,请求格式无法识别。
    [UE4]计算2点坐标附近的坐标:线性插值法
    [UE4]Selector和Sequence的区别
    [UE4]蒙太奇动画运行时不播放,预览是好的
    [UE4]蓝图函数库小结
    [UE4]C++调用蓝图函数:BlueprintImplementableEvent函数说明符用法
    [UE4GamePlay架构(九)GameInstance(转)
    [UE4]C++取得蓝图控件实例
    [UE4]虚幻4的智能指针
  • 原文地址:https://www.cnblogs.com/rafell/p/7880354.html
Copyright © 2020-2023  润新知