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);  
}
  • 運行截圖
相關文章
相關標籤/搜索