come funziona {} while (0) in macro?

Anche se questo argomento è stato discusso molte volte in questo forum e in tutti gli altri forum, ho ancora dei dubbi. Per favore aiuto.

Come funziona do{} while(0) in macro nel kernel Linux? Per esempio,

 #define preempt_disable() do { } while (0) 

Come disabilita il preempt?

 #define might_resched() do { } while (0) 

Come si riprogramma?

Allo stesso modo ho visto macro per lock mutex e altri anche. In che modo questo aiuta? Capisco per il seguente problema ma non per gli esempi sopra.

 #define foo(x) do { do something } while(0) 

Modificare:

Che dire del seguente codice per rt_mutex_lock ?

 /** * rt_mutex_lock - lock a rt_mutex * * @lock: the rt_mutex to be locked */ void __sched rt_mutex_lock(struct rt_mutex *lock) { might_sleep(); rt_mutex_fastlock(lock, TASK_UNINTERRUPTIBLE, 0, rt_mutex_slowlock); } EXPORT_SYMBOL_GPL(rt_mutex_lock); /* * debug aware fast / slowpath lock,trylock,unlock * * The atomic acquire/release ops are compiled away, when either the * architecture does not support cmpxchg or when debugging is enabled. */ static inline int rt_mutex_fastlock(struct rt_mutex *lock, int state, int detect_deadlock, int (*slowfn)(struct rt_mutex *lock, int state, struct hrtimer_sleeper *timeout, int detect_deadlock)) { if (!detect_deadlock && likely(rt_mutex_cmpxchg(lock, NULL, current))) { rt_mutex_deadlock_account_lock(lock, current); return 0; } else{ return slowfn(lock, state, NULL, detect_deadlock); } } 

Sono confuso perché rt_mutex_deadlock_account_lock è definito in due punti nel kernel:

Nel kernel/rtmutex-debug.c :

 void rt_mutex_deadlock_account_lock(struct rt_mutex *lock, struct task_struct *task) { //.... } 

Nel kernel/rtmutex.h :

 #define rt_mutex_deadlock_account_lock(m, t) do { } while (0) 

Nel nuovo kernel 2.6.35.4 nel driver rt_mutex_lock(&adap->bus_lock); ha sostituito il mutex_lock() . Come si chiude questo blocco?

@Kragen ha risposto a cosa fa … mentre il costrutto è per – rende fondamentalmente una macro molto più sicura da usare.

Tuttavia, non penso che risponda alla domanda “come funziona?”:

 #define preempt_disable() do { } while (0) 

La macro è definita per non fare nulla . Perché non vuoi fare nulla?

  • In alcuni casi si desidera utilizzare una macro come segnaposto per fare qualcosa. Ad esempio, è ansible scrivere codice su un sistema in cui “preempt” non è un problema, ma si sa che il codice potrebbe essere trasferito su un sistema in cui “preempt” richiede una gestione speciale. Quindi tu usi una macro ovunque il secondo sistema ne ha bisogno (in modo che la gestione sia facile da abilitare in seguito), ma per il primo sistema definisci quella macro come una macro vuota.

  • In alcuni casi potresti voler fare cose come un’attività composta da parti diverse (ad esempio, START_TABLE (); TABLE_ENTRY (1); TABLE_ENTRY (2); END_TABLE ();). Ciò rende piacevole l’implementazione chiara del tuo tavolo. Ma poi scopri che non hai realmente bisogno della macro END_TABLE (). Per mantenere ordinato il codice cliente, si lascia la macro definita e si definisce semplicemente di non fare nulla. In questo modo, tutte le tue tabelle hanno END_TABLE e il codice è più facile da leggere.

  • Un caso simile può verificarsi con due stati (abilitazione / disabilitazione) in cui uno stato richiede che la macro esegua qualcosa, ma l’altro stato avviene per impostazione predefinita, quindi l’implementazione di uno è “vuoto”: si utilizza ancora la macro perché rende il codice client è più facile da capire, perché indica esplicitamente i luoghi in cui le cose sono abilitate o disabilitate.

Vedi questo link per una spiegazione migliore di quella che potrei dare.

IIRC l’uso del do-while nei macro serve a farli apparire più come una normale funzione invocata; ci sono alcuni problemi di syntax sottili attorno a dichiarazioni unbraced se e cose del genere. Senza il do-while la macro potrebbe sembrare una normale funzione invocante ma funzionerebbe diversamente.

Immagino che in questo caso vengano usate quelle macro in modo che certe chiamate di funzioni vengano compilate a nulla; sembra che potrebbe essere quello che ottieni se CONFIG_PREEMPT non è stato impostato, quindi alcune parti del kernel che sono necessarie solo per il comando semplicemente svaniscono senza di essa. Quindi quei loop non disabilitano il preempt o il ripianificare nulla; ci sarà un’altra definizione (probabilmente una funzione reale) altrove nel sorgente del kernel.