1. 程式人生 > >20155212 2017-2018-1 《信息安全系統設計》第10周學習總結

20155212 2017-2018-1 《信息安全系統設計》第10周學習總結

顯示文件 sgi 查看 共享 設備號 顯示 sta 空間 commit

20155212 2017-2018-1 《信息安全系統設計》第10周學習總結

stat命令的實現-mysate

  • 要求:學習使用stat(1),並用C語言實現
  • 學習stat(1)
    • 功能:顯示文件或者文件系統信息
    • 語法stat [選項] 文件
    • 選項參數
      • null:顯示詳細信息
      • -l:鏈接
      • -f:不顯示文件的信息,而顯示其所在文件系統的信息
      • -t:顯示簡潔的信息
      • -c:以指定格式輸出
    • man 1 stat查看stat命令技術分享圖片
    • 使用stat命令技術分享圖片
  • 使用man -k stat | grep 2函數找到如下技術分享圖片
  • 使用man 2 stat查看技術分享圖片
  • 使用stat()函數會獲得stat結構體技術分享圖片

    struct stat    
    {    
        dev_t       st_dev;     /* ID of device containing file -文件所在設備的ID*/    
        ino_t       st_ino;     /* inode number -inode節點號*/  
        mode_t      st_mode;    /* 文件的類型和存取的權限*/    
        nlink_t     st_nlink;   /* number of hard links -鏈向此文件的連接數(硬連接)*/    
        uid_t       st_uid;     /* user ID of owner -user id*/    
        gid_t       st_gid;     /* group ID of owner - group id*/    
        dev_t       st_rdev;    /* device ID (if special file) -設備號,針對設備文件*/    
        off_t       st_size;    /* total size, in bytes -文件大小,字節為單位*/    
        blksize_t   st_blksize; /* blocksize for filesystem I/O -系統塊的大小*/    
        blkcnt_t    st_blocks;  /* number of blocks allocated -文件所占塊數*/    
        time_t      st_atime;   /* time of last access -最近存取時間*/    
        time_t      st_mtime;   /* time of last modification -最近修改時間*/    
        time_t      st_ctime;   /* time of last status change - */    
    };    

    其中,比較特殊的是st_mode,st_mode是用特征位來表示文件類型的,特征位的定義如下:

    S_IFMT      0170000     文件類型的位遮罩  
    S_IFSOCK    0140000     socket  
    S_IFLNK     0120000     符號鏈接(symbolic link)  
    S_IFREG     0100000     一般文件  
    S_IFBLK     0060000     區塊裝置(block device)  
    S_IFDIR     0040000     目錄  
    S_IFCHR     0020000     字符裝置(character device)  
    S_IFIFO     0010000     先進先出(fifo)  
    S_ISUID     0004000     文件的(set user-id on execution)位  
    S_ISGID     0002000     文件的(set group-id on execution)位  
    S_ISVTX     0001000     文件的sticky位  
    S_IRWXU     00700       文件所有者的遮罩值(即所有權限值)  
    S_IRUSR     00400       文件所有者具可讀取權限  
    S_IWUSR     00200       文件所有者具可寫入權限  
    S_IXUSR     00100       文件所有者具可執行權限  
    S_IRWXG     00070       用戶組的遮罩值(即所有權限值)  
    S_IRGRP     00040       用戶組具可讀取權限  
    S_IWGRP     00020       用戶組具可寫入權限  
    S_IXGRP     00010       用戶組具可執行權限  
    S_IRWXO     00007       其他用戶的遮罩值(即所有權限值)  
    S_IROTH     00004       其他用戶具可讀取權限  
    S_IWOTH     00002       其他用戶具可寫入權限  
    S_IXOTH     00001       其他用戶具可執行權限  

    判斷文件類型時,用對文件的st_mode的值與文件類型的位遮罩相與,再比較。

  • stat結構體中很多變量的類型都是不常用的,不能直接輸出該類型,所以使用grep -r *查找同名變量的類型。以存儲大小的變量st_size為例,發現很多使用的long long類型技術分享圖片

  • 偽代碼

input path;
struct state;
stat(path,state);
print(state);
  • 產品代碼 mystate.c,提交碼雲鏈接
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <time.h>

