c_cpp 内存cgroup中的内存使用情况监视

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了c_cpp 内存cgroup中的内存使用情况监视相关的知识,希望对你有一定的参考价值。

#define __GNU_SOURCE
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <sys/epoll.h>
#include <sys/eventfd.h>
#include <sys/types.h>

static ssize_t
cgroup_memory_limit(void);
static size_t
compute_threshold(size_t limit, double pct);
static ssize_t
current_memory_usage(void);

#define KB (1 * 1024)
#define MB (KB * KB)

#define MAX_EVENTS                                                             \
  5 /* Maximum number of events to be returned from                            \
       a single epoll_wait() call */

#define errExit(msg)                                                           \
  do {                                                                         \
    perror(msg);                                                               \
    exit(EXIT_FAILURE);                                                        \
  } while (0)

#define checkCall(num, msg)                                                    \
  if (num == -1) {                                                             \
    errExit(msg);                                                              \
  }

static char* levels[] = {
  [0] = "yellow",
  [1] = "orange",
  [2] = "red",
};

typedef struct
{
  int low, mid, high;
} limits_t;

typedef struct
{
  int efd; /* event fd */
  int fd;  /* file fd */
  unsigned short level;
  size_t lim;
} cg_ctrl_t;

int
main(int argc, char* argv[])
{
  int epfd, ready, c, j, numOpenFds;
  struct epoll_event ev;
  struct epoll_event evlist[MAX_EVENTS];

  limits_t args = {
    .low = MB * 1, // these values are fallback essentially
    .mid = MB * 3,
    .high = MB * 4,
  };

  if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-h") == 0) {
    fprintf(
      stderr,
      "Usage: %s -l <low pct> -m <mid pct> -h <high pct> " \
      "i.e.: %s -l 0.3 -m 0.5 -h 0.8\n",
      argv[0], argv[0]);
    exit(2);
  }

  while ((c = getopt(argc, argv, "h:l:m:")) != -1)
    switch (c) {
      case 'h':
        args.high =
          compute_threshold(cgroup_memory_limit(), strtod(optarg, NULL));
        break;
      case 'l':
        args.low =
          compute_threshold(cgroup_memory_limit(), strtod(optarg, NULL));
        break;
      case 'm':
        args.mid =
          compute_threshold(cgroup_memory_limit(), strtod(optarg, NULL));
        break;
      case '?':
        if (optopt == 'c')
          fprintf(stderr, "Option -%c requires an argument.\n", optopt);
        else if (isprint(optopt))
          fprintf(stderr, "Unknown option `-%c'.\n", optopt);
        else
          fprintf(stderr, "Unknown option character `\\x%x'.\n", optopt);
        return 1;
      default:
        abort();
    }

    assert(args.low < args.mid);
    assert(args.mid < args.high);

  checkCall((epfd = epoll_create(3)), "epoll_create() failed");

  pid_t pid = getpid();
  int tasks_fd;
  char pid_str[10] = { 0 };
  snprintf(pid_str, 10, "%d", pid);

#define USAGE_IN_BYTES "/sys/fs/cgroup/memory/0/memory.usage_in_bytes"
#define CG_EVENT_CTL "/sys/fs/cgroup/memory/0/cgroup.event_control"
#define TASKS "/sys/fs/cgroup/memory/0/tasks"

  tasks_fd = open(TASKS, O_RDWR);
  checkCall(tasks_fd, "open() failed");
  checkCall(write(tasks_fd, pid_str, 10), "write() failed");
  checkCall(close(tasks_fd), "close() failed");

  int usage_fd = open(USAGE_IN_BYTES, O_RDONLY);
  checkCall(usage_fd, "open failed()");

  int cgcntl_fd = open(CG_EVENT_CTL, O_WRONLY);
  checkCall(cgcntl_fd, "open failed()");

  char control_msg[16] = { 0 }; // Store string for initiating monitoring

  // Limits is used to setup watchpoints with cgroup.
  int limits[3] = {
    [0] = args.low,
    [1] = args.mid,
    [2] = args.high,
  };
  cg_ctrl_t watches[3] = { 0 };

  for (size_t i = 0; i < 3; i++) {
    checkCall((watches[i].efd = eventfd(0, 0)), "eventfd() failed");

    watches[i].lim = (size_t)(limits[i]);
    watches[i].level = (unsigned short)i;
    snprintf(
      control_msg, 16, "%d %d %zu", watches[i].efd, usage_fd, watches[i].lim);
    checkCall(write(cgcntl_fd, control_msg, 16), "write() failed");

    ev.events = EPOLLIN;
    ev.data.fd = watches[i].efd;
    if (epoll_ctl(epfd, EPOLL_CTL_ADD, watches[i].efd, &ev) == -1)
      errExit("epoll_ctl");
  }

  checkCall(close(cgcntl_fd), "close() failed");

  numOpenFds = 3;

