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结构体

    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);
#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'
", argv[1]);
    printf("  大小:%lld	", (long long)state.st_size);
    printf("块:%lld	", (long long)state.st_blocks);
    printf("IO块:%ld	", (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("
");

    printf("设备:%xh/%ldd	", (long)state.st_dev, (long)state.st_dev);
    printf("Inode:%ld	", (long)state.st_ino);
    printf("硬链接:%ld
", (long)state.st_nlink);
    printf("权限:(%o)	", (unsigned int)(state.st_mode & ~S_IFMT));
    printf("Uid:(%ld)	", (long)state.st_uid);
    printf("Gid:(%ld)
", (long)state.st_gid);
    printf("最近访问:%s", ctime(&state.st_atim));
    printf("最近更改:%s", ctime(&state.st_ctim));
    printf("最近改动:%s", ctime(&state.st_mtim));
    printf("创建时间:-");
    printf("
");
}

  • 测试代码,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 <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
      ", 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
      ", (unsigned int) getpid());
          printf("send data: %s
      ", buffer);
      
          memcpy(shm_addr, buffer, strlen(buffer));
      
          sleep(5);
      
          // 分离
          if (shmdt(shm_addr) == -1)
          {
              printf("shmdt error!
      ");
              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
      ", 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
      ", tmp);
      
          sleep(5);
      
          // 分离
          if (shmdt(shm_addr) == -1)
          {
              printf("shmdt error!
      ");
              exit(1);
          }
      
          // 删除共享内存
          if (shmctl(shmid, IPC_RMID, 0) == -1)
          {
              printf("shmctl error!
      ");
              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()函数通常用来实现父子进程之间的通信。

  • 特点
    • 只支持单向数据流
    • 只能用于具有亲缘关系的进程之间;
    • 没有名字
    • 管道的缓冲区是有限的(管道制存在于内存中,在管道创建时,为缓冲区分配一个页面大小)
    • 管道所传送的是无格式字节流,这就要求管道的读出方和写入方必须事先约定好数据的格式,比如多少字节算作一个消息(或命令、或记录)等等
  • 示例: 构造父子进程间任意方向的的数据通道
    • 代码

      #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!
      ");
              exit(1);
          }
      
          pid = fork();
          if (pid < 0)
          {
              printf("pipe error!
      ");
              exit(1);
          }
          else if (pid == 0)
          {
              close(fd[1]);
              int len = read(fd[0], buf, sizeof(buf));
              printf("child: %s
      ", buf);
          }
          else 
          {
              close(fd[0]);
              write(fd[1], data, strlen(data));
              printf("parent: %s
      ", 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
        ");
                exit(1);
            }
            else 
            {
                printf("create a FIFO(name : %s)
        ", 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
      ");
      
          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
      ", 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
        ");
            bzero(buf, BUF_SIZE);
            while ((len = read(fd, buf, BUF_SIZE)) > 0)
            {
                printf("read from fifo: %s
        ", 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
      ", si->si_signo, si->si_code);
                  }
              break;
              default:
                  TM_INFO_LOG("what!!!
      ");
              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
      ", pid, PROCFS_MTK_CL_SD_PID, ret);
          } else {
              TM_INFO_LOG("Success to write %d to %s
      ", 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
      ", getpid());
          if (msgsnd(msgid, (void *) &data, MSG_SIZE, 0) == -1)
          {
              perror("msgsnd error!");
              exit(1);
          }
      
          printf("send msg: %s
      ", 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
      ", data.msgtext);
      
          sleep(5);
      
          // 删除消息队列
          if (msgctl(msgid, IPC_RMID, 0) == -1)
          {
              perror("msgctl error!");
              exit(1);
          }
      };
      
      • 运行结果

参考

原文地址:https://www.cnblogs.com/dky20155212/p/7900371.html