Как узнать, является ли страница с копией при записи реальной копией? - PullRequest
7 голосов
/ 18 декабря 2010

Когда я создаю сопоставление при записи (MAP_PRIVATE) с использованием mmap, некоторые страницы этого сопоставления будут скопированы, как только я напишу на определенные адреса. В определенный момент в моей программе я хотел бы выяснить, какие страницы на самом деле были скопированы. Существует вызов, называемый mincore, но он сообщает только о том, находится страница в памяти или нет, что не совпадает с копируемой страницей или нет.

Есть ли способ выяснить, какие страницы были скопированы?

Ответы [ 6 ]

9 голосов
/ 20 декабря 2010

Хорошо, следуя совету MarkR , я попробовал пройти через интерфейс pagemap и kpageflags.Ниже приведен быстрый тест, чтобы проверить, находится ли страница в памяти «SWAPBACKED», как она называется.Конечно, остается одна проблема, которая заключается в том, что kpageflags доступен только для root.

int main(int argc, char* argv[])
{
  unsigned long long pagesize=getpagesize();
  assert(pagesize>0);
  int pagecount=4;
  int filesize=pagesize*pagecount;
  int fd=open("test.dat", O_RDWR);
  if (fd<=0)
    {
      fd=open("test.dat", O_CREAT|O_RDWR,S_IRUSR|S_IWUSR);
      printf("Created test.dat testfile\n");
    }
  assert(fd);
  int err=ftruncate(fd,filesize);
  assert(!err);

  char* M=(char*)mmap(NULL, filesize, PROT_READ|PROT_WRITE, MAP_PRIVATE,fd,0);
  assert(M!=(char*)-1);
  assert(M);
  printf("Successfully create private mapping\n");

Тестовая установка содержит 4 страницы.страницы 0 и 2 грязные

  strcpy(M,"I feel so dirty\n");
  strcpy(M+pagesize*2,"Christ on crutches\n");

страница 3 считана с.

  char t=M[pagesize*3];

страница 1 не будет доступна

Файл карты страницы отображает процесс, еговиртуальной памяти на реальные страницы, которые затем могут быть извлечены из глобального файла kpageflags позже. Прочтите файл /usr/src/linux/Documentation/vm/pagemap.txt

  int mapfd=open("/proc/self/pagemap",O_RDONLY);
  assert(mapfd>0);
  unsigned long long target=((unsigned long)(void*)M)/pagesize;
  err=lseek64(mapfd, target*8, SEEK_SET);
  assert(err==target*8);
  assert(sizeof(long long)==8);

Здесь мы читаем номера фреймов страниц для каждой из наших виртуальных страниц

  unsigned long long page2pfn[pagecount];
  err=read(mapfd,page2pfn,sizeof(long long)*pagecount);
  if (err<0)
    perror("Reading pagemap");
  if(err!=pagecount*8)
    printf("Could only read %d bytes\n",err);

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

  int pageflags=open("/proc/kpageflags",O_RDONLY);
  assert(pageflags>0);
  for(int i = 0 ; i < pagecount; i++)
    {
      unsigned long long v2a=page2pfn[i];
      printf("Page: %d, flag %llx\n",i,page2pfn[i]);

      if(v2a&0x8000000000000000LL) // Is the virtual page present ?
        {
        unsigned long long pfn=v2a&0x3fffffffffffffLL;
        err=lseek64(pageflags,pfn*8,SEEK_SET);
        assert(err==pfn*8);
        unsigned long long pf;
        err=read(pageflags,&pf,8);
        assert(err==8);
        printf("pageflags are %llx with SWAPBACKED: %d\n",pf,(pf>>14)&1);
        }
    }
}

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

3 голосов
/ 05 мая 2011

Я обычно использую mprotect, чтобы настроить отслеживаемые страницы копирования при записи только для чтения, а затем обработать получающиеся значения SIGSEGV, пометив данную страницу как грязную и разрешив запись.

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

2 голосов
/ 27 января 2011

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

Я думаю, что ответ от bmargulies на этот вопрос подходит вамнужно идеально, когда две идеи объединены.

2 голосов
/ 19 декабря 2010

Копирование при записи осуществляется с использованием схемы защиты памяти оборудования виртуальной памяти.

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

Новая страницане только для чтения и не для копирования при записи, ссылка на исходную страницу полностью не работает.

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

В Windows API - GetWorkingSet, см. Объяснение в VirtualQueryEx.Я не знаю, что такое соответствующий API Linux.

2 голосов
/ 19 декабря 2010

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

  1. Прочитайте запись в / proc / pid / pagemap для соответствующих страниц в процессе (ах)
  2. Опросить / proc / kpageflags

Затем вы можете определить, что две страницы фактически являются одной и той же страницей в памяти.

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

1 голос
/ 18 декабря 2010

Я не помню, чтобы такой API экспортировался. Почему вы хотите сделать это (в чем корень проблемы, которую вы решаете?)

Возможно, вы захотите взглянуть на / proc / [pid] / smaps (который предоставляет несколько подробную статистику используемых / скопированных / сохраненных страниц).

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...