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

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結構體html

    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是用特徵位來表示文件類型的,特徵位的定義以下:node

    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的值與文件類型的位遮罩相與,再比較。linux

  • stat結構體中不少變量的類型都是不經常使用的,不能直接輸出該類型,因此使用grep -r *查找同名變量的類型。以存儲大小的變量st_size爲例,發現不少使用的long long類型git

  • 僞代碼程序員

input path;
struct state;
stat(path,state);
print(state);
#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)指至少兩個進程或線程間傳送數據或信號的一些技術或方法。shell

共享內存

共享內存容許兩個或多個進程共享必定的存儲區,由於不須要拷貝數據,因此這是最快的一種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 <stdlib.h>
      #include <stdio.h>
      #include <sys/shm.h>
      #include <unistd.h>
      #include <string.h>
      #include <sys/types.h>
      #include <sys/ipc.h>
      #include <errno.h>
      
      #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);
          }
      }
      • 運行結果
      • 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()函數socket

      #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()函數一般用來實現父子進程之間的通訊。
  • 特色
    • 只支持單向數據流
    • 只能用於具備親緣關係的進程之間;
    • 沒有名字
    • 管道的緩衝區是有限的(管道制存在於內存中,在管道建立時,爲緩衝區分配一個頁面大小)
    • 管道所傳送的是無格式字節流,這就要求管道的讀出方和寫入方必須事先約定好數據的格式,好比多少字節算做一個消息(或命令、或記錄)等等
  • 示例: 構造父子進程間任意方向的的數據通道
    • 代碼

      #include <unistd.h>
      #include <stdio.h>
      #include <stdlib.h>
      #include <string.h>
      #include <sys/types.h>
      
      #define BUF_SIZE 256
      
      int main()
      {
          int fd[2];
          char data[] = "Hello, I am parent!";
          char buf[BUF_SIZE];
          pid_t pid;
      
          if (pipe(fd) < 0)
          {
              printf("pipe error!\n");
              exit(1);
          }
      
          pid = fork();
          if (pid < 0)
          {
              printf("pipe error!\n");
              exit(1);
          }
          else if (pid == 0)
          {
              close(fd[1]);
              int len = read(fd[0], buf, sizeof(buf));
              printf("child: %s\n", buf);
          }
          else 
          {
              close(fd[0]);
              write(fd[1], data, strlen(data));
              printf("parent: %s\n", data);
              sleep(1);
          }
      }
    • 運行結果
    • 利用管道在父進程到子進程的通訊,而管道在兄弟進程間通訊則須要在父進程調用兩次fork函數建立兩個子進程,在兩個兄弟子進程中維護通訊管道

FIFO