#define LEAK_512K (2 << 18)
  while (numOpenFds > 0) {
    /* Fetch up to MAX_EVENTS items from the ready list of the
       epoll instance with a 1 second timeout */

    ready = epoll_wait(epfd, evlist, MAX_EVENTS, 1000);
    if (ready == -1) {
      if (errno == EINTR)
        continue; /* Restart if interrupted by signal */
      else
        errExit("epoll_wait()");
    }

    printf("Leaking...\n");
    void* p = malloc(LEAK_512K);
    if (!p) {
      errExit("malloc()");
    } else {
      memset(p, 0, LEAK_512K);
    }

    /* Deal with returned list of events */

    for (j = 0; j < ready; j++) {
      if (evlist[j].events & EPOLLIN) {
        eventfd_t value;
        checkCall(eventfd_read(evlist[j].data.fd, &value),
                  "eventfd_read() failed");
        assert(value == 1);
        ssize_t used;
        checkCall((used = current_memory_usage()),
                  "current_memory_usage() failed");
        for (size_t i = 0; i < 3; i++) {
          if (watches[i].efd == evlist[j].data.fd) {
            printf("limit reached (fd=%d) | %zu => %s | exceeded by: %zd\n",
                   evlist[j].data.fd,
                   watches[i].lim,
                   levels[watches[i].level],
                   used - watches[i].lim);
          }
        }
      } else if (evlist[j].events & (EPOLLHUP | EPOLLERR)) {
        printf("closing fd %d\n", evlist[j].data.fd);
        if (close(evlist[j].data.fd) == -1)
          errExit("close");
        numOpenFds--;
      }
    }
  }

  printf("All file descriptors closed; bye\n");
  exit(EXIT_SUCCESS);
}

static ssize_t
cgroup_memory_limit(void)
{
#define MEMORY_LIMIT_IN_BYTES "/sys/fs/cgroup/memory/0/memory.limit_in_bytes"
  FILE* fp = fopen(MEMORY_LIMIT_IN_BYTES, "rb");
  if (!fp) {
    return -1;
  }
  uint64_t n;
  fscanf(fp, "%ld", &n);
  checkCall(fclose(fp), "fclose() failed");
  return n;
}

static size_t
compute_threshold(size_t limit, double pct)
{
  assert(pct < 1.);
  return (double)limit * pct;
}

static ssize_t
current_memory_usage(void)
{
#define MEMORY_USAGE_IN_BYTES "/sys/fs/cgroup/memory/0/memory.usage_in_bytes"
  FILE* fp = fopen(MEMORY_USAGE_IN_BYTES, "rb");
  if (!fp) {
    return -1;
  }
  uint64_t n = 0;
  assert(fscanf(fp, "%ld", &n) > 0);
  checkCall(fclose(fp), "fclose() failed");
  return n;
}

以上是关于c_cpp 内存cgroup中的内存使用情况监视的主要内容,如果未能解决你的问题,请参考以下文章

监视 R 中的内存使用情况

用于监视Linux上的内存使用情况的Bash脚本

Ubuntu下限制进程使用内存资源的方法(cgroup)

重学容器31: 容器资源限制之限制容器的内存

cgroup限制内存

python 在python脚本中监视内存使用情况