2012-09-19 10 views
5

Ich bin sehr neu in diesen Konzepten, aber ich möchte Sie alle eine Frage stellen, die sehr einfach ist, denke ich, aber ich bin verwirrt, also frage ich es. Die Frage ist ... Wie wird die Größe eines Prozesses vom Betriebssystem bestimmt? Lassen Sie mich zuerst klarstellen, angenommen, dass ich ein C-Programm geschrieben habe und ich möchte wissen, wie viel Speicher es braucht, wie kann ich es bestimmen? Zweitens weiß ich, dass es viele Abschnitte wie Codeabschnitt, Datenabschnitt, BSS eines Prozesses gibt. Ist nun die Größe von diesen vorbestimmt? zweitens, wie die Größe von Stack und Heap ermittelt wird. Es spielt auch die Größe von Stack und Heap eine Rolle, während die Gesamtgröße des Prozesses berechnet wird.Wie wird die Prozessgröße bestimmt?

Wieder sagen wir, dass, wenn wir das Programm laden, ein Adressraum für den Prozess gegeben wird (das wird von Basis- und Limit-Register getan und von MMU gesteuert, nehme ich an) und wenn der Prozess versucht, auf einen Speicherort zuzugreifen ist nicht in seinem Adressraum erhalten wir Segmentierungsfehler. Wie kann ein Prozess auf einen Speicher zugreifen, der sich nicht in seinem Adressraum befindet? Nach meinem Verständnis, wenn einige Pufferüberläufe auftreten, wird die Adresse beschädigt. Wenn der Prozess nun auf den beschädigten Speicherort zugreifen möchte, erhalten wir den Segmentierungsfehler. Gibt es einen anderen Weg der Adressverletzung?

und drittens, warum der Stapel nach unten wächst und nach oben häuft. Ist dieser Prozess der gleiche mit allen Betriebssystemen. Wie wirkt sich das auf die Leistung aus? Warum können wir es nicht anders haben?

Bitte korrigieren Sie mich, wenn ich falsch in irgendeiner Aussage bin.

Dank Sohrab

+0

Keine Programmierfrage. – unwind

Antwort

1

Wenn ein Prozess gestartet wird, erhält er seinen eigenen virtuellen Adressraum. Die Größe des virtuellen Adressraums hängt von Ihrem Betriebssystem ab. Im Allgemeinen erhalten 32-Bit-Prozesse 4 GiB (4 Giga-Binär) -Adressen und 64-Bit-Prozesse erhalten 18 EiB (18 Exa-Binär) -Adressen.

Sie können auf keinen Fall auf etwas zugreifen, das nicht Ihrem virtuellen Adressraum zugeordnet ist, da per Definition alles, was dort nicht zugeordnet ist, keine Adresse für Sie hat. Sie können versuchen, auf Bereiche Ihres virtuellen Adressraums zuzugreifen, die derzeit keinem zugeordnet sind. In diesem Fall erhalten Sie eine segfault-Ausnahme.

Nicht alle Adressräume sind zu irgendeinem Zeitpunkt auf etwas abgebildet. Es kann auch nicht alles abgebildet werden (wie viel davon abgebildet werden kann, hängt vom Prozessor und vom Betriebssystem ab). Auf aktuellen Generation Intel Prozessoren können bis zu 256 TiB Ihres Adressraums abgebildet werden. Beachten Sie, dass Betriebssysteme das weiter einschränken können. Zum Beispiel für 32-Bit-Prozesse (mit bis zu 4 GiB-Adressen) Windows reserviert standardmäßig 2 GiB für das System und 2 GiB für die Anwendung (aber es gibt eine Möglichkeit, 1 GiB für das System und 3 GiB für die Anwendung).

Wie viel von dem Adressraum verwendet wird und wie viel zugeordnet wird, ändert sich, während die Anwendung ausgeführt wird. Mit betriebssystemspezifischen Tools können Sie überwachen, was der aktuell zugewiesene Speicher und der virtuelle Adressraum für eine laufende Anwendung sind.

Codeabschnitt, Datenabschnitt, BSS usw. sind Begriffe, die sich auf verschiedene Bereiche der vom Linker erstellten ausführbaren Datei beziehen. Im Allgemeinen ist Code von statischen unveränderlichen Daten getrennt, die von statisch zugewiesenen, aber veränderbaren Daten getrennt sind. Stack und Heap sind getrennt von allen oben genannten. Ihre Größe wird vom Compiler und dem Linker berechnet. Beachten Sie, dass jede Binärdatei über eigene Abschnitte verfügt. Daher werden alle dynamisch verknüpften Bibliotheken im Adressraum separat zugeordnet, wobei jeder Abschnitt eigene Bereiche aufweist. Heap und Stack sind jedoch nicht Teil des Binärbildes, es gibt im Allgemeinen nur einen Stack pro Prozess und einen Heap.