管道應用的一個重大限制是它沒有名字,所以,只能用於具備親緣關係的進程間通訊,在有名管道(named pipe或FIFO)提出後,該限制獲得了克服。

  • 原理

    FIFO不一樣於管道之處在於它提供一個路徑名與之關聯,以FIFO的文件形式存在於文件系統中。這樣,即便與FIFO的建立進程不存在親緣關係的進程,只要能夠訪問該路徑,就可以彼此經過FIFO相互通訊(可以訪問該路徑的進程以及FIFO的建立進程之間),所以,經過FIFO不相關的進程也能交換數據。值得注意的是,FIFO嚴格遵循先進先出(first in first out),對管道及FIFO的讀老是從開始處返回數據,對它們的寫則把數據添加到末尾。它們不支持諸如lseek()等文件定位操做。
    • 有名管道的建立

      #include <sys/types.h>
      #include <sys/stat.h>
      int mkfifo(const char * pathname, mode_t mode)
    • 具體操做方法只要建立了一個命名管道而後就可使用open、read、write等系統調用來操做。建立能夠手工建立或者程序中建立

    int mknod(const char *path, mode_t mode, dev_t dev); //第一個參數表示你要建立的文件的名稱,第二個參數表示文件類型,第三個參數表示該文件對應的設備文件的設備號。只有當文件類型爲 S_IFCHR 或 S_IFBLK 的時候該文件纔有設備號,建立普通文件時傳入0便可
  • 特色
    • 匿名管道只能用於具備親緣關係的兩個進程間的通訊,而命名管道能夠用於任何兩個進程間的通訊,要靈活方便許多
    • 命名管道雖然克服了匿名管道只能在具備親緣關係的兩個進程間使用的限制,可是並未克服匿名管道只能半雙工通訊的缺點。也就是說命名管道一般也是一種半雙工通訊方式
    • 命名管道是一種特殊的文件類型,並且存在於文件系統中。因此,當通訊進程使用完命名管道後,若是沒有對其進行刪除該文件依然存在。而匿名管道只存在於內存中,沒法在文件系統中查看
  • 示例
    • 命名管道的建立
      • 代碼

        #include <sys/types.h>
        #include <sys/stat.h>
        #include <errno.h>
        #include <stdio.h>
        #include <stdlib.h>
        
        int main()
        {
            char *pathname = "myfifo";
            if (mkfifo(pathname, 0666) < 0)
            {
                perror("mkfifo error\n");
                exit(1);
            }
            else 
            {
                printf("create a FIFO(name : %s)\n", pathname);
            }
        }
      • 運行結果
      • 使用ls -l命令查看

    • 命名管道的寫操做
      • 代碼
      #include <sys/types.h>
      #include <sys/stat.h>
      #include <unistd.h>
      #include <fcntl.h>
      #include <stdlib.h>
      #include <stdio.h>
      #include <errno.h>
      #include <string.h>
      
      #define BUF_SIZE 256
      
      int main()
      {
          char buf[BUF_SIZE];
          int fd;
          int i;
      
          fd = open("myfifo", O_WRONLY);
          if (fd < 0)
          {
              perror("open error!");
              exit(1);
          }   
          printf("open success\n");
      
          for (i = 0; i < 10; i++)
          {
              bzero(buf, BUF_SIZE);
              int len = sprintf(buf, "write process: this is %dth message!", i);
              if (write(fd, buf, len) < 0)
              {
                  perror("write error!");
                  close(fd);
                  exit(1);
              }
              printf("write to fifo: %s\n", buf);
              sleep(2);// 休眠2秒便於觀察
          }
      
          close(fd);
      }
      • 運行結果
    • 命名管道讀進程
      • 代碼

        #include <sys/types.h>
        #include <sys/stat.h>
        #include <unistd.h>
        #include <fcntl.h>
        #include <errno.h>
        #include <string.h>
        #include <stdio.h>
        #include <stdlib.h>
        
        #define BUF_SIZE 512
        
        int main()
        {
            char buf[BUF_SIZE];
            int fd;
            int len;
        
            fd = open("myfifo", O_RDONLY);
            if (fd < 0)
            {
                perror("open error!");
                exit(1);
            }
        
            printf("open success\n");
            bzero(buf, BUF_SIZE);
            while ((len = read(fd, buf, BUF_SIZE)) > 0)
            {
                printf("read from fifo: %s\n", buf);
            }
            close(fd);
        }
      • 運行結果
    • 當一個進程爲「寫」打開命名管道時,若是沒有別的進程爲「讀」打開就一直阻塞到有其餘進程爲「讀」打開。爲了更清楚地瞭解這一過程,咱們先運行寫進程。這時並無「open success」輸出,寫進程一直阻塞在open函數。接下來咱們運行讀進程,能夠看到寫進程輸出「open success」,open操做返回。

信號

