요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.
1. 요약·해설
원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.
2. 영어 원문 전체
번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.
원문 전체 펼치기
.. include:: ../disclaimer-ita.rst
:Original: :ref:`Documentation/process/adding-syscalls.rst <addsyscalls>`
:Translator: Federico Vaga <[email protected]>
.. _it_addsyscalls:
Aggiungere una nuova chiamata di sistema
========================================
Questo documento descrive quello che è necessario sapere per aggiungere
nuove chiamate di sistema al kernel Linux; questo è da considerarsi come
un'aggiunta ai soliti consigli su come proporre nuove modifiche
:ref:`Documentation/translations/it_IT/process/submitting-patches.rst <it_submittingpatches>`.
Alternative alle chiamate di sistema
------------------------------------
La prima considerazione da fare quando si aggiunge una nuova chiamata di
sistema è quella di valutare le alternative. Nonostante le chiamate di sistema
siano il punto di interazione fra spazio utente e kernel più tradizionale ed
ovvio, esistono altre possibilità - scegliete quella che meglio si adatta alle
vostra interfaccia.
- Se le operazioni coinvolte possono rassomigliare a quelle di un filesystem,
allora potrebbe avere molto più senso la creazione di un nuovo filesystem o
dispositivo. Inoltre, questo rende più facile incapsulare la nuova
funzionalità in un modulo kernel piuttosto che essere sviluppata nel cuore
del kernel.
- Se la nuova funzionalità prevede operazioni dove il kernel notifica
lo spazio utente su un avvenimento, allora restituire un descrittore
di file all'oggetto corrispondente permette allo spazio utente di
utilizzare ``poll``/``select``/``epoll`` per ricevere quelle notifiche.
- Tuttavia, le operazioni che non si sposano bene con operazioni tipo
:manpage:`read(2)`/:manpage:`write(2)` dovrebbero essere implementate
come chiamate :manpage:`ioctl(2)`, il che potrebbe portare ad un'API in
un qualche modo opaca.
- Se dovete esporre solo delle informazioni sul sistema, un nuovo nodo in
sysfs (vedere ``Documentation/filesystems/sysfs.rst``) o
in procfs potrebbe essere sufficiente. Tuttavia, l'accesso a questi
meccanismi richiede che il filesystem sia montato, il che potrebbe non
essere sempre vero (per esempio, in ambienti come namespace/sandbox/chroot).
Evitate d'aggiungere nuove API in debugfs perché questo non viene
considerata un'interfaccia di 'produzione' verso lo spazio utente.
- Se l'operazione è specifica ad un particolare file o descrittore, allora
potrebbe essere appropriata l'aggiunta di un comando :manpage:`fcntl(2)`.
Tuttavia, :manpage:`fcntl(2)` è una chiamata di sistema multiplatrice che
nasconde una notevole complessità, quindi è ottima solo quando la nuova
funzione assomiglia a quelle già esistenti in :manpage:`fcntl(2)`, oppure
la nuova funzionalità è veramente semplice (per esempio, leggere/scrivere
un semplice flag associato ad un descrittore di file).
- Se l'operazione è specifica ad un particolare processo, allora
potrebbe essere appropriata l'aggiunta di un comando :manpage:`prctl(2)`.
Come per :manpage:`fcntl(2)`, questa chiamata di sistema è un complesso
multiplatore quindi è meglio usarlo per cose molto simili a quelle esistenti
nel comando ``prctl`` oppure per leggere/scrivere un semplice flag relativo
al processo.
Progettare l'API: pianificare le estensioni
-------------------------------------------
Una nuova chiamata di sistema diventerà parte dell'API del kernel, e
dev'essere supportata per un periodo indefinito. Per questo, è davvero
un'ottima idea quella di discutere apertamente l'interfaccia sulla lista
di discussione del kernel, ed è altrettanto importante pianificarne eventuali
estensioni future.
(Nella tabella delle chiamate di sistema sono disseminati esempi dove questo
non fu fatto, assieme ai corrispondenti aggiornamenti -
``eventfd``/``eventfd2``, ``dup2``/``dup3``, ``inotify_init``/``inotify_init1``,
``pipe``/``pipe2``, ``renameat``/``renameat2`` --quindi imparate dalla storia
del kernel e pianificate le estensioni fin dall'inizio)
Per semplici chiamate di sistema che accettano solo un paio di argomenti,
il modo migliore di permettere l'estensibilità è quello di includere un
argomento *flags* alla chiamata di sistema. Per assicurarsi che i programmi
dello spazio utente possano usare in sicurezza *flags* con diverse versioni
del kernel, verificate se *flags* contiene un qualsiasi valore sconosciuto,
in qual caso rifiutate la chiamata di sistema (con ``EINVAL``)::
if (flags & ~(THING_FLAG1 | THING_FLAG2 | THING_FLAG3))
return -EINVAL;
(Se *flags* non viene ancora utilizzato, verificate che l'argomento sia zero)
Per chiamate di sistema più sofisticate che coinvolgono un numero più grande di
argomenti, il modo migliore è quello di incapsularne la maggior parte in una
struttura dati che verrà passata per puntatore. Questa struttura potrà
funzionare con future estensioni includendo un campo *size*::
struct xyzzy_params {
u32 size; /* userspace sets p->size = sizeof(struct xyzzy_params) */
u32 param_1;
u64 param_2;
u64 param_3;
};
Fintanto che un qualsiasi campo nuovo, diciamo ``param_4``, è progettato per
offrire il comportamento precedente quando vale zero, allora questo permetterà
di gestire un conflitto di versione in entrambe le direzioni:
- un vecchio kernel può gestire l'accesso di una versione moderna di un
programma in spazio utente verificando che la memoria oltre la dimensione
della struttura dati attesa sia zero (in pratica verificare che
``param_4 == 0``).
- un nuovo kernel può gestire l'accesso di una versione vecchia di un
programma in spazio utente estendendo la struttura dati con zeri (in pratica
``param_4 = 0``).
Vedere :manpage:`perf_event_open(2)` e la funzione ``perf_copy_attr()`` (in
``kernel/events/core.c``) per un esempio pratico di questo approccio.
Progettare l'API: altre considerazioni
--------------------------------------
Se la vostra nuova chiamata di sistema permette allo spazio utente di fare
riferimento ad un oggetto del kernel, allora questa dovrebbe usare un
descrittore di file per accesso all'oggetto - non inventatevi nuovi tipi di
accesso da spazio utente quando il kernel ha già dei meccanismi e una semantica
ben definita per utilizzare i descrittori di file.
Se la vostra nuova chiamata di sistema :manpage:`xyzzy(2)` ritorna un nuovo
descrittore di file, allora l'argomento *flags* dovrebbe includere un valore
equivalente a ``O_CLOEXEC`` per i nuovi descrittori. Questo rende possibile,
nello spazio utente, la chiusura della finestra temporale fra le chiamate a
``xyzzy()`` e ``fcntl(fd, F_SETFD, FD_CLOEXEC)``, dove un inaspettato
``fork()`` o ``execve()`` potrebbe trasferire il descrittore al programma
eseguito (Comunque, resistete alla tentazione di riutilizzare il valore di
``O_CLOEXEC`` dato che è specifico dell'architettura e fa parte di una
enumerazione di flag ``O_*`` che è abbastanza ricca).
Se la vostra nuova chiamata di sistema ritorna un nuovo descrittore di file,
dovreste considerare che significato avrà l'uso delle chiamate di sistema
della famiglia di :manpage:`poll(2)`. Rendere un descrittore di file pronto
per la lettura o la scrittura è il tipico modo del kernel per notificare lo
spazio utente circa un evento associato all'oggetto del kernel.
Se la vostra nuova chiamata di sistema :manpage:`xyzzy(2)` ha un argomento
che è il percorso ad un file::
int sys_xyzzy(const char __user *path, ..., unsigned int flags);
dovreste anche considerare se non sia più appropriata una versione
:manpage:`xyzzyat(2)`::
int sys_xyzzyat(int dfd, const char __user *path, ..., unsigned int flags);
Questo permette più flessibilità su come lo spazio utente specificherà il file
in questione; in particolare, permette allo spazio utente di richiedere la
funzionalità su un descrittore di file già aperto utilizzando il *flag*
``AT_EMPTY_PATH``, in pratica otterremmo gratuitamente l'operazione
:manpage:`fxyzzy(3)`::
- xyzzyat(AT_FDCWD, path, ..., 0) is equivalent to xyzzy(path,...)
- xyzzyat(fd, "", ..., AT_EMPTY_PATH) is equivalent to fxyzzy(fd, ...)
(Per maggiori dettagli sulla logica delle chiamate \*at(), leggete la pagina
man :manpage:`openat(2)`; per un esempio di AT_EMPTY_PATH, leggere la pagina
man :manpage:`fstatat(2)`).
Se la vostra nuova chiamata di sistema :manpage:`xyzzy(2)` prevede un parametro
per descrivere uno scostamento all'interno di un file, usate ``loff_t`` come
tipo cosicché scostamenti a 64-bit potranno essere supportati anche su
architetture a 32-bit.
Se la vostra nuova chiamata di sistema :manpage:`xyzzy(2)` prevede l'uso di
funzioni riservate, allora dev'essere gestita da un opportuno bit di privilegio
(verificato con una chiamata a ``capable()``), come descritto nella pagina man
:manpage:`capabilities(7)`. Scegliete un bit di privilegio già esistente per
gestire la funzionalità associata, ma evitate la combinazione di diverse
funzionalità vagamente collegate dietro lo stesso bit, in quanto va contro il
principio di *capabilities* di separare i poteri di root. In particolare,
evitate di aggiungere nuovi usi al fin-troppo-generico privilegio
``CAP_SYS_ADMIN``.
Se la vostra nuova chiamata di sistema :manpage:`xyzzy(2)` manipola altri
processi oltre a quello chiamato, allora dovrebbe essere limitata (usando
la chiamata ``ptrace_may_access()``) di modo che solo un processo chiamante
con gli stessi permessi del processo in oggetto, o con i necessari privilegi,
possa manipolarlo.
Infine, state attenti che in alcune architetture non-x86 la vita delle chiamate
di sistema con argomenti a 64-bit viene semplificata se questi argomenti
ricadono in posizioni dispari (pratica, i parametri 1, 3, 5); questo permette
l'uso di coppie contigue di registri a 32-bit. (Questo non conta se gli
argomenti sono parte di una struttura dati che viene passata per puntatore).
Proporre l'API
--------------
Al fine di rendere le nuove chiamate di sistema di facile revisione, è meglio
che dividiate le modifiche i pezzi separati. Questi dovrebbero includere
almeno le seguenti voci in *commit* distinti (ognuno dei quali sarà descritto
più avanti):
- l'essenza dell'implementazione della chiamata di sistema, con i prototipi,
i numeri generici, le modifiche al Kconfig e l'implementazione *stub* di
ripiego.
- preparare la nuova chiamata di sistema per un'architettura specifica,
solitamente x86 (ovvero tutti: x86_64, x86_32 e x32).
- un programma di auto-verifica da mettere in ``tools/testing/selftests/``
che mostri l'uso della chiamata di sistema.
- una bozza di pagina man per la nuova chiamata di sistema. Può essere
scritta nell'email di presentazione, oppure come modifica vera e propria
al repositorio delle pagine man.
Le proposte di nuove chiamate di sistema, come ogni altro modifica all'API del
kernel, deve essere sottomessa alla lista di discussione
Implementazione di chiamate di sistema generiche
------------------------------------------------
Il principale punto d'accesso alla vostra nuova chiamata di sistema
:manpage:`xyzzy(2)` verrà chiamato ``sys_xyzzy()``; ma, piuttosto che in modo
esplicito, lo aggiungerete tramite la macro ``SYSCALL_DEFINEn``. La 'n'
indica il numero di argomenti della chiamata di sistema; la macro ha come
argomento il nome della chiamata di sistema, seguito dalle coppie (tipo, nome)
per definire i suoi parametri. L'uso di questa macro permette di avere
i metadati della nuova chiamata di sistema disponibili anche per altri
strumenti.
Il nuovo punto d'accesso necessita anche del suo prototipo di funzione in
``include/linux/syscalls.h``, marcato come asmlinkage di modo da abbinargli
il modo in cui quelle chiamate di sistema verranno invocate::
asmlinkage long sys_xyzzy(...);
Alcune architetture (per esempio x86) hanno le loro specifiche tabelle di
chiamate di sistema (syscall), ma molte altre architetture condividono una
tabella comune di syscall. Aggiungete alla lista generica la vostra nuova
chiamata di sistema aggiungendo un nuovo elemento alla lista in
``include/uapi/asm-generic/unistd.h``::
#define __NR_xyzzy 292
__SYSCALL(__NR_xyzzy, sys_xyzzy)
Aggiornate anche il contatore __NR_syscalls di modo che sia coerente con
l'aggiunta della nuove chiamate di sistema; va notato che se più di una nuova
chiamata di sistema viene aggiunga nella stessa finestra di sviluppo, il numero
della vostra nuova syscall potrebbe essere aggiustato al fine di risolvere i
conflitti.
Il file ``kernel/sys_ni.c`` fornisce le implementazioni *stub* di ripiego che
ritornano ``-ENOSYS``. Aggiungete la vostra nuova chiamata di sistema anche
qui::
COND_SYSCALL(xyzzy);
La vostra nuova funzionalità del kernel, e la chiamata di sistema che la
controlla, dovrebbero essere opzionali. Quindi, aggiungete un'opzione
``CONFIG`` (solitamente in ``init/Kconfig``). Come al solito per le nuove
opzioni ``CONFIG``:
- Includete una descrizione della nuova funzionalità e della chiamata di
sistema che la controlla.
- Rendete l'opzione dipendente da EXPERT se dev'essere nascosta agli utenti
normali.
- Nel Makefile, rendere tutti i nuovi file sorgenti, che implementano la
nuova funzionalità, dipendenti dall'opzione CONFIG (per esempio
``obj-$(CONFIG_XYZZY_SYSCALL) += xyzzy.o``).
- Controllate due volte che sia possibile generare il kernel con la nuova
opzione CONFIG disabilitata.
Per riassumere, vi serve un *commit* che includa:
- un'opzione ``CONFIG``per la nuova funzione, normalmente in ``init/Kconfig``
- ``SYSCALL_DEFINEn(xyzzy, ...)`` per il punto d'accesso
- il corrispondente prototipo in ``include/linux/syscalls.h``
- un elemento nella tabella generica in ``include/uapi/asm-generic/unistd.h``
- *stub* di ripiego in ``kernel/sys_ni.c``
Implementazione delle chiamate di sistema x86
---------------------------------------------
Per collegare la vostra nuova chiamate di sistema alle piattaforme x86,
dovete aggiornate la tabella principale di syscall. Assumendo che la vostra
nuova chiamata di sistema non sia particolarmente speciale (vedere sotto),
dovete aggiungere un elemento *common* (per x86_64 e x32) in
arch/x86/entry/syscalls/syscall_64.tbl::
333 common xyzzy sys_xyzzy
e un elemento per *i386* ``arch/x86/entry/syscalls/syscall_32.tbl``::
380 i386 xyzzy sys_xyzzy
Ancora una volta, questi numeri potrebbero essere cambiati se generano
conflitti durante la finestra di integrazione.
Chiamate di sistema compatibili (generico)
------------------------------------------
Per molte chiamate di sistema, la stessa implementazione a 64-bit può essere
invocata anche quando il programma in spazio utente è a 32-bit; anche se la
chiamata di sistema include esplicitamente un puntatore, questo viene gestito
in modo trasparente.
Tuttavia, ci sono un paio di situazione dove diventa necessario avere un
livello di gestione della compatibilità per risolvere le differenze di
dimensioni fra 32-bit e 64-bit.
Il primo caso è quando un kernel a 64-bit supporta anche programmi in spazio
utente a 32-bit, perciò dovrà ispezionare aree della memoria (``__user``) che
potrebbero contenere valori a 32-bit o a 64-bit. In particolar modo, questo
è necessario quando un argomento di una chiamata di sistema è:
- un puntatore ad un puntatore
- un puntatore ad una struttura dati contenente a sua volta un puntatore
( ad esempio ``struct iovec __user *``)
- un puntatore ad un tipo intero di dimensione variabile (``time_t``,
``off_t``, ``long``, ...)
- un puntatore ad una struttura dati contenente un tipo intero di dimensione
variabile.
Il secondo caso che richiede un livello di gestione della compatibilità è
quando uno degli argomenti di una chiamata a sistema è esplicitamente un tipo
a 64-bit anche su architetture a 32-bit, per esempio ``loff_t`` o ``__u64``.
In questo caso, un valore che arriva ad un kernel a 64-bit da un'applicazione
a 32-bit verrà diviso in due valori a 32-bit che dovranno essere riassemblati
in questo livello di compatibilità.
(Da notare che non serve questo livello di compatibilità per argomenti che
sono puntatori ad un tipo esplicitamente a 64-bit; per esempio, in
:manpage:`splice(2)` l'argomento di tipo ``loff_t __user *`` non necessita
di una chiamata di sistema ``compat_``)
La versione compatibile della nostra chiamata di sistema si chiamerà
``compat_sys_xyzzy()``, e viene aggiunta utilizzando la macro
``COMPAT_SYSCALL_DEFINEn()`` (simile a SYSCALL_DEFINEn). Questa versione
dell'implementazione è parte del kernel a 64-bit ma accetta parametri a 32-bit
che trasformerà secondo le necessità (tipicamente, la versione
``compat_sys_`` converte questi valori nello loro corrispondente a 64-bit e
può chiamare la versione ``sys_`` oppure invocare una funzione che implementa
le parti comuni).
Il punto d'accesso *compat* deve avere il corrispondente prototipo di funzione
in ``include/linux/compat.h``, marcato come asmlinkage di modo da abbinargli
il modo in cui quelle chiamate di sistema verranno invocate::
asmlinkage long compat_sys_xyzzy(...);
Se la chiamata di sistema prevede una struttura dati organizzata in modo
diverso per sistemi a 32-bit e per quelli a 64-bit, diciamo
``struct xyzzy_args``, allora il file d'intestazione
``then the include/linux/compat.h`` deve includere la sua versione
*compatibile* (``struct compat_xyzzy_args``); ogni variabile con
dimensione variabile deve avere il proprio tipo ``compat_`` corrispondente
a quello in ``struct xyzzy_args``. La funzione ``compat_sys_xyzzy()``
può usare la struttura ``compat_`` per analizzare gli argomenti ricevuti
da una chiamata a 32-bit.
Per esempio, se avete i seguenti campi::
struct xyzzy_args {
const char __user *ptr;
__kernel_long_t varying_val;
u64 fixed_val;
/* ... */
};
nella struttura ``struct xyzzy_args``, allora la struttura
``struct compat_xyzzy_args`` dovrebbe avere::
struct compat_xyzzy_args {
compat_uptr_t ptr;
compat_long_t varying_val;
u64 fixed_val;
/* ... */
};
La lista generica delle chiamate di sistema ha bisogno di essere
aggiustata al fine di permettere l'uso della versione *compatibile*;
la voce in ``include/uapi/asm-generic/unistd.h`` dovrebbero usare
``__SC_COMP`` piuttosto di ``__SYSCALL``::
#define __NR_xyzzy 292
__SC_COMP(__NR_xyzzy, sys_xyzzy, compat_sys_xyzzy)
Riassumendo, vi serve:
- un ``COMPAT_SYSCALL_DEFINEn(xyzzy, ...)`` per il punto d'accesso
*compatibile*
- un prototipo in ``include/linux/compat.h``
- (se necessario) una struttura di compatibilità a 32-bit in
``include/linux/compat.h``
- una voce ``__SC_COMP``, e non ``__SYSCALL``, in
``include/uapi/asm-generic/unistd.h``
Compatibilità delle chiamate di sistema (x86)
---------------------------------------------
Per collegare una chiamata di sistema, su un'architettura x86, con la sua
versione *compatibile*, è necessario aggiustare la voce nella tabella
delle syscall.
Per prima cosa, la voce in ``arch/x86/entry/syscalls/syscall_32.tbl`` prende
un argomento aggiuntivo per indicare che un programma in spazio utente
a 32-bit, eseguito su un kernel a 64-bit, dovrebbe accedere tramite il punto
d'accesso compatibile::
380 i386 xyzzy sys_xyzzy __ia32_compat_sys_xyzzy
Secondo, dovete capire cosa dovrebbe succedere alla nuova chiamata di sistema
per la versione dell'ABI x32. Qui C'è una scelta da fare: gli argomenti
possono corrisponde alla versione a 64-bit o a quella a 32-bit.
Se c'è un puntatore ad un puntatore, la decisione è semplice: x32 è ILP32,
quindi gli argomenti dovrebbero corrispondere a quelli a 32-bit, e la voce in
``arch/x86/entry/syscalls/syscall_64.tbl`` sarà divisa cosicché i programmi
x32 eseguano la chiamata *compatibile*::
333 64 xyzzy sys_xyzzy
...
555 x32 xyzzy __x32_compat_sys_xyzzy
Se non ci sono puntatori, allora è preferibile riutilizzare la chiamata di
sistema a 64-bit per l'ABI x32 (e di conseguenza la voce in
arch/x86/entry/syscalls/syscall_64.tbl rimane immutata).
In ambo i casi, dovreste verificare che i tipi usati dagli argomenti
abbiano un'esatta corrispondenza da x32 (-mx32) al loro equivalente a
32-bit (-m32) o 64-bit (-m64).
Chiamate di sistema che ritornano altrove
-----------------------------------------
Nella maggior parte delle chiamate di sistema, al termine della loro
esecuzione, i programmi in spazio utente riprendono esattamente dal punto
in cui si erano interrotti -- quindi dall'istruzione successiva, con lo
stesso *stack* e con la maggior parte del registri com'erano stati
lasciati prima della chiamata di sistema, e anche con la stessa memoria
virtuale.
Tuttavia, alcune chiamata di sistema fanno le cose in modo differente.
Potrebbero ritornare ad un punto diverso (``rt_sigreturn``) o cambiare
la memoria in spazio utente (``fork``/``vfork``/``clone``) o perfino
l'architettura del programma (``execve``/``execveat``).
Per permettere tutto ciò, l'implementazione nel kernel di questo tipo di
chiamate di sistema potrebbero dover salvare e ripristinare registri
aggiuntivi nello *stack* del kernel, permettendo così un controllo completo
su dove e come l'esecuzione dovrà continuare dopo l'esecuzione della
chiamata di sistema.
Queste saranno specifiche per ogni architettura, ma tipicamente si definiscono
dei punti d'accesso in *assembly* per salvare/ripristinare i registri
aggiuntivi e quindi chiamare il vero punto d'accesso per la chiamata di
sistema.
Per l'architettura x86_64, questo è implementato come un punto d'accesso
``stub_xyzzy`` in ``arch/x86/entry/entry_64.S``, e la voce nella tabella
di syscall (``arch/x86/entry/syscalls/syscall_64.tbl``) verrà corretta di
conseguenza::
333 common xyzzy stub_xyzzy
L'equivalente per programmi a 32-bit eseguiti su un kernel a 64-bit viene
normalmente chiamato ``stub32_xyzzy`` e implementato in
``arch/x86/entry/entry_64_compat.S`` con la corrispondente voce nella tabella
di syscall ``arch/x86/entry/syscalls/syscall_32.tbl`` corretta nel
seguente modo::
380 i386 xyzzy sys_xyzzy stub32_xyzzy
Se una chiamata di sistema necessita di un livello di compatibilità (come
nella sezione precedente), allora la versione ``stub32_`` deve invocare
la versione ``compat_sys_`` piuttosto che quella nativa a 64-bit. In aggiunta,
se l'implementazione dell'ABI x32 è diversa da quella x86_64, allora la sua
voce nella tabella di syscall dovrà chiamare uno *stub* che invoca la versione
``compat_sys_``,
Per completezza, sarebbe carino impostare una mappatura cosicché
*user-mode* Linux (UML) continui a funzionare -- la sua tabella di syscall
farà riferimento a stub_xyzzy, ma UML non include l'implementazione
in ``arch/x86/entry/entry_64.S`` (perché UML simula i registri eccetera).
Correggerlo è semplice, basta aggiungere una #define in
``arch/x86/um/sys_call_table_64.c``::
#define stub_xyzzy sys_xyzzy
Altri dettagli
--------------
La maggior parte dei kernel tratta le chiamate di sistema allo stesso modo,
ma possono esserci rare eccezioni per le quali potrebbe essere necessario
l'aggiornamento della vostra chiamata di sistema.
Il sotto-sistema di controllo (*audit subsystem*) è uno di questi casi
speciali; esso include (per architettura) funzioni che classificano alcuni
tipi di chiamate di sistema -- in particolare apertura dei file
(``open``/``openat``), esecuzione dei programmi (``execve``/``exeveat``)
oppure multiplatori di socket (``socketcall``). Se la vostra nuova chiamata
di sistema è simile ad una di queste, allora il sistema di controllo dovrebbe
essere aggiornato.
Più in generale, se esiste una chiamata di sistema che è simile alla vostra,
vale la pena fare una ricerca con ``grep`` su tutto il kernel per la chiamata
di sistema esistente per verificare che non ci siano altri casi speciali.
Verifica
--------
Una nuova chiamata di sistema dev'essere, ovviamente, provata; è utile fornire
ai revisori un programma in spazio utente che mostri l'uso della chiamata di
sistema. Un buon modo per combinare queste cose è quello di aggiungere un
semplice programma di auto-verifica in una nuova cartella in
``tools/testing/selftests/``.
Per una nuova chiamata di sistema, ovviamente, non ci sarà alcuna funzione
in libc e quindi il programma di verifica dovrà invocarla usando ``syscall()``;
inoltre, se la nuova chiamata di sistema prevede un nuova struttura dati
visibile in spazio utente, il file d'intestazione necessario dev'essere
installato al fine di compilare il programma.
Assicuratevi che il programma di auto-verifica possa essere eseguito
correttamente su tutte le architetture supportate. Per esempio, verificate che
funzioni quando viene compilato per x86_64 (-m64), x86_32 (-m32) e x32 (-mx32).
Al fine di una più meticolosa ed estesa verifica della nuova funzionalità,
dovreste considerare l'aggiunta di nuove verifica al progetto 'Linux Test',
oppure al progetto xfstests per cambiamenti relativi al filesystem.
- https://linux-test-project.github.io/
- git://git.kernel.org/pub/scm/fs/xfs/xfstests-dev.git
Pagine man
----------
Tutte le nuove chiamate di sistema dovrebbero avere una pagina man completa,
idealmente usando i marcatori groff, ma anche il puro testo può andare. Se
state usando groff, è utile che includiate nella email di presentazione una
versione già convertita in formato ASCII: semplificherà la vita dei revisori.
Le pagine man dovrebbero essere in copia-conoscenza verso
Per maggiori dettagli, leggere
https://www.kernel.org/doc/man-pages/patches.html
Non invocate chiamate di sistema dal kernel
-------------------------------------------
Le chiamate di sistema sono, come già detto prima, punti di interazione fra
lo spazio utente e il kernel. Perciò, le chiamate di sistema come
``sys_xyzzy()`` o ``compat_sys_xyzzy()`` dovrebbero essere chiamate solo dallo
spazio utente attraverso la tabella syscall, ma non da nessun altro punto nel
kernel. Se la nuova funzionalità è utile all'interno del kernel, per esempio
dev'essere condivisa fra una vecchia e una nuova chiamata di sistema o
dev'essere utilizzata da una chiamata di sistema e la sua variante compatibile,
allora dev'essere implementata come una funzione di supporto
(*helper function*) (per esempio ``ksys_xyzzy()``). Questa funzione potrà
essere chiamata dallo *stub* (``sys_xyzzy()``), dalla variante compatibile
(``compat_sys_xyzzy()``), e/o da altri parti del kernel.
Sui sistemi x86 a 64-bit, a partire dalla versione v4.17 è un requisito
fondamentale quello di non invocare chiamate di sistema all'interno del kernel.
Esso usa una diversa convenzione per l'invocazione di chiamate di sistema dove
``struct pt_regs`` viene decodificata al volo in una funzione che racchiude
la chiamata di sistema la quale verrà eseguita successivamente.
Questo significa che verranno passati solo i parametri che sono davvero
necessari ad una specifica chiamata di sistema, invece che riempire ogni volta
6 registri del processore con contenuti presi dallo spazio utente (potrebbe
causare seri problemi nella sequenza di chiamate).
Inoltre, le regole su come i dati possano essere usati potrebbero differire
fra il kernel e l'utente. Questo è un altro motivo per cui invocare
``sys_xyzzy()`` è generalmente una brutta idea.
Eccezioni a questa regola vengono accettate solo per funzioni d'architetture
che surclassano quelle generiche, per funzioni d'architettura di compatibilità,
o per altro codice in arch/
Riferimenti e fonti
-------------------
- Articolo di Michael Kerris su LWN sull'uso dell'argomento flags nelle
chiamate di sistema: https://lwn.net/Articles/585415/
- Articolo di Michael Kerris su LWN su come gestire flag sconosciuti in
una chiamata di sistema: https://lwn.net/Articles/588444/
- Articolo di Jake Edge su LWN che descrive i limiti degli argomenti a 64-bit
delle chiamate di sistema: https://lwn.net/Articles/311630/
- Una coppia di articoli di David Drysdale che descrivono i dettagli del
percorso implementativo di una chiamata di sistema per la versione v3.14:
- https://lwn.net/Articles/604287/
- https://lwn.net/Articles/604515/
- Requisiti specifici alle architetture sono discussi nella pagina man
:manpage:`syscall(2)` :
http://man7.org/linux/man-pages/man2/syscall.2.html#NOTES
- Collezione di email di Linux Torvalds sui problemi relativi a ``ioctl()``:
http://yarchive.net/comp/linux/ioctl.html
- "Come non inventare interfacce del kernel", Arnd Bergmann,
http://www.ukuug.org/events/linux2007/2007/papers/Bergmann.pdf
- Articolo di Michael Kerris su LWN sull'evitare nuovi usi di CAP_SYS_ADMIN:
https://lwn.net/Articles/486306/
- Raccomandazioni da Andrew Morton circa il fatto che tutte le informazioni
su una nuova chiamata di sistema dovrebbero essere contenute nello stesso
filone di discussione di email: https://lore.kernel.org/r/[email protected]
- Raccomandazioni da Michael Kerrisk circa il fatto che le nuove chiamate di
sistema dovrebbero avere una pagina man: https://lore.kernel.org/r/CAKgNAkgMA39AfoSoA5Pe1r9N+ZzfYQNvNPvcRN7tOvRb8+v06Q@mail.gmail.com
- Consigli da Thomas Gleixner sul fatto che il collegamento all'architettura
x86 dovrebbe avvenire in un *commit* differente:
https://lore.kernel.org/r/alpine.DEB.2.11.1411191249560.3909@nanos
- Consigli da Greg Kroah-Hartman circa la bontà d'avere una pagina man e un
programma di auto-verifica per le nuove chiamate di sistema:
https://lore.kernel.org/r/[email protected]
- Discussione di Michael Kerrisk sulle nuove chiamate di sistema contro
le estensioni :manpage:`prctl(2)`: https://lore.kernel.org/r/CAHO5Pa3F2MjfTtfNxa8LbnkeeU8=YJ+9tDqxZpw7Gz59E-4AUg@mail.gmail.com
- Consigli da Ingo Molnar che le chiamate di sistema con più argomenti
dovrebbero incapsularli in una struttura che includa un argomento
*size* per garantire l'estensibilità futura:
https://lore.kernel.org/r/[email protected]
- Un certo numero di casi strani emersi dall'uso (riuso) dei flag O_*:
- commit 75069f2b5bfb ("vfs: renumber FMODE_NONOTIFY and add to uniqueness
check")
- commit 12ed2e36c98a ("fanotify: FMODE_NONOTIFY and __O_SYNC in sparc
conflict")
- commit bb458c644a59 ("Safer ABI for O_TMPFILE")
- Discussion from Matthew Wilcox about restrictions on 64-bit arguments:
https://lore.kernel.org/r/[email protected]
- Raccomandazioni da Greg Kroah-Hartman sul fatto che i flag sconosciuti dovrebbero
essere controllati: https://lore.kernel.org/r/[email protected]
- Raccomandazioni da Linus Torvalds che le chiamate di sistema x32 dovrebbero
favorire la compatibilità con le versioni a 64-bit piuttosto che quelle a 32-bit:
https://lore.kernel.org/r/CA+55aFxfmwfB7jbbrXxa=K7VBYPfAvmu3XOkGrLbB1UFjX1+Ew@mail.gmail.com
3. 한국어 전문 번역
영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.
system call 대신 쓸 수 있는 interface
1-62이 문서는 Documentation/process/submitting-patches.rst의 일반적인 제출 지침에 더해 Linux kernel에 새 system call을 추가할 때 필요한 사항을 설명한다.
가장 먼저 검토할 것은 system call이 아닌 다른 interface가 더 적합한지다. System call은 userspace와 kernel이 상호 작용하는 가장 전통적이고 분명한 지점이지만 유일한 방법은 아니므로 interface 성격에 가장 잘 맞는 방식을 선택해야 한다.
Filesystem 또는 device
관련 operation을 filesystem-like object로 표현할 수 있다면 새 filesystem이나 device를 만드는 편이 더 적절할 수 있다. 이 방식은 기능을 main kernel에 built-in으로 넣지 않고 kernel module로 캡슐화하기도 쉽다.
Kernel이 어떤 사건의 발생을 userspace에 알리는 기능이라면 관련 object의 새 file descriptor를 반환하게 만들 수 있다. 그러면 userspace는 poll/select/epoll로 notification을 받을 수 있다. 반면 read(2)나 write(2) 형태로 대응되지 않는 operation은 ioctl(2) request로 구현해야 하므로 API가 다소 불투명해질 수 있다.
sysfs, /proc, fcntl(2), prctl(2)
Runtime system information을 노출하기만 한다면 Documentation/filesystems/sysfs.rst가 설명하는 sysfs의 새 node나 /proc filesystem이 더 적절할 수 있다. 다만 이 mechanism에 접근하려면 해당 filesystem이 mount되어 있어야 하며 namespaced, sandboxed, chrooted environment에서는 그렇지 않을 수 있다. Debugfs는 production userspace interface로 간주되지 않으므로 API를 추가해서는 안 된다.
특정 file이나 file descriptor에만 적용되는 operation이면 fcntl(2) command option을 추가하는 방안이 더 적절할 수 있다. 그러나 fcntl(2)은 많은 복잡성을 감추는 multiplexing system call이므로 기존 fcntl(2) 기능과 매우 유사하거나 file descriptor 관련 단순 flag를 얻고 설정하는 정도의 기능에 가장 알맞다.
특정 task나 process에 적용되는 operation이면 prctl(2) command option을 추가할 수 있다. prctl(2) 역시 복잡한 multiplexor이므로 기존 prctl() command와 거의 같은 기능이나 process 관련 단순 flag의 get/set에 한정하는 편이 좋다.
새 system call은 영구 user-space ABI가 되므로 먼저 filesystem·device, sysfs나 `/proc`, `fcntl(2)`, `prctl(2)` 같은 기존 interface가 더 자연스러운지 검토합니다. Event notification은 file descriptor를 반환해 `poll`·`select`·`epoll`과 결합할 수 있습니다.
Production interface를 debugfs에 두지 않으며, namespace·sandbox·chroot 환경에서 sysfs나 `/proc`가 항상 mount된다고 가정해서도 안 됩니다. Multiplexor인 `fcntl`과 `prctl`은 기존 operation과 밀접하거나 단순 flag를 다룰 때만 확장하는 편이 좋습니다.
.. include:: ../disclaimer-ita.rst
:Original: :ref:`Documentation/process/adding-syscalls.rst <addsyscalls>`
:Translator: Federico Vaga <[email protected]>
.. _it_addsyscalls:
Aggiungere una nuova chiamata di sistema
========================================
Questo documento descrive quello che è necessario sapere per aggiungere
nuove chiamate di sistema al kernel Linux; questo è da considerarsi come
un'aggiunta ai soliti consigli su come proporre nuove modifiche
:ref:`Documentation/translations/it_IT/process/submitting-patches.rst <it_submittingpatches>`.
Alternative alle chiamate di sistema
------------------------------------
La prima considerazione da fare quando si aggiunge una nuova chiamata di
sistema è quella di valutare le alternative. Nonostante le chiamate di sistema
siano il punto di interazione fra spazio utente e kernel più tradizionale ed
ovvio, esistono altre possibilità - scegliete quella che meglio si adatta alle
vostra interfaccia.
- Se le operazioni coinvolte possono rassomigliare a quelle di un filesystem,
allora potrebbe avere molto più senso la creazione di un nuovo filesystem o
dispositivo. Inoltre, questo rende più facile incapsulare la nuova
funzionalità in un modulo kernel piuttosto che essere sviluppata nel cuore
del kernel.
- Se la nuova funzionalità prevede operazioni dove il kernel notifica
lo spazio utente su un avvenimento, allora restituire un descrittore
di file all'oggetto corrispondente permette allo spazio utente di
utilizzare ``poll``/``select``/``epoll`` per ricevere quelle notifiche.
- Tuttavia, le operazioni che non si sposano bene con operazioni tipo
:manpage:`read(2)`/:manpage:`write(2)` dovrebbero essere implementate
come chiamate :manpage:`ioctl(2)`, il che potrebbe portare ad un'API in
un qualche modo opaca.
- Se dovete esporre solo delle informazioni sul sistema, un nuovo nodo in
sysfs (vedere ``Documentation/filesystems/sysfs.rst``) o
in procfs potrebbe essere sufficiente. Tuttavia, l'accesso a questi
meccanismi richiede che il filesystem sia montato, il che potrebbe non
essere sempre vero (per esempio, in ambienti come namespace/sandbox/chroot).
Evitate d'aggiungere nuove API in debugfs perché questo non viene
considerata un'interfaccia di 'produzione' verso lo spazio utente.
- Se l'operazione è specifica ad un particolare file o descrittore, allora
potrebbe essere appropriata l'aggiunta di un comando :manpage:`fcntl(2)`.
Tuttavia, :manpage:`fcntl(2)` è una chiamata di sistema multiplatrice che
nasconde una notevole complessità, quindi è ottima solo quando la nuova
funzione assomiglia a quelle già esistenti in :manpage:`fcntl(2)`, oppure
la nuova funzionalità è veramente semplice (per esempio, leggere/scrivere
un semplice flag associato ad un descrittore di file).
- Se l'operazione è specifica ad un particolare processo, allora
potrebbe essere appropriata l'aggiunta di un comando :manpage:`prctl(2)`.
Come per :manpage:`fcntl(2)`, questa chiamata di sistema è un complesso
multiplatore quindi è meglio usarlo per cose molto simili a quelle esistenti
nel comando ``prctl`` oppure per leggere/scrivere un semplice flag relativo
al processo.
flags와 size field로 미래 확장 설계
63-117새 system call은 kernel API의 일부가 되며 사실상 무기한 지원해야 한다. 따라서 interface를 kernel mailing list에서 명시적으로 논의하고 처음부터 향후 확장을 계획하는 것이 중요하다.
Syscall table에는 확장을 미리 고려하지 않아 후속 system call이 생긴 역사적 사례가 많다. eventfd/eventfd2, dup2/dup3, inotify_init/inotify_init1, pipe/pipe2, renameat/renameat2가 그 예다. Kernel의 역사를 보고 처음부터 확장 지점을 설계해야 한다.
Argument가 적으면 flags를 둔다
Argument가 두어 개뿐인 단순한 system call은 향후 확장을 위해 flags argument를 두는 방식이 선호된다. 서로 다른 kernel version에서 userspace program이 flag를 안전하게 쓰게 하려면 알려지지 않은 bit가 있는지 검사하고, 있으면 EINVAL로 system call을 거부한다.
if (flags & ~(THING_FLAG1 | THING_FLAG2 | THING_FLAG3))
return -EINVAL;
아직 정의된 flag가 하나도 없다면 flags argument가 0인지 검사한다.
Argument가 많으면 size field가 있는 struct를 쓴다
Argument가 많은 복잡한 system call은 대부분의 argument를 structure에 넣고 pointer로 전달하는 방식이 선호된다. Structure 안에 size argument를 두면 미래의 field 추가를 처리할 수 있다.
struct xyzzy_params {
u32 size; /* userspace sets p->size = sizeof(struct xyzzy_params) */
u32 param_1;
u64 param_2;
u64 param_3;
};
나중에 추가하는 param_4 같은 field의 0 값이 과거 동작을 뜻하도록 설계하면 양방향 version mismatch를 모두 처리할 수 있다.
- 새 userspace program이 오래된 kernel을 호출할 때: kernel이 알고 있는 structure 크기 너머의 memory가 모두 0인지 검사한다. 사실상 param_4 == 0을 확인하는 셈이다.
- 오래된 userspace program이 새 kernel을 호출할 때: kernel은 작은 structure instance를 0으로 확장해 사실상 param_4 = 0으로 만든다.
이 pattern의 실제 예는 perf_event_open(2)과 kernel/events/core.c의 perf_copy_attr()에서 볼 수 있다.
Argument가 적은 call에는 `flags`를 두고 알려지지 않은 bit가 하나라도 있으면 `EINVAL`로 거부합니다. 아직 정의된 flag가 없어도 0인지 검사하면 미래 kernel과 과거 userspace의 의미 충돌을 막을 수 있습니다.
Argument가 많으면 `size` field를 첫 부분에 둔 structure를 pointer로 전달합니다. 새 field의 0 값이 과거 동작을 뜻하게 만들면 새 program이 오래된 kernel을 호출하거나 오래된 program이 새 kernel을 호출하는 양방향 version mismatch를 안전하게 처리할 수 있습니다.
Argument 수와 향후 확장 방식에 따른 권장 pattern입니다.
Progettare l'API: pianificare le estensioni
-------------------------------------------
Una nuova chiamata di sistema diventerà parte dell'API del kernel, e
dev'essere supportata per un periodo indefinito. Per questo, è davvero
un'ottima idea quella di discutere apertamente l'interfaccia sulla lista
di discussione del kernel, ed è altrettanto importante pianificarne eventuali
estensioni future.
(Nella tabella delle chiamate di sistema sono disseminati esempi dove questo
non fu fatto, assieme ai corrispondenti aggiornamenti -
``eventfd``/``eventfd2``, ``dup2``/``dup3``, ``inotify_init``/``inotify_init1``,
``pipe``/``pipe2``, ``renameat``/``renameat2`` --quindi imparate dalla storia
del kernel e pianificate le estensioni fin dall'inizio)
Per semplici chiamate di sistema che accettano solo un paio di argomenti,
il modo migliore di permettere l'estensibilità è quello di includere un
argomento *flags* alla chiamata di sistema. Per assicurarsi che i programmi
dello spazio utente possano usare in sicurezza *flags* con diverse versioni
del kernel, verificate se *flags* contiene un qualsiasi valore sconosciuto,
in qual caso rifiutate la chiamata di sistema (con ``EINVAL``)::
if (flags & ~(THING_FLAG1 | THING_FLAG2 | THING_FLAG3))
return -EINVAL;
(Se *flags* non viene ancora utilizzato, verificate che l'argomento sia zero)
Per chiamate di sistema più sofisticate che coinvolgono un numero più grande di
argomenti, il modo migliore è quello di incapsularne la maggior parte in una
struttura dati che verrà passata per puntatore. Questa struttura potrà
funzionare con future estensioni includendo un campo *size*::
struct xyzzy_params {
u32 size; /* userspace sets p->size = sizeof(struct xyzzy_params) */
u32 param_1;
u64 param_2;
u64 param_3;
};
Fintanto che un qualsiasi campo nuovo, diciamo ``param_4``, è progettato per
offrire il comportamento precedente quando vale zero, allora questo permetterà
di gestire un conflitto di versione in entrambe le direzioni:
- un vecchio kernel può gestire l'accesso di una versione moderna di un
programma in spazio utente verificando che la memoria oltre la dimensione
della struttura dati attesa sia zero (in pratica verificare che
``param_4 == 0``).
- un nuovo kernel può gestire l'accesso di una versione vecchia di un
programma in spazio utente estendendo la struttura dati con zeri (in pratica
``param_4 = 0``).
Vedere :manpage:`perf_event_open(2)` e la funzione ``perf_copy_attr()`` (in
``kernel/events/core.c``) per un esempio pratico di questo approccio.
file descriptor·pathname·권한과 proposal 구성
118-217새 system call이 userspace에서 kernel object를 가리키게 한다면 그 object handle로 file descriptor를 사용해야 한다. Kernel에 file descriptor를 위한 mechanism과 잘 정의된 semantics가 이미 있으므로 새로운 userspace object handle type을 만들지 않는다.
새 FD와 close-on-exec
새 xyzzy(2)가 file descriptor를 반환한다면 flags argument에는 새 FD에 O_CLOEXEC를 설정한 것과 같은 값을 포함해야 한다. 그래야 xyzzy() 반환 뒤 fcntl(fd, F_SETFD, FD_CLOEXEC)를 호출하기 전의 timing window를 없앨 수 있다. 이 window에서 다른 thread가 예기치 않게 fork()와 execve()를 수행하면 descriptor가 exec된 program으로 새어 나갈 수 있다.
실제 O_CLOEXEC 상수 값을 그대로 재사용하려는 유혹은 피한다. 이 값은 architecture-specific이고 이미 상당히 가득 찬 O_* flag numbering space의 일부다.
새 FD를 반환한다면 그 descriptor에 poll(2) 계열 system call을 적용했을 때의 의미도 설계해야 한다. FD를 read-ready 또는 write-ready로 만드는 것은 해당 kernel object에서 event가 발생했음을 kernel이 userspace에 알리는 일반적인 방법이다.
Pathname API는 *at 형태를 검토한다
xyzzy(2)가 filename argument를 받는다면 xyzzyat(2) 형태가 더 적절한지 검토한다.
int sys_xyzzy(const char __user *path, ..., unsigned int flags);
int sys_xyzzyat(int dfd, const char __user *path, ..., unsigned int flags);
xyzzyat(2)는 userspace가 대상 file을 지정하는 방법을 더 유연하게 만든다. 특히 AT_EMPTY_PATH를 사용하면 이미 open한 file descriptor에 기능을 적용할 수 있어 사실상 fxyzzy(3) operation까지 별도 system call 없이 얻는다.
xyzzyat(AT_FDCWD, path, ..., 0) == xyzzy(path, ...)
xyzzyat(fd, "", ..., AT_EMPTY_PATH) == fxyzzy(fd, ...)
*at() call의 근거는 openat(2), AT_EMPTY_PATH 예는 fstatat(2) man page에서 확인할 수 있다.
Offset, privilege와 target process
File 내부 offset을 나타내는 parameter는 32-bit architecture에서도 64-bit offset을 지원하도록 loff_t를 사용한다.
Privileged functionality는 capabilities(7)에 설명된 적절한 Linux capability bit로 통제하고 capable()로 검사한다. 관련 기능을 다스리는 기존 capability bit를 선택하되, root 권한을 나누려는 capability의 목적을 해치지 않도록 느슨하게 관련된 기능을 하나의 bit에 지나치게 묶지 않는다. 특히 이미 지나치게 일반적인 CAP_SYS_ADMIN의 새 사용은 피한다.
호출 process가 아닌 다른 process를 조작하는 system call은 ptrace_may_access()로 제한한다. Target process와 같은 permission을 가진 caller 또는 필요한 capability가 있는 caller만 target을 조작할 수 있어야 한다.
일부 non-x86 architecture는 명시적인 64-bit parameter가 홀수 번째 argument, 즉 1·3·5번에 있을 때 연속된 32-bit register 두 개를 사용하기 쉽다. Argument가 pointer로 전달되는 structure 안에 있다면 이 고려 사항은 적용되지 않는다.
새 system call을 쉽게 review할 수 있도록 patchset을 독립된 묶음으로 나누는 것이 좋다. 최소한 다음 항목은 서로 다른 commit이어야 한다.
- System call core implementation, prototype, generic numbering, Kconfig 변경, fallback stub implementation
- 특정 architecture의 wiring. 보통 x86이며 x86_64, x86_32, x32를 모두 포함한다.
- tools/testing/selftests/ 아래 selftest로 작성한 userspace 사용 예
- Cover letter에 plain text로 넣거나 별도 man-pages repository patch로 제출하는 새 system call의 draft man page
Kernel API의 다른 변경과 마찬가지로 새 system call proposal은 항상 [email protected]를 CC해야 한다.
Core API와 architecture wiring, userspace 검증, 문서를 분리하면 각 책임과 review 범위가 분명해진다.
Kernel object handle에는 새 종류를 만들지 말고 file descriptor를 사용합니다. 새 FD를 반환한다면 atomic close-on-exec flag와 `poll(2)` 의미를 설계하고 실제 architecture-specific `O_CLOEXEC` 값은 임의로 재사용하지 않습니다.
Path를 받는 call은 `*at` 형태와 `AT_EMPTY_PATH`를 검토하고 file offset에는 `loff_t`를 씁니다. Privileged operation은 적절한 capability와 `capable()`로 제한하되 `CAP_SYS_ADMIN`에 무관한 권한을 계속 추가하지 않으며, 다른 process 조작은 `ptrace_may_access()`로 검사합니다.
Patchset은 core implementation·generic ABI, x86 wiring, `tools/testing/selftests/` selftest, draft man page를 독립 commit으로 나눕니다. 새 kernel API 제안은 `[email protected]`에 보내 관련 설계와 userspace 영향을 한 thread에서 review받습니다.
Progettare l'API: altre considerazioni
--------------------------------------
Se la vostra nuova chiamata di sistema permette allo spazio utente di fare
riferimento ad un oggetto del kernel, allora questa dovrebbe usare un
descrittore di file per accesso all'oggetto - non inventatevi nuovi tipi di
accesso da spazio utente quando il kernel ha già dei meccanismi e una semantica
ben definita per utilizzare i descrittori di file.
Se la vostra nuova chiamata di sistema :manpage:`xyzzy(2)` ritorna un nuovo
descrittore di file, allora l'argomento *flags* dovrebbe includere un valore
equivalente a ``O_CLOEXEC`` per i nuovi descrittori. Questo rende possibile,
nello spazio utente, la chiusura della finestra temporale fra le chiamate a
``xyzzy()`` e ``fcntl(fd, F_SETFD, FD_CLOEXEC)``, dove un inaspettato
``fork()`` o ``execve()`` potrebbe trasferire il descrittore al programma
eseguito (Comunque, resistete alla tentazione di riutilizzare il valore di
``O_CLOEXEC`` dato che è specifico dell'architettura e fa parte di una
enumerazione di flag ``O_*`` che è abbastanza ricca).
Se la vostra nuova chiamata di sistema ritorna un nuovo descrittore di file,
dovreste considerare che significato avrà l'uso delle chiamate di sistema
della famiglia di :manpage:`poll(2)`. Rendere un descrittore di file pronto
per la lettura o la scrittura è il tipico modo del kernel per notificare lo
spazio utente circa un evento associato all'oggetto del kernel.
Se la vostra nuova chiamata di sistema :manpage:`xyzzy(2)` ha un argomento
che è il percorso ad un file::
int sys_xyzzy(const char __user *path, ..., unsigned int flags);
dovreste anche considerare se non sia più appropriata una versione
:manpage:`xyzzyat(2)`::
int sys_xyzzyat(int dfd, const char __user *path, ..., unsigned int flags);
Questo permette più flessibilità su come lo spazio utente specificherà il file
in questione; in particolare, permette allo spazio utente di richiedere la
funzionalità su un descrittore di file già aperto utilizzando il *flag*
``AT_EMPTY_PATH``, in pratica otterremmo gratuitamente l'operazione
:manpage:`fxyzzy(3)`::
- xyzzyat(AT_FDCWD, path, ..., 0) is equivalent to xyzzy(path,...)
- xyzzyat(fd, "", ..., AT_EMPTY_PATH) is equivalent to fxyzzy(fd, ...)
(Per maggiori dettagli sulla logica delle chiamate \*at(), leggete la pagina
man :manpage:`openat(2)`; per un esempio di AT_EMPTY_PATH, leggere la pagina
man :manpage:`fstatat(2)`).
Se la vostra nuova chiamata di sistema :manpage:`xyzzy(2)` prevede un parametro
per descrivere uno scostamento all'interno di un file, usate ``loff_t`` come
tipo cosicché scostamenti a 64-bit potranno essere supportati anche su
architetture a 32-bit.
Se la vostra nuova chiamata di sistema :manpage:`xyzzy(2)` prevede l'uso di
funzioni riservate, allora dev'essere gestita da un opportuno bit di privilegio
(verificato con una chiamata a ``capable()``), come descritto nella pagina man
:manpage:`capabilities(7)`. Scegliete un bit di privilegio già esistente per
gestire la funzionalità associata, ma evitate la combinazione di diverse
funzionalità vagamente collegate dietro lo stesso bit, in quanto va contro il
principio di *capabilities* di separare i poteri di root. In particolare,
evitate di aggiungere nuovi usi al fin-troppo-generico privilegio
``CAP_SYS_ADMIN``.
Se la vostra nuova chiamata di sistema :manpage:`xyzzy(2)` manipola altri
processi oltre a quello chiamato, allora dovrebbe essere limitata (usando
la chiamata ``ptrace_may_access()``) di modo che solo un processo chiamante
con gli stessi permessi del processo in oggetto, o con i necessari privilegi,
possa manipolarlo.
Infine, state attenti che in alcune architetture non-x86 la vita delle chiamate
di sistema con argomenti a 64-bit viene semplificata se questi argomenti
ricadono in posizioni dispari (pratica, i parametri 1, 3, 5); questo permette
l'uso di coppie contigue di registri a 32-bit. (Questo non conta se gli
argomenti sono parte di una struttura dati che viene passata per puntatore).
Proporre l'API
--------------
Al fine di rendere le nuove chiamate di sistema di facile revisione, è meglio
che dividiate le modifiche i pezzi separati. Questi dovrebbero includere
almeno le seguenti voci in *commit* distinti (ognuno dei quali sarà descritto
più avanti):
- l'essenza dell'implementazione della chiamata di sistema, con i prototipi,
i numeri generici, le modifiche al Kconfig e l'implementazione *stub* di
ripiego.
- preparare la nuova chiamata di sistema per un'architettura specifica,
solitamente x86 (ovvero tutti: x86_64, x86_32 e x32).
- un programma di auto-verifica da mettere in ``tools/testing/selftests/``
che mostri l'uso della chiamata di sistema.
- una bozza di pagina man per la nuova chiamata di sistema. Può essere
scritta nell'email di presentazione, oppure come modifica vera e propria
al repositorio delle pagine man.
Le proposte di nuove chiamate di sistema, come ogni altro modifica all'API del
kernel, deve essere sottomessa alla lista di discussione
[email protected].
generic implementation과 fallback
218-280새 xyzzy(2)의 주 entry point 이름은 sys_xyzzy()지만 function을 직접 선언해 정의하지 않고 적절한 SYSCALL_DEFINEn() macro로 추가한다. n은 system call argument 수다. Macro에는 system call 이름 뒤에 각 parameter의 (type, name) pair를 전달한다. 이 macro를 쓰면 새 system call의 metadata를 다른 tool이 이용할 수 있다.
System call 호출 방식과 맞추기 위해 include/linux/syscalls.h에 asmlinkage로 표시한 대응 prototype도 필요하다.
asmlinkage long sys_xyzzy(...);
x86 같은 architecture는 자체 syscall table을 가지지만 여러 architecture는 generic table을 공유한다. Generic 목록에 추가할 때는 include/uapi/asm-generic/unistd.h에 entry를 넣는다.
#define __NR_xyzzy 292
__SYSCALL(__NR_xyzzy, sys_xyzzy)
추가된 system call을 반영해 __NR_syscalls count도 갱신한다. 같은 merge window에 새 system call이 여러 개 들어오면 conflict를 해결하기 위해 syscall number가 바뀔 수 있다.
kernel/sys_ni.c는 각 system call이 구현되지 않았을 때 -ENOSYS를 반환하는 fallback stub를 제공한다. 새 system call도 여기에 추가한다.
COND_SYSCALL(xyzzy);
새 kernel 기능과 이를 제어하는 system call은 보통 optional이어야 하므로 CONFIG option을 추가한다. 일반적으로 init/Kconfig에 넣는다. 새 기능과 system call을 설명하고, 일반 사용자에게 숨겨야 한다면 EXPERT에 depend하게 한다. 새 source file은 Makefile에서 CONFIG option에 종속시킨다.
obj-$(CONFIG_XYZZY_SYSCALL) += xyzzy.o
새 CONFIG option을 끈 상태에서도 kernel이 build되는지 반드시 다시 확인한다.
Generic core commit의 필수 항목
- 보통 init/Kconfig에 두는 새 기능의 CONFIG option
- Entry point를 정의하는 SYSCALL_DEFINEn(xyzzy, ...)
- include/linux/syscalls.h의 대응 prototype
- include/uapi/asm-generic/unistd.h의 generic table entry
- kernel/sys_ni.c의 fallback stub
Entry point는 `sys_xyzzy()`를 직접 선언해 정의하는 대신 `SYSCALL_DEFINEn()`으로 만들고 `include/linux/syscalls.h`에 `asmlinkage` prototype을 둡니다. Generic table의 `__NR_xyzzy`와 `__NR_syscalls`를 갱신하며 merge window 충돌로 번호가 바뀔 수 있음을 감안합니다.
`kernel/sys_ni.c`에는 `COND_SYSCALL(xyzzy)` fallback을 추가해 미구현 configuration에서 `-ENOSYS`를 반환하게 합니다. 기능은 `CONFIG_XYZZY_SYSCALL`로 optional하게 만들고 Makefile object도 해당 option에 종속시키며 option을 끈 build까지 검사합니다.
로컬 이탈리아어 원문이 요구하는 core wiring 항목입니다.
Implementazione di chiamate di sistema generiche
------------------------------------------------
Il principale punto d'accesso alla vostra nuova chiamata di sistema
:manpage:`xyzzy(2)` verrà chiamato ``sys_xyzzy()``; ma, piuttosto che in modo
esplicito, lo aggiungerete tramite la macro ``SYSCALL_DEFINEn``. La 'n'
indica il numero di argomenti della chiamata di sistema; la macro ha come
argomento il nome della chiamata di sistema, seguito dalle coppie (tipo, nome)
per definire i suoi parametri. L'uso di questa macro permette di avere
i metadati della nuova chiamata di sistema disponibili anche per altri
strumenti.
Il nuovo punto d'accesso necessita anche del suo prototipo di funzione in
``include/linux/syscalls.h``, marcato come asmlinkage di modo da abbinargli
il modo in cui quelle chiamate di sistema verranno invocate::
asmlinkage long sys_xyzzy(...);
Alcune architetture (per esempio x86) hanno le loro specifiche tabelle di
chiamate di sistema (syscall), ma molte altre architetture condividono una
tabella comune di syscall. Aggiungete alla lista generica la vostra nuova
chiamata di sistema aggiungendo un nuovo elemento alla lista in
``include/uapi/asm-generic/unistd.h``::
#define __NR_xyzzy 292
__SYSCALL(__NR_xyzzy, sys_xyzzy)
Aggiornate anche il contatore __NR_syscalls di modo che sia coerente con
l'aggiunta della nuove chiamate di sistema; va notato che se più di una nuova
chiamata di sistema viene aggiunga nella stessa finestra di sviluppo, il numero
della vostra nuova syscall potrebbe essere aggiustato al fine di risolvere i
conflitti.
Il file ``kernel/sys_ni.c`` fornisce le implementazioni *stub* di ripiego che
ritornano ``-ENOSYS``. Aggiungete la vostra nuova chiamata di sistema anche
qui::
COND_SYSCALL(xyzzy);
La vostra nuova funzionalità del kernel, e la chiamata di sistema che la
controlla, dovrebbero essere opzionali. Quindi, aggiungete un'opzione
``CONFIG`` (solitamente in ``init/Kconfig``). Come al solito per le nuove
opzioni ``CONFIG``:
- Includete una descrizione della nuova funzionalità e della chiamata di
sistema che la controlla.
- Rendete l'opzione dipendente da EXPERT se dev'essere nascosta agli utenti
normali.
- Nel Makefile, rendere tutti i nuovi file sorgenti, che implementano la
nuova funzionalità, dipendenti dall'opzione CONFIG (per esempio
``obj-$(CONFIG_XYZZY_SYSCALL) += xyzzy.o``).
- Controllate due volte che sia possibile generare il kernel con la nuova
opzione CONFIG disabilitata.
Per riassumere, vi serve un *commit* che includa:
- un'opzione ``CONFIG``per la nuova funzione, normalmente in ``init/Kconfig``
- ``SYSCALL_DEFINEn(xyzzy, ...)`` per il punto d'accesso
- il corrispondente prototipo in ``include/linux/syscalls.h``
- un elemento nella tabella generica in ``include/uapi/asm-generic/unistd.h``
- *stub* di ripiego in ``kernel/sys_ni.c``
x86_64·x32·i386 syscall table
281-299새 system call을 x86 platform에 연결하려면 master syscall table을 갱신한다. 특별한 system call이 아니라면 arch/x86/entry/syscalls/syscall_64.tbl에 x86_64와 x32용 common entry를 넣는다.
333 common xyzzy sys_xyzzy
arch/x86/entry/syscalls/syscall_32.tbl에는 i386 entry를 넣는다.
380 i386 xyzzy sys_xyzzy
여기 적은 number도 해당 merge window의 conflict에 따라 바뀔 수 있다.
일반적인 새 call은 `arch/x86/entry/syscalls/syscall_64.tbl`에 x86_64와 x32용 `common` entry를, `syscall_32.tbl`에는 i386 entry를 추가합니다. 예시 번호 333과 380은 고정값이 아니며 같은 merge window의 다른 syscall과 충돌하면 조정됩니다.
Implementazione delle chiamate di sistema x86
---------------------------------------------
Per collegare la vostra nuova chiamate di sistema alle piattaforme x86,
dovete aggiornate la tabella principale di syscall. Assumendo che la vostra
nuova chiamata di sistema non sia particolarmente speciale (vedere sotto),
dovete aggiungere un elemento *common* (per x86_64 e x32) in
arch/x86/entry/syscalls/syscall_64.tbl::
333 common xyzzy sys_xyzzy
e un elemento per *i386* ``arch/x86/entry/syscalls/syscall_32.tbl``::
380 i386 xyzzy sys_xyzzy
Ancora una volta, questi numeri potrebbero essere cambiati se generano
conflitti durante la finestra di integrazione.
32-bit userspace용 generic compat layer
300-398System call 대부분은 userspace program이 32-bit여도 같은 64-bit implementation을 호출할 수 있다. Argument에 명시적인 pointer가 있어도 보통 투명하게 처리된다. 그러나 32-bit와 64-bit의 크기 차이를 다루는 compatibility layer가 필요한 경우가 있다.
Compat layer가 필요한 첫 번째 경우
64-bit kernel이 32-bit userspace program도 지원하면서 32-bit 또는 64-bit 값이 들어 있을 수 있는 __user memory를 parse해야 하는 경우다. 다음 형태의 system call argument가 해당한다.
- Pointer를 가리키는 pointer
- Pointer를 포함한 struct를 가리키는 pointer. 예: struct iovec __user *
- 크기가 달라지는 integral type을 가리키는 pointer. 예: time_t, off_t, long
- 크기가 달라지는 integral type을 포함한 struct를 가리키는 pointer
Compat layer가 필요한 두 번째 경우
loff_t나 __u64처럼 32-bit architecture에서도 명시적으로 64-bit인 argument가 있는 경우다. 32-bit application에서 64-bit kernel로 들어오는 값은 32-bit 값 두 개로 분할되므로 compatibility layer에서 다시 조립해야 한다.
명시적인 64-bit type을 가리키는 pointer argument 자체는 compatibility layer가 필요하지 않다. 예를 들어 splice(2)의 loff_t __user * argument 때문에 compat_ system call이 필요해지지는 않는다.
compat_sys_xyzzy() 구현
Compatibility version 이름은 compat_sys_xyzzy()이며 SYSCALL_DEFINEn과 같은 방식의 COMPAT_SYSCALL_DEFINEn() macro로 추가한다. 이 implementation은 64-bit kernel의 일부로 실행되지만 32-bit parameter value가 들어온다고 가정하고 필요한 변환을 수행한다.
일반적으로 compat_sys_ version이 값을 64-bit 형태로 변환한 뒤 sys_ version을 호출하거나, native와 compat entry가 공통 inner implementation function을 호출한다.
include/linux/compat.h에는 system call 호출 방식과 맞도록 asmlinkage로 표시한 compat entry prototype도 넣는다.
asmlinkage long compat_sys_xyzzy(...);
System call이 32-bit와 64-bit에서 layout이 다른 struct xyzzy_args를 사용한다면 include/linux/compat.h에 struct compat_xyzzy_args도 정의한다. 원래 struct에서 크기가 달라지는 각 field를 대응 compat_ type으로 바꾼다. compat_sys_xyzzy()는 이 compat structure로 32-bit invocation의 argument를 parse한다.
struct xyzzy_args {
const char __user *ptr;
__kernel_long_t varying_val;
u64 fixed_val;
/* ... */
};
struct compat_xyzzy_args {
compat_uptr_t ptr;
compat_long_t varying_val;
u64 fixed_val;
/* ... */
};
Generic syscall list도 compat version을 가리키도록 바꿔야 한다. include/uapi/asm-generic/unistd.h에서 __SYSCALL 대신 __SC_COMP를 쓴다.
#define __NR_xyzzy 292
__SC_COMP(__NR_xyzzy, sys_xyzzy, compat_sys_xyzzy)
Generic compat 구현의 필수 항목
- Compat entry point의 COMPAT_SYSCALL_DEFINEn(xyzzy, ...)
- include/linux/compat.h의 대응 prototype
- 필요한 경우 include/linux/compat.h의 32-bit mapping struct
- include/uapi/asm-generic/unistd.h에서 __SYSCALL 대신 __SC_COMP 사용
Compat wrapper가 pointer와 가변 폭 field를 native representation으로 변환한 뒤 공통 구현으로 넘긴다.
64-bit kernel이 32-bit userspace memory에서 pointer-to-pointer, pointer를 포함한 struct, `time_t`·`off_t`·`long`처럼 폭이 달라지는 값을 읽거나 명시적 64-bit argument를 두 조각에서 재조립해야 하면 compat layer가 필요합니다. 단, `loff_t __user *`처럼 고정 64-bit type을 가리키는 pointer만으로는 별도 compat call이 필요하지 않습니다.
`COMPAT_SYSCALL_DEFINEn()`으로 `compat_sys_xyzzy()`를 만들고 `include/linux/compat.h`에 prototype과 필요한 `compat_xyzzy_args`를 정의합니다. Pointer는 `compat_uptr_t`, 폭이 달라지는 long은 `compat_long_t`처럼 대응 type으로 변환하고 generic table은 `__SYSCALL` 대신 `__SC_COMP`를 사용합니다.
Chiamate di sistema compatibili (generico)
------------------------------------------
Per molte chiamate di sistema, la stessa implementazione a 64-bit può essere
invocata anche quando il programma in spazio utente è a 32-bit; anche se la
chiamata di sistema include esplicitamente un puntatore, questo viene gestito
in modo trasparente.
Tuttavia, ci sono un paio di situazione dove diventa necessario avere un
livello di gestione della compatibilità per risolvere le differenze di
dimensioni fra 32-bit e 64-bit.
Il primo caso è quando un kernel a 64-bit supporta anche programmi in spazio
utente a 32-bit, perciò dovrà ispezionare aree della memoria (``__user``) che
potrebbero contenere valori a 32-bit o a 64-bit. In particolar modo, questo
è necessario quando un argomento di una chiamata di sistema è:
- un puntatore ad un puntatore
- un puntatore ad una struttura dati contenente a sua volta un puntatore
( ad esempio ``struct iovec __user *``)
- un puntatore ad un tipo intero di dimensione variabile (``time_t``,
``off_t``, ``long``, ...)
- un puntatore ad una struttura dati contenente un tipo intero di dimensione
variabile.
Il secondo caso che richiede un livello di gestione della compatibilità è
quando uno degli argomenti di una chiamata a sistema è esplicitamente un tipo
a 64-bit anche su architetture a 32-bit, per esempio ``loff_t`` o ``__u64``.
In questo caso, un valore che arriva ad un kernel a 64-bit da un'applicazione
a 32-bit verrà diviso in due valori a 32-bit che dovranno essere riassemblati
in questo livello di compatibilità.
(Da notare che non serve questo livello di compatibilità per argomenti che
sono puntatori ad un tipo esplicitamente a 64-bit; per esempio, in
:manpage:`splice(2)` l'argomento di tipo ``loff_t __user *`` non necessita
di una chiamata di sistema ``compat_``)
La versione compatibile della nostra chiamata di sistema si chiamerà
``compat_sys_xyzzy()``, e viene aggiunta utilizzando la macro
``COMPAT_SYSCALL_DEFINEn()`` (simile a SYSCALL_DEFINEn). Questa versione
dell'implementazione è parte del kernel a 64-bit ma accetta parametri a 32-bit
che trasformerà secondo le necessità (tipicamente, la versione
``compat_sys_`` converte questi valori nello loro corrispondente a 64-bit e
può chiamare la versione ``sys_`` oppure invocare una funzione che implementa
le parti comuni).
Il punto d'accesso *compat* deve avere il corrispondente prototipo di funzione
in ``include/linux/compat.h``, marcato come asmlinkage di modo da abbinargli
il modo in cui quelle chiamate di sistema verranno invocate::
asmlinkage long compat_sys_xyzzy(...);
Se la chiamata di sistema prevede una struttura dati organizzata in modo
diverso per sistemi a 32-bit e per quelli a 64-bit, diciamo
``struct xyzzy_args``, allora il file d'intestazione
``then the include/linux/compat.h`` deve includere la sua versione
*compatibile* (``struct compat_xyzzy_args``); ogni variabile con
dimensione variabile deve avere il proprio tipo ``compat_`` corrispondente
a quello in ``struct xyzzy_args``. La funzione ``compat_sys_xyzzy()``
può usare la struttura ``compat_`` per analizzare gli argomenti ricevuti
da una chiamata a 32-bit.
Per esempio, se avete i seguenti campi::
struct xyzzy_args {
const char __user *ptr;
__kernel_long_t varying_val;
u64 fixed_val;
/* ... */
};
nella struttura ``struct xyzzy_args``, allora la struttura
``struct compat_xyzzy_args`` dovrebbe avere::
struct compat_xyzzy_args {
compat_uptr_t ptr;
compat_long_t varying_val;
u64 fixed_val;
/* ... */
};
La lista generica delle chiamate di sistema ha bisogno di essere
aggiustata al fine di permettere l'uso della versione *compatibile*;
la voce in ``include/uapi/asm-generic/unistd.h`` dovrebbero usare
``__SC_COMP`` piuttosto di ``__SYSCALL``::
#define __NR_xyzzy 292
__SC_COMP(__NR_xyzzy, sys_xyzzy, compat_sys_xyzzy)
Riassumendo, vi serve:
- un ``COMPAT_SYSCALL_DEFINEn(xyzzy, ...)`` per il punto d'accesso
*compatibile*
- un prototipo in ``include/linux/compat.h``
- (se necessario) una struttura di compatibilità a 32-bit in
``include/linux/compat.h``
- una voce ``__SC_COMP``, e non ``__SYSCALL``, in
``include/uapi/asm-generic/unistd.h``
x86 compat entry와 x32 ABI
399-434Compat version이 있는 system call을 x86에 연결하려면 syscall table entry를 조정한다. 먼저 arch/x86/entry/syscalls/syscall_32.tbl에 열을 하나 더 두어 64-bit kernel에서 실행되는 32-bit userspace program이 compat entry로 들어가게 한다.
380 i386 xyzzy sys_xyzzy __ia32_compat_sys_xyzzy
다음으로 새 system call의 x32 ABI가 어떤 argument layout을 사용할지 결정한다. 64-bit version 또는 32-bit version 중 하나와 일치해야 한다.
Pointer-to-pointer가 있다면 결정은 명확하다. x32는 ILP32이므로 layout은 32-bit version과 같아야 한다. arch/x86/entry/syscalls/syscall_64.tbl entry를 나눠 x32 program이 compatibility wrapper로 들어가게 한다.
333 64 xyzzy sys_xyzzy
...
555 x32 xyzzy __x32_compat_sys_xyzzy
Pointer가 없다면 x32 ABI에서 64-bit system call을 재사용하는 편이 좋다. 이 경우 arch/x86/entry/syscalls/syscall_64.tbl entry는 바꾸지 않는다.
어느 경우든 argument layout에 쓰인 type이 x32(-mx32)에서 선택한 32-bit(-m32) 또는 64-bit(-m64) equivalent와 정확히 mapping되는지 확인해야 한다.
`syscall_32.tbl`에는 64-bit kernel에서 실행되는 32-bit program이 `__ia32_compat_sys_xyzzy`로 들어가도록 compat 열을 추가합니다. x32 ABI는 pointer-to-pointer가 있으면 ILP32 layout을 따라 `__x32_compat_sys_xyzzy`를 쓰고 pointer가 없으면 가능한 한 64-bit implementation을 재사용합니다.
선택한 layout은 `-mx32` build의 argument type이 `-m32` 또는 `-m64` equivalent와 정확히 일치하는지 확인해야 합니다.
Compatibilità delle chiamate di sistema (x86)
---------------------------------------------
Per collegare una chiamata di sistema, su un'architettura x86, con la sua
versione *compatibile*, è necessario aggiustare la voce nella tabella
delle syscall.
Per prima cosa, la voce in ``arch/x86/entry/syscalls/syscall_32.tbl`` prende
un argomento aggiuntivo per indicare che un programma in spazio utente
a 32-bit, eseguito su un kernel a 64-bit, dovrebbe accedere tramite il punto
d'accesso compatibile::
380 i386 xyzzy sys_xyzzy __ia32_compat_sys_xyzzy
Secondo, dovete capire cosa dovrebbe succedere alla nuova chiamata di sistema
per la versione dell'ABI x32. Qui C'è una scelta da fare: gli argomenti
possono corrisponde alla versione a 64-bit o a quella a 32-bit.
Se c'è un puntatore ad un puntatore, la decisione è semplice: x32 è ILP32,
quindi gli argomenti dovrebbero corrispondere a quelli a 32-bit, e la voce in
``arch/x86/entry/syscalls/syscall_64.tbl`` sarà divisa cosicché i programmi
x32 eseguano la chiamata *compatibile*::
333 64 xyzzy sys_xyzzy
...
555 x32 xyzzy __x32_compat_sys_xyzzy
Se non ci sono puntatori, allora è preferibile riutilizzare la chiamata di
sistema a 64-bit per l'ABI x32 (e di conseguenza la voce in
arch/x86/entry/syscalls/syscall_64.tbl rimane immutata).
In ambo i casi, dovreste verificare che i tipi usati dagli argomenti
abbiano un'esatta corrispondenza da x32 (-mx32) al loro equivalente a
32-bit (-m32) o 64-bit (-m64).
다른 위치나 상태로 복귀하는 syscall
435-492System call 대부분은 완료 뒤 user program이 중단한 바로 다음 instruction에서 계속 실행된다. Stack과 register 대부분은 호출 전과 같고 virtual memory space도 같다.
그러나 rt_sigreturn은 다른 위치로 복귀할 수 있고, fork/vfork/clone은 memory space를 바꾸며, execve/execveat는 program의 architecture까지 바꿀 수 있다.
이런 동작을 허용하려면 system call kernel implementation이 추가 register를 kernel stack에 save하고 restore해야 할 수 있다. 그래야 system call 뒤 execution을 어디서 어떤 상태로 계속할지 완전히 제어할 수 있다.
구현은 architecture-specific이지만 보통 추가 register를 save/restore하고 실제 system call entry point를 호출하는 assembly entry point를 정의한다.
x86_64에서는 arch/x86/entry/entry_64.S에 stub_xyzzy entry point를 구현하고 arch/x86/entry/syscalls/syscall_64.tbl을 그 entry에 맞춘다.
333 common xyzzy stub_xyzzy
64-bit kernel에서 실행되는 32-bit program용 equivalent는 보통 stub32_xyzzy라고 하며 arch/x86/entry/entry_64_compat.S에 구현한다. arch/x86/entry/syscalls/syscall_32.tbl도 대응되게 조정한다.
380 i386 xyzzy sys_xyzzy stub32_xyzzy
앞 절처럼 compatibility layer가 필요하면 stub32_ version은 native 64-bit version이 아니라 compat_sys_ version을 호출해야 한다. x32 ABI implementation이 x86_64 version과 공통이 아니라면 x32 syscall table도 compat_sys_ version을 부르는 stub를 호출해야 한다.
User-mode Linux도 계속 동작하도록 mapping을 추가하는 것이 좋다. UML syscall table은 stub_xyzzy를 참조하지만 register 등을 simulation하므로 UML build에는 arch/x86/entry/entry_64.S implementation이 들어가지 않는다. arch/x86/um/sys_call_table_64.c에 다음 define을 추가하면 해결된다.
#define stub_xyzzy sys_xyzzy
`rt_sigreturn`, `fork`·`vfork`·`clone`, `execve`·`execveat`처럼 일반 return path를 따르지 않는 call은 추가 register save·restore와 architecture-specific assembly entry가 필요할 수 있습니다.
x86_64의 `stub_xyzzy`, 32-bit compat의 `stub32_xyzzy`, 필요 시 `compat_sys_` 호출과 x32 stub을 table에 연결합니다. User-mode Linux는 실제 x86 assembly entry를 포함하지 않으므로 `arch/x86/um/sys_call_table_64.c`에서 `stub_xyzzy`를 `sys_xyzzy`로 mapping합니다.
Chiamate di sistema che ritornano altrove
-----------------------------------------
Nella maggior parte delle chiamate di sistema, al termine della loro
esecuzione, i programmi in spazio utente riprendono esattamente dal punto
in cui si erano interrotti -- quindi dall'istruzione successiva, con lo
stesso *stack* e con la maggior parte del registri com'erano stati
lasciati prima della chiamata di sistema, e anche con la stessa memoria
virtuale.
Tuttavia, alcune chiamata di sistema fanno le cose in modo differente.
Potrebbero ritornare ad un punto diverso (``rt_sigreturn``) o cambiare
la memoria in spazio utente (``fork``/``vfork``/``clone``) o perfino
l'architettura del programma (``execve``/``execveat``).
Per permettere tutto ciò, l'implementazione nel kernel di questo tipo di
chiamate di sistema potrebbero dover salvare e ripristinare registri
aggiuntivi nello *stack* del kernel, permettendo così un controllo completo
su dove e come l'esecuzione dovrà continuare dopo l'esecuzione della
chiamata di sistema.
Queste saranno specifiche per ogni architettura, ma tipicamente si definiscono
dei punti d'accesso in *assembly* per salvare/ripristinare i registri
aggiuntivi e quindi chiamare il vero punto d'accesso per la chiamata di
sistema.
Per l'architettura x86_64, questo è implementato come un punto d'accesso
``stub_xyzzy`` in ``arch/x86/entry/entry_64.S``, e la voce nella tabella
di syscall (``arch/x86/entry/syscalls/syscall_64.tbl``) verrà corretta di
conseguenza::
333 common xyzzy stub_xyzzy
L'equivalente per programmi a 32-bit eseguiti su un kernel a 64-bit viene
normalmente chiamato ``stub32_xyzzy`` e implementato in
``arch/x86/entry/entry_64_compat.S`` con la corrispondente voce nella tabella
di syscall ``arch/x86/entry/syscalls/syscall_32.tbl`` corretta nel
seguente modo::
380 i386 xyzzy sys_xyzzy stub32_xyzzy
Se una chiamata di sistema necessita di un livello di compatibilità (come
nella sezione precedente), allora la versione ``stub32_`` deve invocare
la versione ``compat_sys_`` piuttosto che quella nativa a 64-bit. In aggiunta,
se l'implementazione dell'ABI x32 è diversa da quella x86_64, allora la sua
voce nella tabella di syscall dovrà chiamare uno *stub* che invoca la versione
``compat_sys_``,
Per completezza, sarebbe carino impostare una mappatura cosicché
*user-mode* Linux (UML) continui a funzionare -- la sua tabella di syscall
farà riferimento a stub_xyzzy, ma UML non include l'implementazione
in ``arch/x86/entry/entry_64.S`` (perché UML simula i registri eccetera).
Correggerlo è semplice, basta aggiungere una #define in
``arch/x86/um/sys_call_table_64.c``::
#define stub_xyzzy sys_xyzzy
audit 특수 처리, selftest와 man page
493-553Kernel 전체의 특수 처리 확인
Kernel 대부분은 system call을 generic하게 다루지만 특정 system call에 맞춰 갱신해야 하는 예외가 가끔 있다.
Audit subsystem이 한 예다. Architecture-specific function으로 file open(open/openat), program execution(execve/exeveat), socket multiplexor(socketcall) 같은 특수 system call type을 분류한다. 새 system call이 이들 중 하나와 유사하다면 audit system도 갱신해야 한다.
일반적으로 새 system call과 비슷한 기존 system call이 있다면 kernel 전체에서 그 이름을 grep해 다른 special case가 없는지 확인할 가치가 있다.
Selftest와 외부 test suite
새 system call은 당연히 test해야 하며 reviewer에게 userspace program의 사용법을 보여 주는 것도 유용하다. 두 목적을 함께 달성하는 좋은 방법은 tools/testing/selftests/ 아래 새 directory에 단순한 self-test program을 넣는 것이다.
새 system call에는 libc wrapper function이 없으므로 test는 syscall()로 직접 호출해야 한다. Userspace-visible structure를 새로 만들었다면 test를 compile할 수 있도록 대응 header도 install해야 한다.
Selftest가 지원하는 모든 architecture에서 성공하는지 확인한다. 예를 들어 x86_64(-m64), x86_32(-m32), x32(-mx32) ABI program으로 각각 compile해 동작을 검사한다.
새 기능을 더 넓고 철저하게 test하려면 Linux Test Project에 test를 추가하거나 filesystem 변경이면 xfstests project에 test를 추가하는 방안도 검토한다.
- Linux Test Project
https://linux-test-project.github.io/ - xfstests repository
git://git.kernel.org/pub/scm/fs/xfs/xfstests-dev.git
Man page
모든 새 system call에는 완전한 man page가 따라와야 한다. Groff markup이 이상적이지만 plain text도 가능하다. Groff를 썼다면 reviewer 편의를 위해 patchset cover email에 미리 render한 ASCII version도 넣는 것이 좋다.
Man page는 [email protected]를 CC해야 한다. 자세한 제출 방법은 kernel.org의 man-pages patch 안내를 참고한다.
`open`·`openat`, `execve`·`execveat`, `socketcall`처럼 audit subsystem이 특별히 분류하는 기존 syscall과 유사하다면 architecture별 audit code도 갱신합니다. Kernel 전체에서 유사 call 이름을 검색해 다른 special case가 없는지 확인합니다.
`tools/testing/selftests/`에 `syscall()`로 직접 호출하는 userspace test를 넣고 새 userspace-visible header를 install합니다. x86_64 `-m64`, x86_32 `-m32`, x32 `-mx32`에서 실행하며 필요하면 Linux Test Project나 filesystem용 xfstests에도 coverage를 추가합니다.
모든 새 syscall에는 완전한 man page를 제공하고 groff source를 보낼 때는 reviewer가 읽기 쉬운 ASCII render도 함께 보냅니다. Man page patch에는 `[email protected]`를 Cc합니다.
Altri dettagli
--------------
La maggior parte dei kernel tratta le chiamate di sistema allo stesso modo,
ma possono esserci rare eccezioni per le quali potrebbe essere necessario
l'aggiornamento della vostra chiamata di sistema.
Il sotto-sistema di controllo (*audit subsystem*) è uno di questi casi
speciali; esso include (per architettura) funzioni che classificano alcuni
tipi di chiamate di sistema -- in particolare apertura dei file
(``open``/``openat``), esecuzione dei programmi (``execve``/``exeveat``)
oppure multiplatori di socket (``socketcall``). Se la vostra nuova chiamata
di sistema è simile ad una di queste, allora il sistema di controllo dovrebbe
essere aggiornato.
Più in generale, se esiste una chiamata di sistema che è simile alla vostra,
vale la pena fare una ricerca con ``grep`` su tutto il kernel per la chiamata
di sistema esistente per verificare che non ci siano altri casi speciali.
Verifica
--------
Una nuova chiamata di sistema dev'essere, ovviamente, provata; è utile fornire
ai revisori un programma in spazio utente che mostri l'uso della chiamata di
sistema. Un buon modo per combinare queste cose è quello di aggiungere un
semplice programma di auto-verifica in una nuova cartella in
``tools/testing/selftests/``.
Per una nuova chiamata di sistema, ovviamente, non ci sarà alcuna funzione
in libc e quindi il programma di verifica dovrà invocarla usando ``syscall()``;
inoltre, se la nuova chiamata di sistema prevede un nuova struttura dati
visibile in spazio utente, il file d'intestazione necessario dev'essere
installato al fine di compilare il programma.
Assicuratevi che il programma di auto-verifica possa essere eseguito
correttamente su tutte le architetture supportate. Per esempio, verificate che
funzioni quando viene compilato per x86_64 (-m64), x86_32 (-m32) e x32 (-mx32).
Al fine di una più meticolosa ed estesa verifica della nuova funzionalità,
dovreste considerare l'aggiunta di nuove verifica al progetto 'Linux Test',
oppure al progetto xfstests per cambiamenti relativi al filesystem.
- https://linux-test-project.github.io/
- git://git.kernel.org/pub/scm/fs/xfs/xfstests-dev.git
Pagine man
----------
Tutte le nuove chiamate di sistema dovrebbero avere una pagina man completa,
idealmente usando i marcatori groff, ma anche il puro testo può andare. Se
state usando groff, è utile che includiate nella email di presentazione una
versione già convertita in formato ASCII: semplificherà la vita dei revisori.
Le pagine man dovrebbero essere in copia-conoscenza verso
[email protected]
Per maggiori dettagli, leggere
https://www.kernel.org/doc/man-pages/patches.html
kernel 내부에서 syscall entry를 직접 호출하지 않기
554-587System call은 userspace와 kernel 사이의 상호 작용 지점이다. 따라서 sys_xyzzy()나 compat_sys_xyzzy() 같은 system call function은 userspace가 syscall table을 통해서만 호출해야 하며 kernel의 다른 code가 직접 호출해서는 안 된다.
System call 기능을 kernel 내부에서도 사용해야 하거나 old syscall과 new syscall이 공유해야 하거나 native syscall과 compatibility variant가 공유해야 한다면 ksys_xyzzy() 같은 helper function으로 구현한다. 그러면 syscall stub인 sys_xyzzy(), compatibility syscall stub인 compat_sys_xyzzy(), 다른 kernel code가 helper를 호출할 수 있다.
적어도 64-bit x86에서는 v4.17부터 kernel 내부에서 system call function을 호출하지 않는 것이 엄격한 요구 사항이다. 이 architecture는 system call에 다른 calling convention을 쓴다. Syscall wrapper가 struct pt_regs를 즉시 decode한 다음 실제 syscall function으로 처리를 넘긴다.
그 결과 syscall entry에서 CPU register 여섯 개를 매번 임의의 userspace content로 채우는 대신 특정 syscall에 실제로 필요한 parameter만 전달한다. 임의의 userspace 값이 call chain 아래로 흘러가면 심각한 문제를 만들 수 있다.
또한 kernel data와 user data는 접근 규칙이 다를 수 있다. 이것도 sys_xyzzy()를 직접 호출하는 것이 일반적으로 나쁜 이유다.
이 규칙의 예외는 architecture-specific override, architecture-specific compatibility wrapper 또는 arch/ 아래의 다른 code에서만 허용된다.
`sys_xyzzy()`와 `compat_sys_xyzzy()`는 userspace가 syscall table을 통해 들어오는 ABI entry이므로 다른 kernel code가 직접 호출해서는 안 됩니다. 공통 기능은 `ksys_xyzzy()` 같은 helper로 분리해 native·compat stub과 내부 caller가 공유합니다.
64-bit x86은 v4.17부터 `struct pt_regs`를 wrapper에서 decode하는 별도 calling convention을 사용하므로 직접 호출 금지가 특히 중요합니다. Kernel pointer와 user pointer의 접근 규칙도 다르며 예외는 architecture override·compat wrapper 등 `arch/`의 제한된 code뿐입니다.
Non invocate chiamate di sistema dal kernel
-------------------------------------------
Le chiamate di sistema sono, come già detto prima, punti di interazione fra
lo spazio utente e il kernel. Perciò, le chiamate di sistema come
``sys_xyzzy()`` o ``compat_sys_xyzzy()`` dovrebbero essere chiamate solo dallo
spazio utente attraverso la tabella syscall, ma non da nessun altro punto nel
kernel. Se la nuova funzionalità è utile all'interno del kernel, per esempio
dev'essere condivisa fra una vecchia e una nuova chiamata di sistema o
dev'essere utilizzata da una chiamata di sistema e la sua variante compatibile,
allora dev'essere implementata come una funzione di supporto
(*helper function*) (per esempio ``ksys_xyzzy()``). Questa funzione potrà
essere chiamata dallo *stub* (``sys_xyzzy()``), dalla variante compatibile
(``compat_sys_xyzzy()``), e/o da altri parti del kernel.
Sui sistemi x86 a 64-bit, a partire dalla versione v4.17 è un requisito
fondamentale quello di non invocare chiamate di sistema all'interno del kernel.
Esso usa una diversa convenzione per l'invocazione di chiamate di sistema dove
``struct pt_regs`` viene decodificata al volo in una funzione che racchiude
la chiamata di sistema la quale verrà eseguita successivamente.
Questo significa che verranno passati solo i parametri che sono davvero
necessari ad una specifica chiamata di sistema, invece che riempire ogni volta
6 registri del processore con contenuti presi dallo spazio utente (potrebbe
causare seri problemi nella sequenza di chiamate).
Inoltre, le regole su come i dati possano essere usati potrebbero differire
fra il kernel e l'utente. Questo è un altro motivo per cui invocare
``sys_xyzzy()`` è generalmente una brutta idea.
Eccezioni a questa regola vengono accettate solo per funzioni d'architetture
che surclassano quelle generiche, per funzioni d'architettura di compatibilità,
o per altro codice in arch/
설계 근거와 참고 자료
588-643System call의 flags argument 사용과 unknown flag 처리에 관한 Michael Kerrisk의 LWN 글, 64-bit system call argument 제약을 설명한 Jake Edge의 LWN 글, Linux v3.14의 system call implementation path를 자세히 설명한 David Drysdale의 LWN 글 두 편이 참고 자료로 제시된다.
Architecture-specific system call 요구 사항은 syscall(2) man page에서 확인할 수 있다. Linus Torvalds가 ioctl()의 문제를 논의한 email 모음과 Arnd Bergmann의 “How to not invent kernel interfaces”도 interface 설계 배경을 제공한다.
Michael Kerrisk의 CAP_SYS_ADMIN 새 사용을 피하는 방법, 새 system call의 관련 정보를 같은 email thread에 모두 담으라는 Andrew Morton의 권고, 새 system call에 man page를 붙이라는 Michael Kerrisk의 권고가 이어진다.
x86 wiring을 별도 commit으로 만들라는 Thomas Gleixner의 제안, 새 system call에 man page와 selftest를 함께 제공하라는 Greg Kroah-Hartman의 제안, 새 system call과 prctl(2) extension 선택을 논의한 Michael Kerrisk의 글도 참고할 수 있다.
Argument가 많은 system call은 future extension을 위한 size field가 있는 struct에 argument를 넣으라는 Ingo Molnar의 제안도 제시된다.
O_* numbering space flag의 재사용으로 생긴 numbering 문제는 commit 75069f2b5bfb("vfs: renumber FMODE_NONOTIFY and add to uniqueness check"), 12ed2e36c98a("fanotify: FMODE_NONOTIFY and __O_SYNC in sparc conflict"), bb458c644a59("Safer ABI for O_TMPFILE")에서 확인할 수 있다.
Matthew Wilcox의 64-bit argument 제약 논의, unknown flag를 엄격히 검사하라는 Greg Kroah-Hartman의 권고, x32 system call은 32-bit보다 64-bit version과의 호환을 선호해야 한다는 Linus Torvalds의 권고도 포함된다. 마지막 자료는 여러 architecture에서 scripts/syscall.tbl을 사용하도록 syscall table infrastructure를 개정한 patch series다.
- Flags argument in system calls
https://lwn.net/Articles/585415/ - Handling unknown system call flags
https://lwn.net/Articles/588444/ - 64-bit system call argument constraints
https://lwn.net/Articles/311630/ - System call implementation paths, part 1
https://lwn.net/Articles/604287/ - System call implementation paths, part 2
https://lwn.net/Articles/604515/ - syscall(2) architecture notes
http://man7.org/linux/man-pages/man2/syscall.2.html#NOTES - Linus Torvalds on ioctl()
https://yarchive.net/comp/linux/ioctl.html - How to not invent kernel interfaces
https://www.ukuug.org/events/linux2007/2007/papers/Bergmann.pdf - Avoiding new CAP_SYS_ADMIN uses
https://lwn.net/Articles/486306/ - Keep syscall proposal information in one thread
https://lore.kernel.org/r/[email protected] - New syscall man-page recommendation
https://lore.kernel.org/r/CAKgNAkgMA39AfoSoA5Pe1r9N+ZzfYQNvNPvcRN7tOvRb8+v06Q@mail.gmail.com - Separate x86 wire-up commit
https://lore.kernel.org/r/alpine.DEB.2.11.1411191249560.3909@nanos - Man page and selftest recommendation
https://lore.kernel.org/r/[email protected] - New system call versus prctl(2)
https://lore.kernel.org/r/CAHO5Pa3F2MjfTtfNxa8LbnkeeU8=YJ+9tDqxZpw7Gz59E-4AUg@mail.gmail.com - Struct size field for extensibility
https://lore.kernel.org/r/[email protected] - 64-bit argument restrictions
https://lore.kernel.org/r/[email protected] - Reject unknown flags
https://lore.kernel.org/r/[email protected] - x32 compatibility preference
https://lore.kernel.org/r/CA+55aFxfmwfB7jbbrXxa=K7VBYPfAvmu3XOkGrLbB1UFjX1+Ew@mail.gmail.com - scripts/syscall.tbl infrastructure series
https://lore.kernel.org/lkml/[email protected]
참고 링크는 unknown flag 처리, 64-bit argument 배치, v3.14 syscall 구현 경로, architecture별 `syscall(2)` 규칙, `ioctl()`의 문제, `CAP_SYS_ADMIN` 남용 방지, man page·selftest·x86 분리 commit 권고의 근거를 제공합니다.
또한 많은 argument를 `size`가 있는 struct로 묶는 방법, `O_*` flag 번호 충돌 사례, x32가 32-bit보다 64-bit ABI 호환을 우선해야 한다는 논의를 포함합니다. 원문 링크와 commit ID는 아래 보존된 588~643줄에서 그대로 확인할 수 있습니다.
Riferimenti e fonti
-------------------
- Articolo di Michael Kerris su LWN sull'uso dell'argomento flags nelle
chiamate di sistema: https://lwn.net/Articles/585415/
- Articolo di Michael Kerris su LWN su come gestire flag sconosciuti in
una chiamata di sistema: https://lwn.net/Articles/588444/
- Articolo di Jake Edge su LWN che descrive i limiti degli argomenti a 64-bit
delle chiamate di sistema: https://lwn.net/Articles/311630/
- Una coppia di articoli di David Drysdale che descrivono i dettagli del
percorso implementativo di una chiamata di sistema per la versione v3.14:
- https://lwn.net/Articles/604287/
- https://lwn.net/Articles/604515/
- Requisiti specifici alle architetture sono discussi nella pagina man
:manpage:`syscall(2)` :
http://man7.org/linux/man-pages/man2/syscall.2.html#NOTES
- Collezione di email di Linux Torvalds sui problemi relativi a ``ioctl()``:
http://yarchive.net/comp/linux/ioctl.html
- "Come non inventare interfacce del kernel", Arnd Bergmann,
http://www.ukuug.org/events/linux2007/2007/papers/Bergmann.pdf
- Articolo di Michael Kerris su LWN sull'evitare nuovi usi di CAP_SYS_ADMIN:
https://lwn.net/Articles/486306/
- Raccomandazioni da Andrew Morton circa il fatto che tutte le informazioni
su una nuova chiamata di sistema dovrebbero essere contenute nello stesso
filone di discussione di email: https://lore.kernel.org/r/[email protected]
- Raccomandazioni da Michael Kerrisk circa il fatto che le nuove chiamate di
sistema dovrebbero avere una pagina man: https://lore.kernel.org/r/CAKgNAkgMA39AfoSoA5Pe1r9N+ZzfYQNvNPvcRN7tOvRb8+v06Q@mail.gmail.com
- Consigli da Thomas Gleixner sul fatto che il collegamento all'architettura
x86 dovrebbe avvenire in un *commit* differente:
https://lore.kernel.org/r/alpine.DEB.2.11.1411191249560.3909@nanos
- Consigli da Greg Kroah-Hartman circa la bontà d'avere una pagina man e un
programma di auto-verifica per le nuove chiamate di sistema:
https://lore.kernel.org/r/[email protected]
- Discussione di Michael Kerrisk sulle nuove chiamate di sistema contro
le estensioni :manpage:`prctl(2)`: https://lore.kernel.org/r/CAHO5Pa3F2MjfTtfNxa8LbnkeeU8=YJ+9tDqxZpw7Gz59E-4AUg@mail.gmail.com
- Consigli da Ingo Molnar che le chiamate di sistema con più argomenti
dovrebbero incapsularli in una struttura che includa un argomento
*size* per garantire l'estensibilità futura:
https://lore.kernel.org/r/[email protected]
- Un certo numero di casi strani emersi dall'uso (riuso) dei flag O_*:
- commit 75069f2b5bfb ("vfs: renumber FMODE_NONOTIFY and add to uniqueness
check")
- commit 12ed2e36c98a ("fanotify: FMODE_NONOTIFY and __O_SYNC in sparc
conflict")
- commit bb458c644a59 ("Safer ABI for O_TMPFILE")
- Discussion from Matthew Wilcox about restrictions on 64-bit arguments:
https://lore.kernel.org/r/[email protected]
- Raccomandazioni da Greg Kroah-Hartman sul fatto che i flag sconosciuti dovrebbero
essere controllati: https://lore.kernel.org/r/[email protected]
- Raccomandazioni da Linus Torvalds che le chiamate di sistema x32 dovrebbero
favorire la compatibilità con le versioni a 64-bit piuttosto che quelle a 32-bit:
https://lore.kernel.org/r/CA+55aFxfmwfB7jbbrXxa=K7VBYPfAvmu3XOkGrLbB1UFjX1+Ew@mail.gmail.com
요약·해설
adding-syscalls.rst:1-643영구 userspace ABI인 새 system call이 정말 필요한지부터 검토하고 flags·size field, file descriptor와 capability를 사용해 미래 확장과 보안을 설계하는 방법을 다룹니다.
Generic·x86·32-bit compat wiring, 특수 return path, audit와 selftest, man page, kernel 내부 helper 분리까지 제출에 필요한 구현·검증 절차를 정리합니다.