Назад | Перейти на главную страницу

max открытые файлы - почему нет ошибки?

Я подозреваю, что одно из наших серверных приложений исчерпало максимальное количество открытых файлов.

Приложение работает в пользовательском пространстве под собственной учетной записью. Сценарий инициализации запускает большое количество процессов, которые, в свою очередь, запускают ряд подпроцессов и большое количество потоков.

Согласно книге, которую я установил /etc/security/limits.conf:

USERNAME - nofile 2048

Я подозреваю, что приложение исчерпало лимит - просмотрев каталог временных файлов, я обнаружил там более 2000 файлов.

После увеличения лимита до 4096 и перезапуска приложения я обнаружил там более 2100 файлов.

Теперь вопрос: если приложение достигло лимита 2048 - почему это не было зарегистрировано в / var / log / messages?

syslog-ng - это текущий используемый демон syslog.

/etc/syslog-ng/syslog-ng.conf

options { long_hostnames(off); sync(0); perm(0640); stats(3600); };
source src {
    internal();
    unix-dgram("/dev/log");
    unix-dgram("/var/lib/ntp/dev/log");
};
filter f_iptables   { facility(kern) and match("IN=") and match("OUT="); };
filter f_console    { level(warn) and facility(kern) and not filter(f_iptables)
                      or level(err) and not facility(authpriv); };
filter f_newsnotice { level(notice) and facility(news); };
filter f_newscrit   { level(crit)   and facility(news); };
filter f_newserr    { level(err)    and facility(news); };
filter f_news       { facility(news); };
filter f_mailinfo   { level(info)      and facility(mail); };
filter f_mailwarn   { level(warn)      and facility(mail); };
filter f_mailerr    { level(err, crit) and facility(mail); };
filter f_mail       { facility(mail); };
filter f_cron       { facility(cron); };
filter f_local      { facility(local0, local1, local2, local3,
                           local4, local5, local6, local7); };
filter f_messages   { not facility(news, mail, cron, authpriv, auth) and not filter(f_iptables); };
filter f_warn       { level(warn, err, crit) and not filter(f_iptables); };
filter f_alert      { level(alert); };
filter f_auth       { facility(authpriv, auth); };
destination console  { pipe("/dev/tty10"    group(tty) perm(0620)); };
  log { source(src); filter(f_console); destination(console); };
destination xconsole { pipe("/dev/xconsole" group(tty) perm(0400)); };
  log { source(src); filter(f_console); destination(xconsole); };
destination auth { file("/var/log/auth"); };
  log { source(src); filter(f_auth); destination(auth); };
destination newscrit { file("/var/log/news/news.crit"); };
  log { source(src); filter(f_newscrit); destination(newscrit); };
destination newserr { file("/var/log/news/news.err"); };
  log { source(src); filter(f_newserr); destination(newserr); };
destination newsnotice { file("/var/log/news/news.notice"); };
  log { source(src); filter(f_newsnotice); destination(newserr); };
destination mailinfo { file("/var/log/mail.info"); };
  log { source(src); filter(f_mailinfo); destination(mailinfo); };
destination mailwarn { file("/var/log/mail.warn"); };
   log { source(src); filter(f_mailwarn); destination(mailwarn); };
destination mailerr  { file("/var/log/mail.err" fsync(yes)); };
  log { source(src); filter(f_mailerr);  destination(mailerr); };
destination mail { file("/var/log/mail"); };
  log { source(src); filter(f_mail); destination(mail); };
destination cron { file("/var/log/cron"); };
  log { source(src); filter(f_cron); destination(cron); };
destination localmessages { file("/var/log/localmessages"); };
  log { source(src); filter(f_local); destination(localmessages); };
destination messages { file("/var/log/messages"); };
  log { source(src); filter(f_messages); destination(messages); };
destination firewall { file("/var/log/firewall"); };
  log { source(src); filter(f_iptables); destination(firewall); };
destination warn { file("/var/log/warn" fsync(yes)); };
  log { source(src); filter(f_warn); destination(warn); };

Вам действительно нужно знать, заканчиваются ли у вас файлы.

Запустите свой процесс. Тогда проверьте кота /proc/<pid>/limits и посмотрите, что говорят его пределы.

Затем вы можете получить количество дескрипторов файла, запустив ls -1 /proc/<pid>/fd | wc -l.

Обратите внимание, что у каждого процесса есть свои ограничения (например, дочерние элементы родительского процесса). Однако потоки явно разделяют таблицу дескрипторов файлов вызывающего процесса и, как таковые, делать разделяйте ограничение файла между потоками и вызывающим процессом.

Хотя вы не можете создавать потоки в bash, эту программу можно использовать для демонстрации эффекта.

/* Compiled with gcc -o upcount upcount.c -pthread */
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <string.h>
#include <unistd.h>

#include <err.h>
#include <sysexits.h>
#include <errno.h>

#include <pthread.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/resource.h>

#define THREADS 3
#define NUMCHILD 3
#define DEF_OPEN_LIMIT 256

/* The gimmick in this program is to constantly dup an FD
 * until we run out of file handles */