信號機制是unix系統中最爲古老的進程之間的通訊機制,用於一個或幾個進程之間傳遞異步信號。信號能夠有各類異步事件產生,好比鍵盤中斷等。shell也可使用信號將做業控制命令傳遞給它的子進程。

  • 原理
    • 註冊信號處理函數

      #include <signal.h>
       /*typedef void (*sighandler_t)(int);  sighandler_t signal(int signum,sighandler_t handler);*/
       * void (*signal(int signum, void (*handler)(int)))(int);  //SIG_IGN && SIG_DFL
       * int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);
    • 發送信號

      #include <signal.h>
      * int kill(pid_t pid,int sig); //#include <sys/types.h>
      * int raise(int sig);            //kill(getpid(),sig);
      * unsigned int alarm(unsigned int seconds); //(#include <unistd.h>) seconds秒後,向進程自己發送SIGALRM信號。
    • 信號集

      typedef struct {unsigned long sig[_NSIG_WORDS];} sigset_t;
      * int sigaddset(sigset_t *set,int sig);
      * int sigemptyset(sigset_t *set);
  • 特色
    • 信號是惟一的異步通訊方式
  • 示例
    • MTK8685平臺有mtk的 thermal管理,實時監控平臺的溫度,當主芯片的溫度太高時,會提示用戶cooldown 即重啓盒子降溫,以避免損壞! 溫度監控系統也是採用信號的方式實現的。thermald.c 首先設置響應的信號,並設置signal_handler,最終會將此進程的pid經過proc文件系統傳遞給內核! 內核的驅動檢測到溫度太高時,會將信號發送給指定的用戶態程序,由用戶決定下一步的操做!
    • 代碼

      #include <stdio.h>
      #include <stdlib.h>
      #include <string.h>
      #include <signal.h>
      #include <errno.h>
      #include <sys/file.h>
      #include <sys/wait.h>
      #include <sys/stat.h>
      #include <cutils/xlog.h>
      #include <sys/types.h>
      
      static int debug_on = 0;
      
      #define TM_LOG_TAG "thermald"
      #define TM_DBG_LOG(_fmt_, args...) \
          do { \
              if (1 == debug_on) { \
                  sxlog_printf(ANDROID_LOG_INFO, TM_LOG_TAG, _fmt_, ##args); \
              } \
          } while(0)
      
      #define TM_INFO_LOG(_fmt_, args...) \
          do { sxlog_printf(ANDROID_LOG_INFO, TM_LOG_TAG, _fmt_, ##args); } while(0)
      
      #define PROCFS_MTK_CL_SD_PID "/proc/driver/mtk_cl_sd_pid"
      
      static void signal_handler(int signo, siginfo_t *si, void *uc)
      {
          switch(si->si_signo) {
              // Add more signo or code to expand thermald
              case SIGIO:
                  if(1 == si->si_code) {
                  //待收到高溫警告後,啓動提示對話框
                      system("am start com.mediatek.thermalmanager/.ShutDownAlertDialogActivity");
                      TM_INFO_LOG("thermal shutdown signal received, si_signo=%d, si_code=%d\n", si->si_signo, si->si_code);
                  }
              break;
              default:
                  TM_INFO_LOG("what!!!\n");
              break;
          }
      }
      
      int main(int argc, char *argv[])
      {
          int fd = open(PROCFS_MTK_CL_SD_PID, O_RDWR);
          int pid = getpid();
          int ret = 0;
          char pid_string[32] = {0};
      
          struct sigaction act;
      
          TM_INFO_LOG("START+++++++++ %d", getpid());
      
          /* Create signal handler */
          memset(&act, 0, sizeof(act));
          act.sa_flags = SA_SIGINFO;//發送額外的信息給signal_handler
          //act.sa_handler = signal_handler;
          act.sa_sigaction = signal_handler;
          sigemptyset(&act.sa_mask);
      
          sigaction(SIGIO, &act, NULL);
      
          /* Write pid to procfs */
          sprintf(pid_string, "%d", pid);
      
          ret = write(fd, pid_string, sizeof(char) * strlen(pid_string)); //將當前的進程pid寫入proc文件系統,供內核使用
          if (ret <= 0)   {
              TM_INFO_LOG("Fail to write %d to %s %x\n", pid, PROCFS_MTK_CL_SD_PID, ret);
          } else {
              TM_INFO_LOG("Success to write %d to %s\n", pid, PROCFS_MTK_CL_SD_PID);
          }
          close(fd);
      
          TM_INFO_LOG("Enter infinite loop");
      
          while(1) {
              sleep(100);
          }
      
          TM_INFO_LOG("END-----------");
      
          return 0;
      }

消息隊列

消息隊列是由內核維護的一種鏈式結構。鏈表中每個記錄又稱做消息,消息具備特定的格式和優先級別

  • 原理

    各個進程經過消息隊列標識符來引用消息隊列,這樣,寫進程就能夠按照必定的規則添加新的消息,讀進程能夠按必定的規則取走消息(具體按什麼規則咱們稍後討論)。和前面介紹的共享內存和信號量同樣,消息隊列是隨內核持續的,也就是說咱們使用完畢後須要顯式刪除消息隊列。
    • 每個消息隊列都一個msqid_ds結構與之關聯。用戶可使用該結構來設置或獲取消息隊列的相關信息

      struct msqid_ds {
           struct ipc_perm msg_perm;
           struct msg *msg_first;      /* first message on queue,unused  */
           struct msg *msg_last;       /* last message in queue,unused */
           __kernel_time_t msg_stime;  /* last msgsnd time */
           __kernel_time_t msg_rtime;  /* last msgrcv time */
           __kernel_time_t msg_ctime;  /* last change time */
           unsigned long  msg_lcbytes; /* Reuse junk fields for 32 bit */
           unsigned long  msg_lqbytes; /* ditto */
           unsigned short msg_cbytes;  /* current number of bytes on queue */
           unsigned short msg_qnum;    /* number of messages in queue */
           unsigned short msg_qbytes;  /* max number of bytes on queue */
           __kernel_ipc_pid_t msg_lspid;   /* pid of last msgsnd */
           __kernel_ipc_pid_t msg_lrpid;   /* last receive pid */
       };
    • 使用msgget函數來建立或打開一個消息隊列

      #include <sys/types.h>
      #include <sys/ipc.h>
      #include <sys/msg.h>
      
      int msgget(key_t key, int msgflg);
    • 消息隊列的寫操做就是往消息隊列中發送數據(消息),主要經過msgsnd函數來執行寫操做

      #include <sys/types.h>
      #include <sys/ipc.h>
      #include <sys/msg.h>
      
      int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
    • 消息隊列的讀操做是指從消息隊列中讀取消息。讀操做的通常過程是:首先聲明一個msgbuf類型的消息,而後調用msgrcv函數把消息讀入該緩衝區

      #include <sys/types.h>
      #include <sys/ipc.h>
      #include <sys/msg.h>
      ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
    • 咱們可使用smgctl函數來得到或設置消息隊列的屬性

      #include <sys/types.h>
      #include <sys/ipc.h>
      #include <sys/msg.h>
      int msgctl(int msqid, int cmd, struct msqid_ds *buf);
  • 特色
    • 消息隊列克服了信號承載信息量少,管道只能承載無格式字符流
    • 消息隊列也能夠獨立於發送和接收進程而存在,從而消除了在同步命名管道的打開和關閉時可能產生的困難
    • 同時經過發送消息還能夠避免命名管道的同步和阻塞問題,不須要由進程本身來提供同步方法
    • 接收程序能夠經過消息類型有選擇地接收數據,而不是像命名管道中那樣,只能默認地接收
    • 消息隊列進行通訊的進程能夠是不相關的進程,同時它們都是經過發送和接收的方式來傳遞數據的
    • 對每一個數據都有一個最大長度的限制
  • 示例(先運行寫進程,再運行讀進程,兩者順序不一樣結果也會不一樣)
    • 寫進程
      • 代碼
      #include <unistd.h>
      #include <sys/msg.h>
      #include <errno.h>
      #include <stdio.h>
      #include <stdlib.h>
      #include <sys/types.h>
      #include <string.h>
      
      #define MSG_SIZE 512
      
      struct msgbuf
      {
          long msgtype;
          char msgtext[MSG_SIZE];
      };
      
      int main()
      {
          int msgid;
      
          // 建立或打開消息隊列
          msgid = msgget((key_t) 2345, IPC_CREAT | 666);
          if (msgid == -1)
          {
              perror("msgget errno!");
              exit(1);
          }
      
          // 發送消息
          struct msgbuf data;
          data.msgtype = 1;
          bzero(&data.msgtext, MSG_SIZE);
          sprintf(data.msgtext, "Hello, I am msg_send.c, my pid is %u\n", getpid());
          if (msgsnd(msgid, (void *) &data, MSG_SIZE, 0) == -1)
          {
              perror("msgsnd error!");
              exit(1);
          }
      
          printf("send msg: %s\n", data.msgtext);
      
          sleep(5);
      }
      • 運行結果
    • 讀進程
      • 代碼
      #include <unistd.h>
      #include <sys/msg.h>
      #include <sys/types.h>
      #include <stdlib.h>
      #include <stdio.h>
      #include <errno.h>
      #include <string.h>
      
      #define MSG_SIZE 512
      
      struct msgbuf
      {
          long msgtype;
          char msgtext[MSG_SIZE];
      };
      
      int main()
      {
          int msgid;
      
          // 建立或打開消息隊列
          msgid = msgget((key_t) 2345, IPC_CREAT | 666);
          if (msgid == -1)
          {
              perror("msgget error!");
              exit(1);
          }
      
          // 讀取消息
          struct msgbuf data;
          bzero(data.msgtext, MSG_SIZE);
          long type = 0;
          if (msgrcv(msgid, (void *) &data, MSG_SIZE, type, 0) == -1)
          {
              perror("msgrcv error!");
              exit(1);
          }
          printf("msg_rend.c read from msg queue: %s\n", data.msgtext);
      
          sleep(5);
      
          // 刪除消息隊列
          if (msgctl(msgid, IPC_RMID, 0) == -1)
          {
              perror("msgctl error!");
              exit(1);
          }
      };
      • 運行結果

參考

相關文章
相關標籤/搜索