void main(int argc, char *argv[])
{
    struct stat state;

    stat(argv[1], &state);

    printf("  文件:‘%s‘\n", argv[1]);
    printf("  大小:%lld\t", (long long)state.st_size);
    printf("塊:%lld\t", (long long)state.st_blocks);
    printf("IO塊:%ld\t", (long)state.st_blksize);
    switch(state.st_mode & S_IFMT)
    {
    case S_IFBLK:
        printf("塊設備文件");
        break;
    case S_IFCHR:
        printf("字符設備文件");
        break;
    case S_IFDIR:
        printf("目錄");
        break;
    case S_IFIFO:
        printf("管道文件");
        break;
    case S_IFLNK:
        printf("符號鏈接文件");
        break;
    case S_IFREG:
        printf("普通文件");
        break;
    case S_IFSOCK:
        printf("套接字文件");
        break;
    default:
        break;
    }
    printf("\n");

    printf("設備:%xh/%ldd\t", (long)state.st_dev, (long)state.st_dev);
    printf("Inode:%ld\t", (long)state.st_ino);
    printf("硬鏈接:%ld\n", (long)state.st_nlink);
    printf("權限:(%o)\t", (unsigned int)(state.st_mode & ~S_IFMT));
    printf("Uid:(%ld)\t", (long)state.st_uid);
    printf("Gid:(%ld)\n", (long)state.st_gid);
    printf("最近訪問:%s", ctime(&state.st_atim));
    printf("最近更改:%s", ctime(&state.st_ctim));
    printf("最近改動:%s", ctime(&state.st_mtim));
    printf("創建時間:-");
    printf("\n");
}
  • 測試代碼,mystat 與stat(1)對比,提交截圖
    技術分享圖片

Linux下IPC機制

進程間通信(IPC,Inter-Process Communication)指至少兩個進程或線程間傳送數據或信號的一些技術或方法。

共享內存

共享內存允許兩個或多個進程共享一定的存儲區,因為不需要拷貝數據,所以這是最快的一種IPC。

  • 原理:

    共享內存是在多個進程之間共享內存區域的一種進程間的通信方式,由IPC為進程創建的一個特殊地址範圍,它將出現在該進程的地址空間中。其他進程可以將同一段共享內存連接到自己的地址空間中。所有進程都可以訪問共享內存中的地址,就好像它們是malloc分配的一樣。如果一個進程向共享內存中寫入了數據,所做的改動將立刻被其他進程看到。
    • 內存頭文件

      #include <sys/types.h>   
      #include <sys/stat.h>  
      #include <sys/shm.h>  
    • 結構shmid_ds結構體(類似msgid_ds結構體)

    strcut shmid_ds{  
        struct ipc_perm    shm_perm;  
        size_t    shm_segsz;  
        time_t    shm_atime;  
        time_t    shm_dtime;  
        ......  
    }  
    • 共享內存函數定義
    int shmget(key_t key,size_t size,int shmflg);  //shmget函數用來創建一個新的共享內存段, 或者訪問一個現有的共享內存段(不同進程只要key值相同即可訪問同一共享內存段)。第一個參數key是ftok生成的鍵值,第二個參數size為共享內存的大小,第三個參數sem_flags是打開共享內存的方式
    eg.int shmid = shmget(key, 1024, IPC_CREATE | IPC_EXCL | 0666);//第三個參數參考消息隊列int msgget(key_t key,int msgflag); 
    void *shmat(int shm_id,const void *shm_addr,int shmflg); //shmat函數通過shm_id將共享內存連接到進程的地址空間中。第二個參數可以由用戶指定共享內存映射到進程空間的地址,shm_addr如果為0,則由內核試著查找一個未映射的區域。返回值為共享內存映射的地址  
    eg.char *shms = (char *)shmat(shmid, 0, 0);//shmid由shmget獲得  
    int shmdt(const void *shm_addr) //shmdt函數將共享內存從當前進程中分離。 參數為共享內存映射的地址。  
    eg.shmdt(shms)
    int shmctl(int shm_id,int cmd,struct shmid_ds *buf);//shmctl函數是控制函數,使用方法和消息隊列msgctl()函數調用完全類似。參數一shm_id是共享內存的句柄,cmd是向共享內存發送的命令,最後一個參數buf是向共享內存發送命令的參數。 
  • 特點
    • 共享內存允許兩個或多個進程共享一定的存儲區,因為不需要拷貝數據,所以這是最快的一種IPC
    • 共享內存本身並沒有同步機制,需要程序員使用諸如信號量等手段進行同步控制,增加了其復雜性
  • 示例
    • 寫進程
      • 代碼
        ```

        include

        include

        include <sys/shm.h>

        include

        include

        include <sys/types.h>

        include <sys/ipc.h>

        include

      define BUF_SIZE 4096

      int main()
      {
      void *shm_addr = NULL;
      char buffer[BUF_SIZE];

      int shmid;
      // 使用約定的鍵值創建共享內存
      shmid = shmget((key_t) 1234,  BUF_SIZE, 0666 | IPC_CREAT);
      printf("shmid : %u\n", shmid);
      if (shmid < 0)
      {
          perror("shmget error!");
          exit(1);
      }
      
      // 將共享內存附加到本進程
      shm_addr = shmat(shmid, NULL, 0);
      if (shm_addr == (void *) -1)
      {
          perror("shmat error!");
          exit(1);
      }
      
      // 寫入數據
      bzero(buffer, BUF_SIZE);
      sprintf(buffer, "Hello, My PID is %u\n", (unsigned int) getpid());
      printf("send data: %s\n", buffer);
      
      memcpy(shm_addr, buffer, strlen(buffer));
      
      sleep(5);
      
      // 分離
      if (shmdt(shm_addr) == -1)
      {
          printf("shmdt error!\n");
          exit(1);
      }

      }
      ``- 運行結果![](https://images2018.cnblogs.com/blog/1043723/201711/1043723-20171126165304640-412383960.png) -ipcs -m`命令查看系統中的確存在標識符為15466507的共享內存區域。寫進程已經跟共享內存分離,所以狀態連接數為0技術分享圖片

    • 讀進程
      • 代碼
      #include <stdio.h>
      #include <stdlib.h>
      #include <sys/shm.h>
      #include <sys/ipc.h>
      #include <sys/types.h>
      #include <unistd.h>
      #include <string.h>
      #include <errno.h>
      
      #define BUF_SIZE 4096
      
      int main()
      {
          void *shm_addr = NULL;
      
          int shmid;
          // 使用約定的鍵值打開共享內存
          shmid = shmget((key_t) 1234, BUF_SIZE, IPC_CREAT);
          printf("shmid : %u\n", shmid);
          if (shmid == -1)
          {
              perror("shmget error!");
              exit(1);
          }
      
          // 將共享內存附加到本進程
          shm_addr = shmat(shmid, NULL, 0);
          if (shm_addr == (void *) -1)
          {
              perror("shmat error!");
              exit(1);
          }
      
          // 讀取數據
          char tmp[BUF_SIZE];
          bzero(tmp, BUF_SIZE);
          memcpy(tmp, shm_addr, BUF_SIZE);
          printf("read from shared memory: %s\n", tmp);
      
          sleep(5);
      
          // 分離
          if (shmdt(shm_addr) == -1)
          {
              printf("shmdt error!\n");
              exit(1);
          }
      
          // 刪除共享內存
          if (shmctl(shmid, IPC_RMID, 0) == -1)
          {
              printf("shmctl error!\n");
              exit(1);
          }
      }
      • 運行結果技術分享圖片
      • ipcs -m查看,沒有15466507的進程,因為讀進程執行完畢後刪除了共享內存區域

