8 Stimmen

Wie erkennt man, ob eine Copy-on-Write-Seite tatsächlich eine Kopie ist?

Wenn ich eine Kopie-bei-Bedarf-Zuordnung (eine MAP_PRIVATE) mithilfe von mmap erstelle, werden einige Seiten dieser Zuordnung sofort kopiert, sobald ich an bestimmten Adressen schreibe. An einem bestimmten Punkt in meinem Programm möchte ich herausfinden, welche Seiten tatsächlich kopiert wurden. Es gibt einen Aufruf namens 'mincore', aber dieser meldet nur, ob sich die Seite im Speicher befindet oder nicht, was nicht dasselbe ist wie die Seite kopiert oder nicht.

Gibt es eine Möglichkeit herauszufinden, welche Seiten kopiert wurden?

11voto

Gut, auf den Ratschlag von MarkR hin habe ich es versucht, durch das Pagemap- und Kpageflags-Interface zu gehen. Unten ein schneller Test, um zu überprüfen, ob eine Seite im Speicher als 'SWAPBACKED', wie es genannt wird, ist. Ein Problem bleibt natürlich, nämlich das Problem, dass kpageflags nur dem Root zugänglich ist.

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("Erstellte Testdatei test.dat\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("Private Zuordnung erfolgreich erstellt\n");

Das Test-Setup enthält 4 Seiten. Seite 0 und 2 sind dirty

  strcpy(M,"Ich fühle mich so schmutzig\n");
  strcpy(M+pagesize*2,"Christus auf Krücken\n");

Seite 3 wurde gelesen.

  char t=M[pagesize*3];

Seite 1 wird nicht zugegriffen

Die Pagemap-Datei ordnet den virtuellen Speicherprozess den tatsächlichen Seiten zu, die dann später aus der globalen kpageflags-Datei abgerufen werden können. Lesen Sie die Datei /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);

Hier lesen wir die Rahmennummern für jede unserer virtuellen Seiten

  unsigned long long page2pfn[pagecount];
  err=read(mapfd,page2pfn,sizeof(long long)*pagecount);
  if (err<0)
    perror("Lesen der Pagemap");
  if(err!=pagecount*8)
    printf("Konnte nur %d Bytes lesen\n",err);

Jetzt werden wir für jede virtuelle Seite die tatsächlichen Seitenflags lesen

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

      if(v2a&0x8000000000000000LL) // Ist die virtuelle Seite vorhanden ?
        {
        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("Seitenflags sind %llx mit SWAPBACKED: %d\n",pf,(pf>>14)&1);
        }
    }
}

Alles in allem bin ich mit diesem Ansatz nicht besonders zufrieden, da er den Zugriff auf eine Datei erfordert, auf die wir im Allgemeinen nicht zugreifen können, und er ist verdammt kompliziert (wie wäre es mit einem einfachen Kernelaufruf zur Abrufung der Seitenflags ?).

3voto

Edward Kmett Punkte 29192

Normalerweise verwende ich mprotect, um meine nachverfolgten Kopierseiten schreibgeschützt zu setzen und dann die resultierenden SIGSEGVs zu behandeln, indem ich die entsprechende Seite als schmutzig markiere und das Schreiben aktiviere.

Es ist nicht ideal, aber der Overhead ist recht überschaubar und es kann in Kombination mit mincore usw. verwendet werden, um kompliziertere Optimierungen durchzuführen, wie die Verwaltung der Arbeitsgrößen oder die Approximation von Zeigerinformationen für Seiten, die ausgelagert werden sollen. Dadurch kann das Laufzeitsystem mit dem Kernel zusammenarbeiten, anstatt dagegen anzukämpfen.

3 Stimmen

Wie behandeln Sie SIGSEGV für diesen Zweck und wie erholen Sie sich davon?

0 Stimmen

@MattJoiner, Sie können genauso wie für jedes andere Signal einen Signalhandler dafür registrieren und die Ausführung wird problemlos fortgesetzt, wenn die Berechtigungen auf der abgerufenen Seite geändert wurden, um die richtigen Berechtigungen zu haben. Dies liegt daran, dass beim Fortsetzen der Ausführung der fehlerhafte Speicherzugriff erneut versucht wird. Rufen Sie also mprotect aus Ihrem Handler auf.

2voto

MarkR Punkte 60862

