Pour savoir ce qui est réellement alloué, il faut regarder Committed_AS.
Après, la différence est déjà dans la mémoire inactive: il s'agit probablement de process qui ont fait des mmap, soit file-backed read-only (donc pas besoin de swapper pour réclamer), soit anonymous mais pas encore écrit dedans (donc pas non plus besoin de swapper pour réclamer), et après quelque temps, le noyau les a dégagés de la RAM pour faire de la place pour le page cache (le degré dépend de la swapinness). Du coup ils n'apparaissent pas dans RSS, mais sont toujours pris en compte par free.
J'ai aussi déjà eu des cas où, en ext3 monté avec data=ordered, supprimer un fichier déjà ouvert va laisser les entrées correspondantes dans le page cache, du coup ils apparaissent en inactive dans /proc/meminfo, free les rapporte toujours, mais on ne voit évidemment rien dans ps.
En gros, tant que ton Committed_AS n'augmente pas, pas de souci…
Donc, pris en compte par le cpu ayant écrit mais pas les autres ?
Et oui, d'où l'utilité des barrières.
J'avais lu qu'il relachait sans cesse la cohérence mémoire, et l'itanium allait assez loin, mais je ne pensais pas à ce point-là.
La performance est à ce prix.
Pour vraiment s'amuser, il y a l'architecture alpha, qui peut réordonner les "dependant reads". Donc en gros, quand tu parcours une liste chaînée, tu peux récupérer une valeur non initialisée dans ton noeud: il faut une read barrier même pour lire une iste chainée ;-)
Ma question est plus poussé, c'est "Est ce que le fait que GCC/Clang ne remplace pas cela par une boucle infinie est un comportement documenté/standard/… et si oui pourquoi ?".
Non, ce n'est pas standard, mais c'est plutôt une limite de l'optimisation faite par GCC.
Si ta variable n'est pas volatile, le compilateur peut tout à fait hoister la lecture.
Le problème c'est que si tu as une variable globale - notamment non statique - et que dans ta boucle tu fais:
variable = 0;
while (variable) {
foo(autre_variable);
…
}
et que GCC ne peut pas prouver que foo(autre_variable) ne modifie pas variable, il va forcer un reload.
Par contre si tu appelles une fonction pure, il y a des chances que GCC ne fasse pas le reload.
Si jamais il est fait référence à la variable running avant la boucle pour un appel de fonction ou autre, GCC génère tout de même le read, même sans volatile.
Essaie d'appeler une fonction static.
Ton volatile ne servira pas à grand chose : rien ne garanti que la lecture ne se fera pas dans un cache ou un write buffer pas flushé. cf. le lien "volatile considered harmfull" que j'ai posté dans un autre commentaire.
En pratique dans ce genre de boucle tu souvent toujours des syscalls/instructions qui vont provoquer une memfence (du genre un mutex ou opération atomique sur une autre variable). Sinon, en l'absence de telles instructions, en effet dans un cas simple comme au dessus ça ne suffit pas à le garantir.
Mais en pratique dès qu'un thread fait un appel système, context switch ou est schedulé sur un autre core, c'est équivalent à une memory barrier.
Je m'explique, le qualifier volatile en c/c++ n'est utile que pour des mappings hardware (genre device mapppé, ou interruption, …), mais il ne change RIEN pour l'atomicité des opérations, mais il empêche le compilateur d'optimiser certains traitements.
Pour l'atomicité non, mais comme tu l'as dit, ça force le compilateur à faire des fetch/store, et ça peut être utile dans un contexte multi-threadé.
Il y a par exemple cette macro utilisée pas mal dans le kernel:
#define ACCESS_ONCE(x) (*(volatile typeof(x) *)&(x))
Ca ne garantit pas que tu liras la valeur la plus récente écrite (pas de memfence contrairement au volatile de java), mais tu la liras éventuellement.
Aussi, ça garantit que la variable ne sera lue qu'une fois (sinon le compilo pourrait la dégager des registres et la relire plus tard).
Et ça c'est utile. Idem pour les écritures.
Après, il est vrai que dans 99% des cas, un volatile dans un code multi-threadé est mauvais signe (d'où le fameux poste de Linus "volatile considered harmful"), mais utilisé à bon escient, si ça t'évite une memfence ou une instruction atomique, ça peut être un gain non négligeable.
La grosse majorité est passé en userland, et le reste contient les I/O, etc. Donc fork()/exec() & Co ne pèsent pas lourd, sans compter que dans ton approche il y aurait aussi beaucoup d'IPC qui n'est pas gratuit non plus…
Sinon, deux autres remarques :
- la version avec polling a une race, si jamais le PID est recyclé entre deux polling. Une alternative serait par exemple d'ouvrir /proc//status, et de faire un read() régulièrement: si jamais le processus est mort entre temps, tu vas prendre ESRCH. Mais bon, ce n'est pas portable, et il y a déjà netlink pour Linux.
- le socket netlink reçoit un message par fork()/exec()/exit(), pour tous les process sur la machine: s'il beaucoup de process sont créés, le socket buffer risque de se remplir.
Démo:
Tu devrais pouvoir mettre en place un filtre pour ne recevoir que les notifications d'intérêt, c'est pas trivial (BPF) mais ça peut être intéressant ;-)
Il y a un problème dans le cas où netlink n'est pas supporté et on passe le PID d'un process auquel on ne peut pas envoyer de signal: EPERM est retourné par kill(), et wait4 ne va jamais retourner.
Un signal handler doit être async-signal safe, en gros réentrant.
Et exit() ne l'est pas, contrairement à _exit(), notamment puisqu'il appelle les callbacks enregistrés avec atexit().
Là tu n'as pas vraiment de risque de deadlock/crash normalement, mais écrire du code exécutant dans le contexte d'un signal handler est subtil, par exemple :
```
static void
wait4pid_close(void)
{
if ( listener_set ) {
set_proc_listen(sock, 0);
listener_set = 0;
}
if ( sock ) {
close(sock);
sock = 0;
}
}
```
Les variables listener_set et sock devraient être volatile (elles pourraient aussi être static).
La façon propre de faire serait d'utiliser select() sur ton socket, pour sortir lorsque le timeout est atteint.
par exemple, le fait que l'UE aurait interdit aux carioles roumaines de circuler
Je ne sais pas qui est à l'origine de cette décision, mais c'est vraiment une bonne idée, parce qu'entre ça et les chiens errants, c'est vraiment chaud la conduite en Roumanie (surtout de nuit, parce que les carrioles n'ont pas de dispositifs rétro-réfléchissants)…
Déjà un tcpdump sur la machine cible te permettra de voir si le SYN arrive.
Ensuite, tu ne précises pas: tu as un switch, un routeur, une box?
Non parce que ça ressemble furieusement à un paquet droppé entre les deux…
Il y a os.walk() pour ça. Il faut utiliser fnmatch pour matcher les noms de fichiers, os.walk() ne le supporte pas.
Sinon il y a WalkDir de Nick Coghlan (core dev Python) qui est pas mal.
Mais ça ne devrait pas accélérer, ça sera toujours O(nombre de fichiers à traiter) (en fait O(nombre de fichiers dans l'arborescence), mais le facteur dominant est le traitement des fichiers).
Sinon, j'imagine que faire un commit à chaque requête n'est pas optimal, tu devrais pouvoir te contenter de le faire à la fin (dans closeDB).
Dans le cas général, lorsqu'un processus se termine, cela se passe très simplement :
- ses processus fils sont reparentés à init
- ils continuent leur vie, et lorsqu'ils meurent init effectue le waitpid()
Mais dans ton cas, c'est un peu plus complexe, à cause du tty. Lorsque le shell se termine :
- comme le shell "controllait" le tty (controlling process, session leader), le noyau (le driver tty) envoie un SIGHUP à tous les process dans le foreground group. C'est ce signal qui provoque la terminaison des processus. Mais ce signal peut être ignoré, par exemple avec sighup, ou on peut carrément lancer les process dans une nouvelle session et sans controlling tty avec setsid.
Notes :
Dans tous les cas, ce n'est pas le shell lui-même qui tue les processus fils.
C'est en fait bien plus complexe sous POSIX, mais les process groups, sessions, etc…
Les seuls systèmes de fichier supportés par des systèmes propriétaires sont des systèmes de fichier propriétaires ? Étonnant, non ?
Plus sérieusement, tu as par exemple des formats standards comme ISO 9660 ou UDF (i.e. supports optiques, pas pour disque dur/clé USB). Sinon, un montage NFS/CIFS.
Heu, c'est pas vrai, Debian t'installe exim et le démarre.
Je crois qu'il est configuré pour écouter sur 127.0.0.1 mais ça fait quand même un service réseau qui tourne.
C'est faux, si tu n'effectues pas l'étape de sélection des paquets après l'installation du système de base, exim n'est pas installé. Je n'ai pas de MTA, sur aucune de mes 3 machines.
Ensuite c'est un peu fort de parler de marketing pour un projet qui n'a rien à vendre.
Marketing, affirmation fallacieuse, peu importe le terme.
Une installation de Debian par défaut (comprendre sélection uniquement des paquets de base) ne démarre aucun service, pas même ssh. Ce "record" est donc du pur marketing, et ne présage en rien de la sécurité du système (que je ne remets pas en cause, il y a un gros travail derrière).
We remain proud of OpenBSD’s record of more than ten years with only two remote holes in the default install
Je vais me faire traiter de troll, mais pour moi cette affirmation est bidon.
Parce qu'il s'agit de failles exploitables à distance dans une installation qui ne démarre que le serveur ssh.
Je suis curieux de savoir combien de telles failles, à configuration identique, ont connu la plupart des distributions Linux.
Oui, enfin uniquement sur des réseaux wifi (802.11 and co), il faut le préciser…
C'est bien connu que TCP pose problème sur les réseaux sans-fil, c'est d'ailleurs pour cela qu'il y a dans 802.11 un mécanisme de retransmission au niveau LLC qu'il n'y a pas pour ethernet.
Le wifi c'est juste pourri, avec un peu de chance ça disparaîtra avec la 4G et la femto.
Attention, sémaphore binaire et mutex ne sont pas exactement identiques, du moins sous POSIX.
Un mutex ne peut être déverrouillé que par le thread qui l'a verrouillé, alors que n'importe quel process/thread peut faire un up sur une sémaphore. Ca permet par exemple d'implémenter simplement un rendez-vous point.
Les locks Python sont des sémaphores binaires, pas des mutex.
Ce que tu implémentes ici, c'est ce qu'on appelle une barrière.
C'est disponible pour les threads POSIX avec pthread_barrier_init()/pthread_barrier_wait(), mais pas pour les IPC.
Sous Python, le module multiprocessing supporte les barrières inter-process, par exemple.
[^] # Re: free n'est pas fiable
Posté par neologix . En réponse au message Utilisation de la RAM. Évalué à 4.
Pour savoir ce qui est réellement alloué, il faut regarder Committed_AS.
Après, la différence est déjà dans la mémoire inactive: il s'agit probablement de process qui ont fait des mmap, soit file-backed read-only (donc pas besoin de swapper pour réclamer), soit anonymous mais pas encore écrit dedans (donc pas non plus besoin de swapper pour réclamer), et après quelque temps, le noyau les a dégagés de la RAM pour faire de la place pour le page cache (le degré dépend de la swapinness). Du coup ils n'apparaissent pas dans RSS, mais sont toujours pris en compte par free.
J'ai aussi déjà eu des cas où, en ext3 monté avec data=ordered, supprimer un fichier déjà ouvert va laisser les entrées correspondantes dans le page cache, du coup ils apparaissent en inactive dans /proc/meminfo, free les rapporte toujours, mais on ne voit évidemment rien dans ps.
En gros, tant que ton Committed_AS n'augmente pas, pas de souci…
# free n'est pas fiable
Posté par neologix . En réponse au message Utilisation de la RAM. Évalué à 2.
Mais pas du tout.
Que donnent:
et
[^] # Re: Ai-je bien compris ?
Posté par neologix . En réponse au journal Performances des processeurs Intel et optimisation. Évalué à 2.
Et oui, d'où l'utilité des barrières.
La performance est à ce prix.
Pour vraiment s'amuser, il y a l'architecture alpha, qui peut réordonner les "dependant reads". Donc en gros, quand tu parcours une liste chaînée, tu peux récupérer une valeur non initialisée dans ton noeud: il faut une read barrier même pour lire une iste chainée ;-)
[^] # Re: Ai-je bien compris ?
Posté par neologix . En réponse au journal Performances des processeurs Intel et optimisation. Évalué à 2.
Non, à cause des store buffers et invalidate queues, il y a besoin de memory barriers:
http://en.wikipedia.org/wiki/Memory_barrier
Ou mieux la référence :
https://www.kernel.org/pub/linux/kernel/people/paulmck/perfbook/perfbook.html
[^] # Re: Ai-je bien compris ?
Posté par neologix . En réponse au journal Performances des processeurs Intel et optimisation. Évalué à 3.
Non, ce n'est pas standard, mais c'est plutôt une limite de l'optimisation faite par GCC.
Si ta variable n'est pas volatile, le compilateur peut tout à fait hoister la lecture.
Le problème c'est que si tu as une variable globale - notamment non statique - et que dans ta boucle tu fais:
variable = 0;
while (variable) {
foo(autre_variable);
…
}
et que GCC ne peut pas prouver que foo(autre_variable) ne modifie pas variable, il va forcer un reload.
Par contre si tu appelles une fonction pure, il y a des chances que GCC ne fasse pas le reload.
Essaie d'appeler une fonction static.
En pratique dans ce genre de boucle tu souvent toujours des syscalls/instructions qui vont provoquer une memfence (du genre un mutex ou opération atomique sur une autre variable). Sinon, en l'absence de telles instructions, en effet dans un cas simple comme au dessus ça ne suffit pas à le garantir.
Mais en pratique dès qu'un thread fait un appel système, context switch ou est schedulé sur un autre core, c'est équivalent à une memory barrier.
[^] # Re: Ai-je bien compris ?
Posté par neologix . En réponse au journal Performances des processeurs Intel et optimisation. Évalué à 3.
Pour l'atomicité non, mais comme tu l'as dit, ça force le compilateur à faire des fetch/store, et ça peut être utile dans un contexte multi-threadé.
Il y a par exemple cette macro utilisée pas mal dans le kernel:
#define ACCESS_ONCE(x) (*(volatile typeof(x) *)&(x))
cf https://lwn.net/Articles/508991/
Ca ne garantit pas que tu liras la valeur la plus récente écrite (pas de memfence contrairement au volatile de java), mais tu la liras éventuellement.
Aussi, ça garantit que la variable ne sera lue qu'une fois (sinon le compilo pourrait la dégager des registres et la relire plus tard).
Et ça c'est utile. Idem pour les écritures.
Après, il est vrai que dans 99% des cas, un volatile dans un code multi-threadé est mauvais signe (d'où le fameux poste de Linus "volatile considered harmful"), mais utilisé à bon escient, si ça t'évite une memfence ou une instruction atomique, ça peut être un gain non négligeable.
[^] # Re: Multicast
Posté par neologix . En réponse au message Cherche protocole P2P inverse à BitTorrent. Évalué à 3.
On utilise murder au boulot, et ça marche très bien:
Par contre j'ai dû fait un peu de tuning pour optimiser le temps de distribution, il faudra que je remonte mes patches un de ces jours…
# premature optimization is the root of all evil
Posté par neologix . En réponse au message Compilateur "streamé". Évalué à 7. Dernière modification le 25 mars 2013 à 10:27.
Bah déjà, pour peu que ton makefile soit bien fait, tu ne vas pas tout recompiler à chaque fois, seulement le différentiel.
Ensuite, j'ai un doute sur le fait que la création des process pèsent lourd face à la phase de compilation.
Par exemple, voilà le résultat d'un benchmark d'une compilation clean de Python :
time :
strace -c -f :
La grosse majorité est passé en userland, et le reste contient les I/O, etc. Donc fork()/exec() & Co ne pèsent pas lourd, sans compter que dans ton approche il y aurait aussi beaucoup d'IPC qui n'est pas gratuit non plus…
[^] # Re: lire l'aide de Maven et appliquer les conseils fournis dans la liste des erreurs
Posté par neologix . En réponse au message Compilation de code source opennms 1.10.8. Évalué à 3.
Tout est dit.
L'interface CommonDataSource a gagné une méthode avec Java 1.7: comme la classe C3P0ConnectionFactory ne l'implémente pas, ça pète.
La raison est très simple, tu utilises JDK 1.7 alors qu'opennms n'a pas été porté dessus. Utilise JDK 1.6, et ça devrait rouler.
[^] # Re: intéressant
Posté par neologix . En réponse au journal wait4: attendre la fin d’un ou plusieurs processus quelconques . Évalué à 4.
Oula je plane, en effet.
Sinon, deux autres remarques :
- la version avec polling a une race, si jamais le PID est recyclé entre deux polling. Une alternative serait par exemple d'ouvrir /proc//status, et de faire un read() régulièrement: si jamais le processus est mort entre temps, tu vas prendre ESRCH. Mais bon, ce n'est pas portable, et il y a déjà netlink pour Linux.
- le socket netlink reçoit un message par fork()/exec()/exit(), pour tous les process sur la machine: s'il beaucoup de process sont créés, le socket buffer risque de se remplir.
Démo:
shell 1:
shell 2:
Tu devrais pouvoir mettre en place un filtre pour ne recevoir que les notifications d'intérêt, c'est pas trivial (BPF) mais ça peut être intéressant ;-)
# intéressant
Posté par neologix . En réponse au journal wait4: attendre la fin d’un ou plusieurs processus quelconques . Évalué à 10.
Intéressant !
Quelques remarques :
Ca ne fonctionnait pas sur ma bécanne, voilà un patch :
Il y a un problème dans le cas où netlink n'est pas supporté et on passe le PID d'un process auquel on ne peut pas envoyer de signal: EPERM est retourné par kill(), et wait4 ne va jamais retourner.
Un signal handler doit être async-signal safe, en gros réentrant.
Et exit() ne l'est pas, contrairement à _exit(), notamment puisqu'il appelle les callbacks enregistrés avec atexit().
Là tu n'as pas vraiment de risque de deadlock/crash normalement, mais écrire du code exécutant dans le contexte d'un signal handler est subtil, par exemple :
```
static void
wait4pid_close(void)
{
if ( listener_set ) {
set_proc_listen(sock, 0);
listener_set = 0;
}
}
```
Les variables listener_set et sock devraient être volatile (elles pourraient aussi être static).
La façon propre de faire serait d'utiliser select() sur ton socket, pour sortir lorsque le timeout est atteint.
Enfin c'est un détail.
Sinon :
ec = payload->evt.event_data.exit.exit_code / 256;
Tu peux utiliser WEXITSTATUS().
Dans set_proc_listen, tu ne retry pas sur EINTR (peu probable).
[^] # Re: Magic SysRq
Posté par neologix . En réponse à la dépêche Infection par rootkit « SSHd Spam » sur des serveurs RHEL/CentOS. Évalué à 10.
En même temps, si on ne sait pas lire…
[^] # Re: Normal
Posté par neologix . En réponse au message recherche trolleur de compétition. Évalué à 2.
Je ne sais pas qui est à l'origine de cette décision, mais c'est vraiment une bonne idée, parce qu'entre ça et les chiens errants, c'est vraiment chaud la conduite en Roumanie (surtout de nuit, parce que les carrioles n'ont pas de dispositifs rétro-réfléchissants)…
[^] # Re: tcpdump, topologie? - WiFi, FAIbox merdique ?
Posté par neologix . En réponse au message connexion réseau bizarre. Évalué à 2.
Non, même si c'est sur la même machine, ce n'est pas normal.
Que renvoient
ip6tables -L
et
route -n
Tu arrives à pinger ?
# tcpdump, topologie?
Posté par neologix . En réponse au message connexion réseau bizarre. Évalué à 2.
Déjà un tcpdump sur la machine cible te permettra de voir si le SYN arrive.
Ensuite, tu ne précises pas: tu as un switch, un routeur, une box?
Non parce que ça ressemble furieusement à un paquet droppé entre les deux…
[^] # Re: regarde si tu ne peux pas faire une simple recherche
Posté par neologix . En réponse au message Optimisation programme. Évalué à 3.
Il y a os.walk() pour ça. Il faut utiliser fnmatch pour matcher les noms de fichiers, os.walk() ne le supporte pas.
Sinon il y a WalkDir de Nick Coghlan (core dev Python) qui est pas mal.
Mais ça ne devrait pas accélérer, ça sera toujours O(nombre de fichiers à traiter) (en fait O(nombre de fichiers dans l'arborescence), mais le facteur dominant est le traitement des fichiers).
Sinon, j'imagine que faire un commit à chaque requête n'est pas optimal, tu devrais pouvoir te contenter de le faire à la fin (dans closeDB).
# SIGHUP
Posté par neologix . En réponse au message Kill d'un processus et processus fils. Évalué à 6.
Dans le cas général, lorsqu'un processus se termine, cela se passe très simplement :
- ses processus fils sont reparentés à init
- ils continuent leur vie, et lorsqu'ils meurent init effectue le waitpid()
Mais dans ton cas, c'est un peu plus complexe, à cause du tty. Lorsque le shell se termine :
- comme le shell "controllait" le tty (controlling process, session leader), le noyau (le driver tty) envoie un SIGHUP à tous les process dans le foreground group. C'est ce signal qui provoque la terminaison des processus. Mais ce signal peut être ignoré, par exemple avec sighup, ou on peut carrément lancer les process dans une nouvelle session et sans controlling tty avec setsid.
Notes :
Dans tous les cas, ce n'est pas le shell lui-même qui tue les processus fils.
C'est en fait bien plus complexe sous POSIX, mais les process groups, sessions, etc…
Pour le minimum à savoir, tu peux voir ici : http://www.win.tue.nl/~aeb/linux/lk/lk-10.html
# donc en gros
Posté par neologix . En réponse au message Obligé d'utiliser un système de fichier propriétaire ?. Évalué à 6.
Les seuls systèmes de fichier supportés par des systèmes propriétaires sont des systèmes de fichier propriétaires ? Étonnant, non ?
Plus sérieusement, tu as par exemple des formats standards comme ISO 9660 ou UDF (i.e. supports optiques, pas pour disque dur/clé USB). Sinon, un montage NFS/CIFS.
[^] # Re: record bidon
Posté par neologix . En réponse à la dépêche OpenBSD 5.2. Évalué à 2.
C'est faux, si tu n'effectues pas l'étape de sélection des paquets après l'installation du système de base, exim n'est pas installé. Je n'ai pas de MTA, sur aucune de mes 3 machines.
Marketing, affirmation fallacieuse, peu importe le terme.
[^] # Re: record bidon
Posté par neologix . En réponse à la dépêche OpenBSD 5.2. Évalué à 3.
Une installation de Debian par défaut (comprendre sélection uniquement des paquets de base) ne démarre aucun service, pas même ssh. Ce "record" est donc du pur marketing, et ne présage en rien de la sécurité du système (que je ne remets pas en cause, il y a un gros travail derrière).
# record bidon
Posté par neologix . En réponse à la dépêche OpenBSD 5.2. Évalué à 1.
Je vais me faire traiter de troll, mais pour moi cette affirmation est bidon.
Parce qu'il s'agit de failles exploitables à distance dans une installation qui ne démarre que le serveur ssh.
Je suis curieux de savoir combien de telles failles, à configuration identique, ont connu la plupart des distributions Linux.
[^] # Re: En parlant de TCP...
Posté par neologix . En réponse à la dépêche MPTCP, TCP dans un monde ultra‐connecté. Évalué à 6.
Oui, enfin uniquement sur des réseaux wifi (802.11 and co), il faut le préciser…
C'est bien connu que TCP pose problème sur les réseaux sans-fil, c'est d'ailleurs pour cela qu'il y a dans 802.11 un mécanisme de retransmission au niveau LLC qu'il n'y a pas pour ethernet.
Le wifi c'est juste pourri, avec un peu de chance ça disparaîtra avec la 4G et la femto.
[^] # Re: Hum
Posté par neologix . En réponse au journal Les sémaphores. Évalué à 3.
Attention, sémaphore binaire et mutex ne sont pas exactement identiques, du moins sous POSIX.
Un mutex ne peut être déverrouillé que par le thread qui l'a verrouillé, alors que n'importe quel process/thread peut faire un up sur une sémaphore. Ca permet par exemple d'implémenter simplement un rendez-vous point.
Les locks Python sont des sémaphores binaires, pas des mutex.
# barrière
Posté par neologix . En réponse au journal Les sémaphores. Évalué à 4.
Ce que tu implémentes ici, c'est ce qu'on appelle une barrière.
C'est disponible pour les threads POSIX avec pthread_barrier_init()/pthread_barrier_wait(), mais pas pour les IPC.
Sous Python, le module multiprocessing supporte les barrières inter-process, par exemple.
# votre/vôtre
Posté par neologix . En réponse au sondage Votre nationalité...?. Évalué à 7.
"votre nationalité"
"la vôtre"
Par ailleurs, puisqu'il s'agit de l'adjectif, on écrit "nationalité française", sans majuscule. Mais on écrit "Les Françaises…".