Przestrzenie nazw

Marek Dzikiewicz

Wstęp

Nazwy różnych obiektów systemowych są przechowywane w tzw. przestrzeniach nazw. Tradycyjnie przestrzenie nazw były globalne dla całego systemu, lecz w miarę rozwoju jądra wprowadzono możliwość tworzenia osobnych przestrzeni nazw dla poszczególnych procesów lub grup procesów. Dzięki temu grupy procesów mogą używać oddzielnych przestrzeni nazw i być odseparowane od reszty systemu.

Aktualnie Linux obsługuje oddzielne tworzenie następujących przestrzeni nazw:

Wskaźniki do poszczególnych struktur reprezentujących przestrzenie nazw procesu są przechowywane w strukturze nsproxy w metryczce procesu.


struct task_struct {
...
/* namespaces */
    struct nsproxy *nsproxy;
...
};
Struktura nsproxy zawiera wskaźniki do struktur reprezentujących poszczególne przestrzenie nazw w systemie. Ponadto struktura zawiera pole count, które określa ile procesów wspołdzieli daną instancję struktury nsproxy. Procesy zawsze współdzielą strukturę nsproxy, dopóki wspóldzielą wszystkie przestrzenie nazw. W momencie utworzenia nowej przestrzeni nazw, tworzona jest kopia struktury nsproxy dla tego procesu.

struct nsproxy {
    atomic_t count;
    struct uts_namespace *uts_ns;
    struct ipc_namespace *ipc_ns;
    struct mnt_namespace *mnt_ns;
    struct pid_namespace *pid_ns;
    struct net           *net_ns;
};

Przestrzeń nazw zamontowanych systemów plików

Przestrzeń nazw systemu plików zawiera drzewo katalogów z zamontowanymi systemami plików. Podstawowy system plików jest montowany w korzeniu drzewa w czasie uruchamiania systemu. Dodatkowe system plików mogą być później montowane w dowolnym miejscu w drzewie.

Utworzenie osobnej przestrzeni nazw systemu plików dla grupy procesów umożliwa zamontowanie systemu plików, który będzie widoczny tylko dla tych procesów. Ponadto można zabrać grupie procesów dostęp do jakiegoś systemu plików poprzez jego odmontowanie w osobnej przestrzeni nazw.

Pojedyncza przestrzeń nazw systemu plików jest reprezentowana przez strukturę mnt_namespace.


struct mnt_namespace {
    atomic_t                count;      /* liczba procesów współdzielących daną przestrzeń nazw */

    struct vfsmount *       root;       /* deskryptor systemu plików katalogu głównego (korzenia)
                                         * przestrzeni nazw
                                         */

    struct list_head        list;       /* lista deskryptorów systemów plików zamontowanych w 
                                         * przestrzeni nazw
                                         */

    wait_queue_head_t poll;             /* kolejka do synchronizacji dostępu do struktury */

    ...
};

Przestrzeń nazw stacji roboczej i domeny

Przestrzeń nazw UTS przechowuje aktualną nazwę stacji roboczej (hosta) oraz nazwę domeny, do której należy stacja robocza. Utworzenie osobnej przestrzeni nazw UTS dla grupy procesów umożliwia zmianę tych parametrów tylko dla poszczególnej grupy procesów.

Pojedyncza przestrzeń UTS jest reprezentowana przez strukturę uts_namespace.


struct uts_namespace {
    struct kref kref;           /* licznik procesów współdzielących daną przestrzeń nazw */

    struct new_utsname name;    /* wskaźnik do struktury, która przechowuje nazwę hosta 
                                 * domeny, itd.
                                 */
};

Przestrzeń nazw identyfikatorów procesów

Przestrzenie nazw identyfikatorów procesów umożliwiają istnienie wielu procesów z takimi samymi identyfikatorami. Funkcjonalność ta jest przydatna przy przenoszeniu grupy procesów między maszynami, ponieważ pozwala na przeniesienie procesu bez zmiany jego identyfikatora.

Po utworzeniu nowej przestrzeni nazw identyfikatorów procesów, procesy z aktualnej przestrzeni nazw będą "widziały" procesy z nowej przestrzeni nazw, natomiast procesy z nowej przestrzeni nazw nie będą widziały procesów z aktualnej przestrzeni. Pierwszy proces w nowej przestrzeni nazw będzie miał PID równy 1, czyli będzie odpowiednikiem procesu init z systemowej przestrzeni identyfikatorów procesów. Zakończenie działania tego procesu powoduje zniszczenie nowo-utworzonej przestrzeni nazw. Podobnie jak w systemowej przestrzeni nazw, "osierocone" procesy będą automatycznie ustawiane jako procesy potomne procesu o identyfikatorze 1. Dla procesów z nowo-utworzonej przestrzeni nazw, powinna zostać zamontowana osobna kopia systemu plików proc, aby polecenia takie jak ps działały prawidłowo.

Pojedyncza przestrzeń nazw identyfikatorów procesów jest reprezentowana przez strukturę pid_namespace.


struct pid_namespace {
    struct kref kref;                       /* licznik procesów współdzielących daną przestrzeń 
                                             * nazw
                                             */