Es ist nicht einfach, aber möglich, dies festzustellen. Um herauszufinden, ob eine Seite eine Kopie einer anderen Seite (möglicherweise eines anderen Prozesses) ist, müssen Sie Folgendes tun (bei aktuellen Kernen):

  1. Lesen Sie den Eintrag in /proc/pid/pagemap für die entsprechenden Seiten im Prozess (en)
  2. Interrogieren Sie /proc/kpageflags

Dann können Sie feststellen, dass zwei Seiten tatsächlich dieselbe Seite im Speicher sind.

Es ist ziemlich knifflig, dies zu tun, Sie müssen root sein und was auch immer Sie tun werden wahrscheinlich einige Wettlaufbedingungen haben, aber es ist möglich.

0 Stimmen

Das wird Ihnen sagen, ob zwei "saubere" Kopie-auf-Schreibeseiten den gleichen Speicher teilen. Herauszufinden, ob eine als Kopie-auf-Schreiben erstellte Seite "sauber" oder "schmutzig" ist, ist viel einfacher.

0 Stimmen

Ich habe wahrscheinlich nicht das aktuelle Kernel 2.6.34 am Laufen und weder kpageflags noch die pagemap-Datei. Außerdem schreibe ich ein Programm auf Benutzerebene, das nicht als Root ausgeführt werden sollte. Als Antwort auf Ben Voigt, der behauptet, dass es einfacher ist, herauszufinden, ob eine Seite schmutzig oder sauber ist: Nun, wie würde ich das dann machen? Denn das ist genau die Frage!

0 Stimmen

Gemäß der pagemap.txt-Dokumentation ist 2.6.34 neu genug. Es könnte nicht notwendig sein, die (nur für Root) kpageflags-Schnittstelle zu verwenden, um das zu tun, was Sie wollen, in welchem ​​Fall Sie keinen Root benötigen.

2voto

Ben Voigt Punkte 268424

Copy-on-write wird mit dem Speicherschutzschema der Hardware für virtuellen Speicher implementiert.

Wenn auf eine schreibgeschützte Seite geschrieben wird, tritt ein Seitenfehler auf. Der Seitenfehler-Handler überprüft, ob die Seite das Copy-on-Write-Flag trägt: Wenn ja, wird eine neue Seite zugewiesen, der Inhalt der alten Seite kopiert und der Schreibvorgang erneut versucht.

Die neue Seite ist weder schreibgeschützt noch copy-on-write, die Verbindung zur Originalseite ist vollständig unterbrochen.

Es reicht also aus, die Speicherschutzflags für die Seite zu testen.

Unter Windows ist die API GetWorkingSet, siehe die Erklärung unter VirtualQueryEx. Ich weiß nicht, was die entsprechende Linux-API ist.

0 Stimmen

Ich nutze nicht Windows, wie angegeben. Ich habe auch nie gesagt, dass die Seite als 'Copy-On-Write' markiert werden würde, ich möchte nur diejenigen herausfinden, die schmutzig sind. Ob es keinen Link zum Original gibt, bin ich mir unsicher, denn in Linux ist es möglich, die kopierten Seiten durch Synchronisieren der schreibgeschützten Seiten ungültig zu machen (siehe msync).

0 Stimmen

@Werner: msync ist irrelevant, es sorgt nur dafür, dass die Schreibvorgänge, die während munmap stattfinden würden, früher stattfinden. Aber Schreibvorgänge auf Kopier-auf-Schreibeseiten beeinflussen die Datei nicht (siehe mmap Man-Seite, Hinweis zu MAP_PRIVATE -- Änderungen im Bereich beeinflussen nicht die Originaldatei.) Und ich weiß, dass du Linux verwendest, aber ich hoffte, dass durch die Angabe des Namens der Windows-API jemand einen Link zur äquivalenten Linux-Funktion bereitstellen würde.

0 Stimmen

Gemäß dieser Frage gibt es keinen Funktionsaufruf zum Lesen der Seitenschutzflags, aber sie sind über /proc/self/maps zugänglich. Wenn eine Seite, die mit MAP_PRIVATE zugeordnet ist, die Schreibberechtigungsflagge hat, wurde sie bereits beschrieben (dirty), und der Seitenfehlerhandler hat die Daten bereits auf eine unabhängige private Seite kopiert.

2voto

James Caccese Punkte 1374

Ich habe jemandem mit einem ähnlichen Ziel geantwortet und auf eine Frage verwiesen, die der Ihren ähnlich ist.

Ich denke, bmargulies' Antwort auf diese Frage passt perfekt zu dem, was Sie brauchen, wenn die beiden Ideen kombiniert werden.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X