Domanda

La chiamata di sistema clone () su Linux prende un parametro che punta allo stack per il nuovo thread creato da utilizzare. Il modo più ovvio per farlo è quello di malloc semplicemente un pò di spazio e di passare, ma poi si deve essere sicuri di aver malloc'd come spazio dello stack fino a quel filo potrà mai utilizzare (difficile da prevedere).

mi sono ricordato che quando si utilizza pthreads non ho dovuto fare questo, quindi ero curioso di quello che ha fatto, invece. Mi sono imbattuto in questo sito che spiega: "La soluzione migliore, usata dall'implementazione pthreads di Linux, è utilizzare mmap per allocare la memoria, con bandiere specificando una regione della memoria che viene allocata in quanto viene utilizzato. in questo modo, la memoria viene allocata per la pila in quanto è necessario, e una violazione di segmentazione si verificherà se il sistema è in grado di allocare ulteriori memoria ".

L'unico contesto che abbia mai sentito mmap utilizzato in è per file di mapping in memoria, e in effetti la lettura della pagina man mmap ci vuole un descrittore di file. Come può essere utilizzato per l'assegnazione di una pila di lunghezza dinamica per dare clonare ()? È quel sito solo pazzo? ;)

In entrambi i casi, non il kernel ha bisogno di sapere come trovare un gruppo privo di memoria per una nuova pila in ogni caso, dal momento che questo è qualcosa che ha a che fare tutto il tempo come l'utente avvia nuovi processi? Perché uno stack pointer nemmeno bisogno di specificare in primo luogo se il kernel può già capire questo?

È stato utile?

Soluzione

Joseph, in risposta alla sua ultima domanda:

Quando un utente crea un "normale" nuovo processo, che è fatto da fork (). In questo caso, il kernel non deve preoccuparsi di creare una nuova pila a tutti, perché il nuovo processo è un duplicato completo di quello vecchio, fino allo stack.

Se l'utente sostituisce il processo attualmente in esecuzione utilizzando exec (), allora il kernel ha bisogno di creare un nuovo stack - ma in questo caso che è facile, perché si arriva a partire da una tabula rasa. exec () cancella lo spazio di memoria del processo e reinitialises esso, in modo che il kernel arriva a dire "dopo exec (), lo stack vive sempre qui".

Se, invece, si usa clone (), allora possiamo dire che il nuovo processo condivideranno uno spazio di memoria con il vecchio processo (CLONE_VM). In questa situazione, il kernel non può lasciare la pila come è stato nel processo di chiamata (come fork () lo fa), perché allora i nostri due processi sarebbero stomping sulla pila a vicenda. Il kernel, inoltre, non può semplicemente mettere in una posizione predefinita (come exec ()) lo fa, perché questo luogo è già preso in questo spazio di memoria. L'unica soluzione è quella di consentire il processo chiamante per trovare un posto per esso, che è ciò che fa.

Altri suggerimenti

Le pile non sono, e non possono essere, senza limiti nel loro spazio per la crescita. Come tutto il resto, vivono in uno spazio di indirizzamento virtuale del processo, e l'importo di cui possono crescere è sempre limitata dalla distanza alla regione adiacente memoria mappata.

Quando si parla di stack di crescere in modo dinamico, quello che potrebbero significare è una delle due cose:

  • Pagine della pila potrebbe essere copy-on-scrivere zero pagine, che non ricevono le copie private fatte fino a quando viene eseguita la prima scrittura.
  • parti inferiori della regione pila potrebbe non ancora essere riservata (e quindi non conta ai fini di carica della commettere processo, vale a dire la quantità di memoria fisica / scambiare il kernel è contabilizzato come riservato per il processo) fino a quando un guard page è colpito , nel qual caso il kernel si impegna di più e si muove nella pagina di guardia, o uccide il processo se non c'è memoria a sinistra per commettere.

