View Single Post
Old 12-01-2005, 09:36   #15
ilsensine
Senior Member
 
L'Avatar di ilsensine
 
Iscritto dal: Apr 2000
Citt: Roma
Messaggi: 15625
Demandare una operazione ad un altro contesto: le workqueue

Una workqueue - lo dice il nome - una coda di operazioni. Ad ogni workqueue associato un kernel thread (o meglio, un thread per cpu) che esegue le operazioni indicate in contesto di processo.
Il loro utilizzo molto semplice; riprendiamo l'esempio sui kernel thread:
Codice:
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kthread.h>
#include <linux/workqueue.h>
#include <linux/delay.h>

static struct task_struct *ktask;
static struct workqueue_struct *wq;

static void kt_work(void *data)
{
	struct work_struct *work = data;
	printk(KERN_INFO "%s\n", __func__);
	kfree(work);
}

static int kt_thread(void *arg)
{
	struct work_struct *work;
	struct workqueue_struct *wq = arg;
	while (!kthread_should_stop()) {
		work = kmalloc(sizeof(*work), GFP_KERNEL);
		if (work) {
			INIT_WORK(work, kt_work, work);
			queue_work(wq, work);
		} else
			printk(KERN_ERR "%s: OOM!\n", __func__);
		msleep(1000);
	}
	return 0;
}

static int __init kt_init(void)
{
	wq = create_workqueue("kworker");
	if (!wq)
		return -ENOMEM;
	ktask = kthread_create(kt_thread, wq, "kthread");
	if (IS_ERR(ktask)) {
		destroy_workqueue(wq);
		return PTR_ERR(ktask);
	} else
		wake_up_process(ktask);
	return 0;
}

static void __exit kt_exit(void)
{
	kthread_stop(ktask);
	flush_workqueue(wq);
	destroy_workqueue(wq);
}

module_init(kt_init);
module_exit(kt_exit);

MODULE_AUTHOR("ilsensine");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("sample kernel process");
La workqueue viene creata tramite la funzione create_workqueue. I thread assegnati alla workqueue saranno visibili tramite top. Un task viene accodato alla queue tramite la funzione queue_work. Un singolo work descritto dalla struttura work_struct, inizializzata dalla macro INIT_WORK(struttura, funzione, parametro della funzione).

Note:
- In questo esempio la workqueue viene creara e distrutta esternamente al kernel thread. Potevamo crearla e distruggerla direttamente nel thread? La risposta no: il nostro thread viene terminato invocando la funzione kthread_stop. Anche la funzione destroy_workqueue chiama kthread_stop per terminare i propri thread. Orbene, kthread_stop blocca un semaforo finch il thread non terminato; quindi se il thread in fase di chiusura chiama a sua volta kthread_stop (o una funzione che la richiama, come destroy_workqueue), abbiamo un bel deadlock.
- Un work pu essere schedulato nel futuro tramite la funzione queue_delayed_work, che ha il prototipo (v. linux/workqueue.h):
Codice:
int queue_delayed_work(struct workqueue_struct *wq,
	struct work_struct *work, unsigned long delay)
dove delay espresso in jiffies (tick di scheduling. In un secondo occorrono HZ tick)
- Solo i moduli con licenza GPL possono creare proprie workqueue. I moduli proprietari possono utilizzare le workqueue di keventd, tramite le funzioni schedule_[delayed_]work.
- flush_workqueue attende la terminazione dei work in coda. Viene chiamata anche da destroy_workqueue, quindi nel codice illustrato risulta superflua ( stata messa solo come esempio).
- Visto che le workqueue sono eseguite in contesto di processo, possibile chiamare funzioni che possono schedulare (come ad es. down).
__________________
0: or %edi, %ecx; adc %eax, (%edx); popf; je 0b-22; pop %ebx; fadds 0x56(%ecx); lds 0x56(%ebx), %esp; mov %al, %al
andeqs pc, r1, #147456; blpl 0xff8dd280; ldrgtb r4, [r6, #-472]; addgt r5, r8, r3, ror #12

Ultima modifica di ilsensine : 23-02-2006 alle 16:01.
ilsensine  offline