void dup_fds(
  int basefd)
{
  int i;
  int *fds = calloc(1048576, sizeof(int));
  char etxt[256];
  int me = pthread_self();

  for (i=0; i < 1048576; i++)
    fds[i] = -1;

  for (i=0; i < 1048576; i++) {
    fds[i] = dup(basefd);
    if (fds[i] < 0) {
      strerror_r(errno, etxt, 256);
      fprintf(stderr, "Cannot dup file: %s\n", etxt);
      return;
    }
    usleep(100000 + (rand_r(&me) % 400000));
  }
}

void * run_thread(
  void *data)
{
  /* This procedure should not be independent */
  struct rlimit ofiles;
  int i;
  i = pthread_self();

  /* Obtain the open files limit */
  if (getrlimit(RLIMIT_NOFILE, &ofiles) < 0) {
    perror("cannot get limits");
    pthread_exit(NULL);
  }

  /* Assign a random value to current limit */
  i = getpid();
  ofiles.rlim_cur = 128 + (rand_r(&i) % 896);

  /* Set the limit */
  if (setrlimit(RLIMIT_NOFILE, &ofiles) < 0) {
    perror("cannot set limits");
    pthread_exit(NULL);
  }


  dup_fds(1);
}


void run_child(
  void)
{
  int i;
  struct rlimit ofiles;
  pthread_t threads[THREADS];

  /* Obtain the open files limit */
  if (getrlimit(RLIMIT_NOFILE, &ofiles) < 0)
    err(EX_OSERR, "Cannot obtain limits");

  /* Assign a random value to current limit */
  i = getpid();
  ofiles.rlim_cur = 128 + (rand_r(&i) % 896);

  /* Set the limit */
  if (setrlimit(RLIMIT_NOFILE, &ofiles) < 0)
    err(EX_OSERR, "Canot set limits");

  /* Create threads */
  for (i=0; i < THREADS; i++) {
    if (pthread_create(&threads[i], NULL, run_thread, NULL))
      err(EX_OSERR, "Cannot spawn thread");
  }

  dup_fds(1);

  for (i=0; i < THREADS; i++)
    if (pthread_join(threads[i], NULL))
      err(EX_OSERR, "Cannot join thread");

  exit(0);
}


int main()
{
  int i, s;
  /* Spawn children */
  for (i=0; i < NUMCHILD; i++) {
    if (fork()) {
      continue;
    }
    run_child();
  }

  for (i=0; i < NUMCHILD; i++) {
    if (wait(&s) < 0)
      warn("wait failed");
  }

  return 0;
}

Эта программа создает 3 потомков с 3 потоками.

$ ./upfilecnt & pstree -p $!
upfilecnt(12662)─┬─upfilecnt(12663)─┬─{upfilecnt}(12666)
                 │                  ├─{upfilecnt}(12667)
                 │                  └─{upfilecnt}(12668)
                 ├─upfilecnt(12664)─┬─{upfilecnt}(12669)
                 │                  ├─{upfilecnt}(12670)
                 │                  └─{upfilecnt}(12671)
                 └─upfilecnt(12665)─┬─{upfilecnt}(12672)
                                    ├─{upfilecnt}(12673)
                                    └─{upfilecnt}(12674)

Каждый дочерний процесс и поток непрерывно создают новый дескриптор файла каждые полсекунды плюс некоторое случайное ожидание.

Вы можете видеть, что из дочерних процессов каждый дочерний процесс имеет независимую таблицу дескрипторов файлов.

$ for i in 1266{3,4,5}; do ls -1 /proc/$i/fd | wc -l; done
637
646
636

Однако потоки этих дочерних процессов имеют такое же количество подсчетов, что и дочерние процессы.

# .. another invokation
$ for i in 134{11,14,15,10,12,13,16,17,18}; do ls -1 /proc/$i/fd | wc -l; done
438
438
438
430
430
430
433
433
433

Также обратите внимание, что дочерние идентификаторы могут иметь независимые ограничения. Эта программа также устанавливает случайный лимит на вызов каждого дочернего элемента.

$ grep -h "Max open" /proc/1420{3,4,5}/limits
Max open files            504                  4096                 files     
Max open files            502                  4096                 files     
Max open files            372                  4096                 files     

А для дополнительного удовольствия он также устанавливает лимит случайных открытых файлов на поток. Но это не прилипает и используется всеми потоками процесса и дочерним процессом.

grep -h "Max open" /proc/1420{3,4,5}/task/*/limits 
Max open files            1011                 4096                 files     
Max open files            1011                 4096                 files     
Max open files            1011                 4096                 files     
Max open files            1011                 4096                 files     
Max open files            1009                 4096                 files     
Max open files            1009                 4096                 files     
Max open files            1009                 4096                 files     
Max open files            1009                 4096                 files     
Max open files            750                  4096                 files     
Max open files            750                  4096                 files     
Max open files            750                  4096                 files     
Max open files            750                  4096                 files  

Вам не хватает этого определения источника:

  # messages from the kernel
  file("/proc/kmsg" program_override("kernel: "));

Тогда все в порядке!