    struct pidmap pidmap[PIDMAP_ENTRIES];   /* mapa bitowa, która przechowuje informację o wolnych i
                                             * użytych numerach PID
                                             */

    struct task_struct *child_reaper;       /* wskaźnik do metryczki procesu, który ma PID 1 w 
                                             * przestrzeni nazw
                                             */

    struct kmem_cache *pid_cachep;          /* struktura, która przechowuje wolne identyfikatory 
                                             * procesów
                                             */

    unsigned int level;                     /* głębokość przestrzeni w hierarchii przestrzeni nazw */

    struct pid_namespace *parent;           /* wskaźnik do nadrzędnej przestrzni nazw procesów 
                                             * (czyli przestrzeni nazw z której została utworzona ta
                                             * przestrzeń nazw)
                                             */

    ...
};

Od momentu wprowadzenie przestrzeni nazw identyfikatorów procesów, każdy proces może mieć więcej niż jeden identyfikator. Dzięki temu proces może być widoczny w wielu przestrzeniach nazw, w szczególności w oryginalnej przestrzeni nazw, z której została utworzona nowa przestrzeń. Do reprezentacji identyfikatorów procesów wprowadzono nową strukturę upid, która przechowuje identyfikator procesu w pojedynczej przestrzeni nazw, natomiast w oryginalnej strukturze pid, przezchowywane są odwołania do struktur upid.


struct upid {
    int nr;                                 /* identyfikator procesu */

    struct pid_namespace *ns;               /* wskaźnik do przestrzeni nazw identyfikatorów 
                                             * procesów 
                                             */

    struct hlist_node pid_chain;
};

struct pid {
    atomic_t count;                         /* licznik odwołań */
    unsigned int level;                     /* liczba struktur upid procesu */
    struct hlist_head tasks[PIDTYPE_MAX];
    struct rcu_head rcu;
    struct upid numbers[1];                 /* wskaźniki do struktur upid */
};

Przestrzeń nazw obiektów IPC

Przestrzenie nazw obiektów IPC umożliwiają tworzenie systemowych obiektów do komunikacji między procesami (np. semafory, kolejki, itp.), które są widoczne tylko dla grupy procesów, które współdzielą daną przestrzeń nazw IPC.

Pojedyncza przestrzeń nazw obiektów IPC jest reprezentowana przez strukturę ipc_namespace.


struct ipc_namespace {
    atomic_t        count;          /* licznik procesów współdzielących daną przestrzeń nazw */
    struct ipc_ids  ids[3];

    int             sem_ctls[4];    /* semafory */
    int             used_sems;

    int             msg_ctlmax;     /* kolejki komunikatów */
    int             msg_ctlmnb;
    int             msg_ctlmni;
    atomic_t        msg_bytes;
    atomic_t        msg_hdrs;
    int             auto_msgmni;
  
    size_t          shm_ctlmax;     /* pamięć współdzielona */
    size_t          shm_ctlall;
    int             shm_ctlmni;
    int             shm_tot;
  
    ...
};

Przestrzeń nazw zasobów sieciowych

Przestrzeń nazw zasobów sieciowych umożliwa odizolowanie całego podsystemu seciowego dla grupy procesów. Dzięki temu grupa procesów może konfigurować urządzenia sieciowe oraz nawiązywać połączenia niezależnie od reszty systemu. Taka grupa procesów ma własne urządzenia sieciowe, adresy IP, trasy, itd. Rozwiązanie to znajduje zastosowanie głównie w wirtualizacji systemów komputerowych lub w sytacji, w której ze względów bezpieczeństwa chcemy aby tylko pewna grupa procesów miała dostęp do odpowiednich zasobów sieciowych.

Pojedyncza przestrzeń nazw sieciowych jest reprezentowana przez strukturę net.


struct net {
    atomic_t                count;              /* licznik procesów współdzielących przestrzeń nazw */

    struct proc_dir_entry   *proc_net;          /* wpisy w systemie plików proc */
    struct proc_dir_entry   *proc_net_stat;

    struct net_device       *loopback_dev;      /* wskaźnik do struktury reprezentującej urządzenie
                                                 * seciowe loopback 
                                                 */

    struct list_head        dev_base_head;      /* lista urządzeń sieciowych przestrzeni nazw */
    struct hlist_head       *dev_name_head;
    struct hlist_head       *dev_index_head;

    struct netns_core       core;               /* struktury ze wskaźnikami do struktur podsystemu 
    struct netns_mib        mib;                 * sieciowego dla przestrzeni nazw
    struct netns_packet     packet;              */
    struct netns_unix       unx;
    struct netns_ipv4       ipv4;

    ...
};

Każde urządzenie sieciowe reprezentowane przez strukturę net_device ma wskaźnik do struktury net, która reprezentuje przestrzeń nazw seciowych dla tego urządzenia.


struct net_device {
    ...
#ifdef CONFIG_NET_NS
    /* Network namespace this network device is inside */
    struct net              *nd_net;
#endif
    ...
};

Tworzenie nowych przestrzeni nazw

Aby utworzyć nową przestrzeń nazw dla nowo-tworzonego procesu, należy wywołać funkcję systemową fork lub clone i ustawić w paramaterach jedną lub więcej z poniższych flag.

Bibliografia