管道

在Linux系統中,我們經常通過符號“|”來使用管道,用以連接兩個或多個命令。實際上,管道是進程與進程間的數據流通道,它使得數據可以以一種“流”的形式在進程間流動。管道也是Unix/Linux系統中一種最常見的進程間通信方式,它在兩個通信進程之間實現一個數據流的通道從而進行信息傳遞。

  • 原理
    • 在兩個程序之間傳遞數據的最簡單的方法是使用popen()和pclose()函數

      #include <stdio.h>
      FILE *popen(const char *command, const char *open_mode);
      int pclose(FILE *stream);
      popen()函數首先調用一個shell,然後把command作為參數傳遞給shell。這樣每次調用popen()函數都需要啟動兩個進程;但是由於在Linux中,所(parameter expansion)都是由shell執行的,這樣command中包含的所有參數擴展都可以在command程序啟動之前完成。
    • pipe()函數

      #include <sys/types.h>
      #include <sys/stat.h>
      int mkfifo(const char *fifo_name, mode_t mode);
      popen()函數只能返回一個管道描述符,並且返回的是文件流(file stream),可以使用函數fread()和fwrite()來訪問。pipe()函數可以返回兩個管道描述符:pipefd[0]和pipefd[1],任何寫入pipefd[1]的數據都可pipefd[0]讀回;pipe()函數返回的是文件描述符(file descriptor),因此只能使用底層的read()和write()系統調用來訪問。pipe()函數通常用來實現父子進程之間的通信。
  • 特點
    • 只支持單向數據流
    • 只能用於具有親緣關系的進程之間;
    • 沒有名字
    • 管道的緩沖區是有限的(管道制存在於內存中,在管道創建時,為緩沖區分配一個頁面大小)
    • 管道所傳送的是無格式字節流,這就要求管道的讀出方和寫入方必須事先約定好數據的格式,比如多少字節算作一個消息(或命令、或記錄)等等
  • 示例: 構造父子進程間任意方向的的數據通道
    • 代碼
    • 運行結果

FIFO

  • 原理
  • 特點
  • 示例

信號

  • 原理
  • 特點
  • 示例

消息隊列

  • 原理
  • 特點
  • 示例

參考

  • 【Linux進程間通信】-共享內存
  • linux基礎——linux進程間通信(IPC)機制總結

20155212 2017-2018-1 《信息安全系統設計》第10周學習總結