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

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

共享内存

  • 共享内存:由于支持多个进程共享同一块内存空间,而且数据不须要在客户进程和服务器进程之间进行复制,所以共享内存可使得通讯速度十分快速
  • 共享内存示意图
  • 经过man命令查询相关的系统内核函数

    发现如下有五个函数与共享内存有关系,但经过man命令可知,对于最后一个shmop函数的解释就是shmat函数:
    • int shmget(key_t key, size_t size, int shmflg):共享内存的创建或者打开linux

    • 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 \n", 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\n");
        }
    }                 

    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 \n", shmid);
    Teacher *p = NULL;
    //将共享内存段链接到进程地址空间
    p = shmat(shmid, NULL, 0);
    if (p == (void *)-1 )
    {
        perror("shmget err");
        return errno;
    }
    
    printf("name:%s\n", p->name);
    printf("age:%d \n", 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
    code

  • 写入数据blog

#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\n", FIFO_NAME);  
            exit(EXIT_FAILURE);  
        }  
    }  
    printf("Process %d opening FIFO_OWRONLY\n", getpid());  
  
    /* open函数以O_WRONLY方式打开FIFO文件,若是成功pipe_fd指向打开的文件 */  
    pipe_fd = open(FIFO_NAME, open_mode);  
    printf("Process %d result %d\n", 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\n");  
                exit(EXIT_FAILURE);  
            }  
            bytes_sent += res;  
        }  
        (void)close(pipe_fd);  
    } else {  
        exit(EXIT_FAILURE);  
    }  
      
    printf("Process %d finished\n", 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, '\0', sizeof(buffer));  
      
    printf("Process %d opening FIFO O_RDONLY\n", getpid());  
    /* open函数打开FIFO_NAME文件,以open_mode的方式(即O_RDONLY) 
     * 若是成功,则返回文件描述符 */  
    pipe_fd = open(FIFO_NAME, open_mode);  
    printf("Process %d result %d\n", 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\n", 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!\n");    
}    
  
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\n");  
        exit(0);  
    }  
  
    printf("pid:%ld\n",(long)getpid());   
  
    while(1)  
    {  
        fgets(buffer,sizeof(buffer),stdin);  
        printf("buffer is:%s\n",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\n", 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\n");  
            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\n", errno);  
        exit(EXIT_FAILURE);  
    }  
    //从队列中获取消息,直到遇到end消息为止  
    while(running)  
    {  
        if(msgrcv(msgid, (void*)&data, BUFSIZ, msgtype, 0) == -1)  
        {  
            fprintf(stderr, "msgrcv failed with errno: %d\n", errno);  
            exit(EXIT_FAILURE);  
        }  
        printf("You wrote: %s\n",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\n");  
        exit(EXIT_FAILURE);  
    }  
    exit(EXIT_SUCCESS);  
}
  • 运行截图
相关文章
相关标签/搜索