Die Größe des Stapels (zumindest der anfängliche Stapel) ist im Allgemeinen fest. Compiler und/oder Linker haben im Allgemeinen einige Flags, mit denen Sie die Größe des Stacks zur Laufzeit festlegen können. Stacks "wachsen im Allgemeinen rückwärts", weil die Anweisungen des Prozessor-Stacks so funktionieren. Wenn Stacks in eine Richtung wachsen und der Rest in dem anderen wächst, ist es leichter, Speicher in Situationen zu organisieren, in denen beide unbegrenzt sein sollen, aber nicht wissen, wie viel jede davon wachsen kann.

Heap bezieht sich im Allgemeinen auf alles, was beim Start des Prozesses nicht vorab zugewiesen wird. Auf der untersten Ebene gibt es mehrere logische Operationen, die sich auf die Heap-Verwaltung beziehen (nicht alle sind so implementiert, wie ich hier in allen Betriebssystemen beschreibe).

Während der Adressraum fest ist, verfolgen einige Betriebssysteme, welche Teile davon derzeit vom Prozess zurückerlangt werden. Auch wenn dies nicht der Fall ist, muss der Prozess selbst den Überblick behalten. Die Operation auf der niedrigsten Ebene besteht also darin, tatsächlich zu entscheiden, dass eine bestimmte Region des Adressraums verwendet wird.

Die zweite Low-Level-Operation besteht darin, das Betriebssystem anzuweisen, diese Region etwas zuzuordnen.Dies kann im allgemeinen

  • einige Speicher, die nicht

  • Speicher swappable ist, die dem System Auslagerungsdatei

  • Speicher, der auf eine andere Datei ist swappable und kartiert swappable und kartiert ist

  • Speicher, der im schreibgeschützten Modus ausgetauscht und einer anderen Datei zugeordnet werden kann

  • dieselbe Zuordnung, dass eine andere virtuelle Adreßbereich

  • die gleiche Abbildung, die einen anderen virtuellen Adreßbereich zugeordnet wird, zu

    abgebildet wird, aber in abzubildenden nur Modus

  • dieselbe Zuordnung, dass eine andere virtuelle Adresse Region ausgelesen , aber auf Schreibmodus mit den kopierten Daten in Kopie an die Datei Default Swap abgebildet

Es können auch andere Kombinationen, die ich vergessen, aber das sind die wichtigsten sind.

Natürlich hängt der gesamte Speicherplatz davon ab, wie Sie ihn definieren. Der aktuell verwendete Arbeitsspeicher unterscheidet sich vom aktuell zugeordneten Adressraum. Aber wie ich oben schrieb, sollten Betriebssystem-abhängige Tools Sie herausfinden lassen, was gerade passiert.

1

Die Abschnitte werden durch die ausführbare Datei vorgegeben.

Daneben kann es diejenigen von dynamisch verknüpften Bibliotheken geben. Während die Code- und Konstantendaten einer DLL über mehrere Prozesse verteilt verwendet werden sollen und nicht mehr als einmal gezählt werden, sollten ihre prozessspezifischen nicht konstanten Daten in jedem Prozess berücksichtigt werden.

Außerdem kann dynamisch Speicher im Prozess zugeordnet werden.

Darüber hinaus, wenn es mehrere Threads in dem Prozess gibt, wird jeder von ihnen einen eigenen Stapel haben.

Darüber hinaus werden pro-Thread-, pro-Prozess- und pro-Bibliothek-Datenstrukturen im Prozess selbst und im Kernel in seinem Namen vorhanden sein (thread-lokaler Speicher, Befehlszeilenparameter, Zugriffe auf verschiedene Ressourcen) , Strukturen für diese Ressourcen und so weiter und so weiter).

Es ist schwierig, die gesamte Prozessgröße genau zu berechnen, ohne zu wissen, wie alles implementiert ist. Sie können jedoch eine vernünftige Schätzung erhalten.

W.r.t. According to my understanding when some buffer overflows happens then the address gets corrupted. Es ist nicht unbedingt wahr. Vor allem die Adresse von was? Es hängt davon ab, was sich im Speicher in der Nähe des Puffers befindet. Wenn eine Adresse vorhanden ist, kann sie während eines Pufferüberlaufs überschrieben werden. Wenn jedoch ein Puffer in der Nähe vorhanden ist, der ein Bild von Ihnen enthält, können die Pixel des Bildes überschrieben werden.