Cercando di fare affidamento sulla bandiera MAP_GROWSDOWN è inaffidabile e pericoloso perché non si può proteggere contro mmap la creazione di una nuova mappatura proprio adiacente al tuo stack, che poi vengono rovinati. (Vedere http://lwn.net/Articles/294001/ ) Per il filo principale, la kernel riserva automaticamente lo stack-size ulimit valore di spazio di indirizzi (non memoria ) sotto la pila e impedisce mmap da assegnazione. (Ma attenzione! Alcuni kernel vendor-patched rotti disattivare questo comportamento che porta alla corruzione della memoria casuale!) Per altre discussioni, è sufficiente deve mmap l'intera gamma di spazio degli indirizzi del filo potrebbe aver bisogno per lo stack durante la creazione di esso . Non c'è altro modo. È potrebbe fare la maggior parte di esso inizialmente non-scrivibile / non-leggibile, e di cambiare che in difetti, ma poi avresti bisogno gestori di segnale e questa soluzione non è accettabile in un'implementazione thread POSIX perché sarebbe interferire con gestori di segnali dell'applicazione. (Si noti che, come estensione, il kernel di potrebbero offrire bandiere speciali MAP_ per fornire un segnale diverso, invece di SIGSEGV sul accesso illegale alla mappatura, e quindi l'attuazione discussioni potevano catturare e agire su questo segnale. ma Linux attualmente non ha tale caratteristica.)

Infine, ricordiamo che la chiamata di sistema clone non prende un argomento stack pointer perché non ne ha bisogno. La chiamata di sistema deve essere eseguita dal codice assembly, perché l'involucro userspace è necessario per modificare lo stack pointer nel thread "bambino" per puntare allo stack desiderato, e evitare di scrivere qualcosa a pila del genitore.

In realtà, clone vuole un argomento stack pointer, perché è pericoloso per l'ora di cambiare stack pointer nel "bambino" dopo il ritorno in userspace. A meno che i segnali sono tutti bloccati, un gestore di segnale potrebbe funzionare immediatamente sulla pila sbagliato, e su alcune architetture lo stack pointer deve essere valido e punto sicuro per scrivere in ogni momento su una superficie.

Non solo sta modificando lo stack pointer impossibile da C, ma anche voi non poteva evitare la possibilità che il compilatore troncherà stack del genitore dopo la chiamata di sistema, ma prima che il puntatore dello stack è stato modificato.

Che ci si vuole la bandiera MAP_ANONYMOUS per mmap. E il MAP_GROWSDOWN dal momento che si desidera fare uso come una pila.

Qualcosa di simile:

void *stack = mmap(NULL,initial_stacksize,PROT_WRITE|PROT_READ,MAP_PRIVATE|MAP_GROWSDOWN|MAP_ANONYMOUS,-1,0);

Si veda la pagina man mmap per maggiori informazioni. E ricordate, clone è un concetto di basso livello, che non si è fatti per usare a meno che non si ha realmente bisogno quello che offre. E offre un sacco di controllo - come la creazione di un proprio stack - nel caso in cui si vuole fare un po 'trickering (come avere lo stack accessibili in tutti i relativi processi). A meno che non si dispone di molto buona ragione per usare clone, bastone con forchetta o pthreads.

Ecco il codice che mmaps una regione pila e istruisce la chiamata di sistema clone di utilizzare questa regione come lo stack.

#include sys/mman.h>
#include stdio.h>
#include string.h>
#include sched.h>
int execute_clone(void *arg)
{
    printf("\nclone function Executed....Sleeping\n");
    fflush(stdout);
    return 0;
}

int main()
{
    void *ptr;

    int rc;
    void *start =(void *) 0x0000010000000000;
    size_t len = 0x0000000000200000;

    ptr = mmap(start, len, PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE|MAP_FIXED|MAP_GROWSDOWN, 0, 0);
    if(ptr == (void *)-1) 
    {
        perror("\nmmap failed");
    }

    rc = clone(&execute_clone, ptr + len, CLONE_VM, NULL);

    if(rc <= 0) 
    {
        perror("\nClone() failed");
    }
}

mmap è più di una semplice mappatura di un file in memoria. In realtà, alcune implementazioni malloc useranno mmap per grandi ripartizioni. Se leggete la pagina man bene noterete la bandiera MAP_ANONYMOUS, e vedrete che non è necessario bisogno di fornire un descrittore di file a tutti.

Per quanto riguarda il motivo per cui il kernel non può semplicemente "trovare un po 'di memoria libera", bene, se si desidera che qualcuno a fare il lavoro per voi, utilizzare la forcella, invece, o utilizzare pthreads.

Si noti che la chiamata di sistema clone non prendere un argomento per la posizione dello stack. In realtà funziona proprio come fork. E 'solo l'involucro glibc che prende tale argomento.

Credo stack cresce verso il basso fino a che non può crescere, per esempio quando si sviluppa una memoria che è stata allocata prima, forse un guasto notified.That può vedere un default è il minimo disponibile stack, se ci spazio ridondante verso il basso quando la pila è piena, può crescere verso il basso, in caso contrario, il sistema può notificare un guasto.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top