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