Sie können Segmentierungen oder Seitenfehler erhalten, wenn Sie versuchen, auf Speicher zuzugreifen, für den Sie nicht über die erforderlichen Berechtigungen verfügen (z. B. den Kernelbereich, der zugeordnet oder anderweitig im Prozessadressraum vorhanden ist). Oder es kann ein schreibgeschützter Standort sein. Oder der Standort kann keine Zuordnung zum physischen Speicher haben.

Es ist schwer zu sagen, wie sich die Position und das Layout des Stacks und des Heaps auf die Leistung auswirken, ohne die Leistung dessen, wovon wir sprechen, zu kennen. Sie können spekulieren, aber die Spekulationen können sich als falsch herausstellen.

Btw, sollten Sie wirklich separate Fragen zu SO für separate Probleme zu stellen.

+0

danke ... Ich werde das beim nächsten Mal beachten ...: -) ... danke für die Erklärung. –

1

"Wie ist es möglich, dass ein Prozess auf einen Speicher zugreift, der sich nicht in seinem Adressraum befindet?"

Angesichts Speicherschutz ist es unmöglich. Aber es könnte versucht sein. Betrachten Sie zufällige Zeiger oder Zugriffe über Puffer hinaus. Wenn Sie einen Zeiger lange genug inkrementieren, wandert er fast sicher in einen nicht zugeordneten Adressbereich. Einfaches Beispiel:

char *p = "some string"; 

while (*p++ != 256) /* Always true. Keeps incrementing p until segfault. */ 
    ; 

Einfache Fehler wie diese sind nicht unbekannt, um eine Untertreibung zu machen.

+1

Es heißt "Litotes". :) – Mehrdad

+0

Danke! Ich hasse es nicht, diese Art von Terminologie zu lernen :-) – Jens

0

Ich kann Fragen # 2 und # 3 beantworten.

Antwort # 2

Wenn in C Sie Zeiger verwenden Sie wirklich einen numerischen Wert verwenden, der als Adresse in dem Speicher interpretiert wird (logische Adresse auf moderne OS, siehe Fußnoten). Sie können diese Adresse nach Belieben ändern. Wenn der Wert auf eine Adresse zeigt, die sich nicht in Ihrem Adressraum befindet, liegt Ihr Segmentierungsfehler vor.

Betrachten Sie zum Beispiel dieses Szenario: Ihr Betriebssystem gibt Ihrem Prozess den Adressbereich von 0x01000 bis 0x09000. Dann

int * ptr = 0x01000; 
printf("%d", ptr[0]); // * prints 4 bytes (sizeof(int) bytes) of your address space 
int * ptr = 0x09100; 
printf("%d", ptr[0]); // * You are accessing out of your space: segfault 

Meistens sind die Ursachen für segfault, wie Sie wies darauf hin, sind die Verwendung von Zeigern auf NULL (das ist meist 0x00-Adresse, aber abhängig von der Implementierung) oder die Verwendung von beschädigten Adressen.

Beachten Sie, dass unter Linux i386, Basis und Limit-Register nicht verwendet werden, wie Sie vielleicht denken. Sie sind keine Grenzen pro Prozess, aber sie verweisen auf zwei Arten von Segmenten: User Space oder Kernel Space.

Antwort # 3

Der Stapel Wachstum ist hardwareabhängig und nicht die OS abhängig. Bei i386-Assemblerbefehlen wie Push und Pop wird der Stack in Bezug auf Stack-bezogene Register nach unten wachsen. Zum Beispiel verringert sich der Stapelzeiger automatisch, wenn Sie einen Push machen, und erhöht sich, wenn Sie einen Pop-Effekt machen. OS kann damit nicht umgehen.

Fußnoten

In einem modernen Betriebssystem verwendet ein Verfahren, die so logische Adresse genannt. Diese Adresse wird vom OS mit der physikalischen Adresse verknüpft. Um eine Notiz von dieser selbst kompiliert dieses einfach Programm:

#include <stdio.h> 

int main() 
{ 
    int a = 10; 
    printf("%p\n", &a); 
    return 0; 
} 

Wenn Sie das Programm mehrfach (auch gleichzeitig) laufen würden Sie, auch für verschiedene Instanzen sehen, die die gleiche Adresse ausgedruckt. Natürlich ist dies nicht die reale Speicheradresse, aber es ist eine logische Adresse, die bei Bedarf auf die physikalische Adresse abgebildet wird.

Verwandte Themen