Simuler un processus impossible à tuer dans l'état D

14

Pour les scénarios de test de catastrophe sur un environnement hors serveur, nous recherchons un moyen simple de bloquer un processus en état D (veille sans interruption).

Des moyens simples? Un exemple de code C serait un plus :)

Modifier - la première réponse est semi-correcte, car le processus est en état D, mais il reçoit toujours des signaux et peut être tué

er453r
la source
Sur quel système d'exploitation? Ou cherchez-vous une solution portable (vous ne savez pas s'il y en a une)?
derobert
@mr_tron - ce n'est pas "sans interruption" :)
er453r
1
@derobert - désolé de ne pas être précis - serveur ubuntu 12.04.4
er453r
1
Pour ceux qui recherchent une solution "fonctionnelle", rendez-vous sur stackoverflow.com/a/22754979/2182622
noname

Réponses:

2

J'ai eu le même problème et l'ai résolu en créant un module de noyau qui se coince dans l'état D.

Comme je n'ai aucune expérience dans les modules, j'ai pris le code de ce turorial avec quelques modifications trouvées quelque part esle .

Le résultat est un périphérique sur / dev / memory qui reste bloqué en lecture mais peut être réveillé en écrivant dessus (il a besoin de deux écritures, je ne sais pas pourquoi mais je m'en fiche).

Pour l'utiliser simplement:

# make
# make mknod
# make install
# cat /dev/memory   # this gets blocked

Pour débloquer, depuis un autre terminal:

# echo -n a > /dev/memory
# echo -n a > /dev/memory

Makefile:

obj-m += memory.o

all:
    make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules

clean:
    make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

install:
    sudo insmod memory.ko

uninstall:
    sudo rmmod memory

mknod:
    sudo mknod /dev/memory c 60 0
    sudo chmod 666 /dev/memory

Code pour memory.c:

/* Necessary includes for device drivers */
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h> /* printk() */
#include <linux/slab.h> /* kmalloc() */
#include <linux/fs.h> /* everything... */
#include <linux/errno.h> /* error codes */
#include <linux/types.h> /* size_t */
#include <linux/proc_fs.h>
#include <linux/fcntl.h> /* O_ACCMODE */
#include <asm/uaccess.h> /* copy_from/to_user */
#include <linux/sched.h>

MODULE_LICENSE("Dual BSD/GPL");

/* Declaration of memory.c functions */
int memory_open(struct inode *inode, struct file *filp);
int memory_release(struct inode *inode, struct file *filp);
ssize_t memory_read(struct file *filp, char *buf, size_t count, loff_t *f_pos);
ssize_t memory_write(struct file *filp, char *buf, size_t count, loff_t *f_pos);
void memory_exit(void);
int memory_init(void);

/* Structure that declares the usual file */
/* access functions */
ssize_t memory_write( struct file *filp, char *buf, size_t count, loff_t *f_pos);
ssize_t memory_read(struct file *filp, char *buf, size_t count, loff_t *f_pos);
int memory_open(struct inode *inode, struct file *filp);
int memory_release(struct inode *inode, struct file *filp);
struct file_operations memory_fops = {
    .read = memory_read,
    .write = memory_write,
    .open = memory_open,
    .release = memory_release
};

/* Declaration of the init and exit functions */
module_init(memory_init);
module_exit(memory_exit);

/* Global variables of the driver */
/* Major number */
int memory_major = 60;
/* Buffer to store data */
char *memory_buffer;

int memory_init(void) {
    int result;

    /* Registering device */
    result = register_chrdev(memory_major, "memory", &memory_fops);
    if (result < 0) {
        printk(
                "<1>memory: cannot obtain major number %d\n", memory_major);
        return result;
    }

    /* Allocating memory for the buffer */
    memory_buffer = kmalloc(1, GFP_KERNEL); 
    if (!memory_buffer) { 
        result = -ENOMEM;
        goto fail; 
    } 
    memset(memory_buffer, 0, 1);

    printk("<1>Inserting memory module\n"); 
    return 0;

fail: 
    memory_exit(); 
    return result;
}

void memory_exit(void) {
    /* Freeing the major number */
    unregister_chrdev(memory_major, "memory");

    /* Freeing buffer memory */
    if (memory_buffer) {
        kfree(memory_buffer);
    }

    printk("<1>Removing memory module\n");

}

int memory_open(struct inode *inode, struct file *filp) {

    /* Success */
    return 0;
}

int memory_release(struct inode *inode, struct file *filp) {

    /* Success */
    return 0;
}
static DECLARE_WAIT_QUEUE_HEAD(wq);
static volatile int flag = 0;

ssize_t memory_read(struct file *filp, char *buf, 
        size_t count, loff_t *f_pos) { 

    printk("<1>going to sleep\n");
    flag = 0;
    //wait_event_interruptible(wq, flag != 0);
    wait_event(wq, flag != 0);

    printk("<1>Reading from memory module\n");
    /* Transfering data to user space */ 
    copy_to_user(buf,memory_buffer,1);

    /* Changing reading position as best suits */ 
    if (*f_pos == 0) { 
        *f_pos+=1; 
        return 1; 
    } else { 
        return 0; 
    }
}

ssize_t memory_write( struct file *filp, char *buf,
        size_t count, loff_t *f_pos) {

    char *tmp;

    printk("<1>wake someone up\n");
    flag = 1;
    //wake_up_interruptible(&wq);
    wake_up(&wq);

    printk("<1>Writting to memory module\n");
    tmp=buf+count-1;
    copy_from_user(memory_buffer,tmp,1);
    return 1;
}
Figo
la source
10

Depuis https://blogs.oracle.com/ksplice/entry/disown_zombie_children_and_the

Un processus est mis en veille sans interruption (STAT D) lorsqu'il doit attendre quelque chose (généralement des E / S) et ne doit pas gérer les signaux en attendant. Cela signifie que vous ne pouvez pas le killfaire, car tout ce que tuer fait, c'est lui envoyer des signaux. Cela peut se produire dans le monde réel si vous débranchez votre serveur NFS alors que d'autres machines y ont des connexions réseau ouvertes.

Nous pouvons créer nos propres processus ininterrompus de durée limitée en tirant parti de l' vforkappel système. vforkest similaire fork, sauf que l'espace d'adressage n'est pas copié du parent dans l'enfant, en prévision d'un execqui ne ferait que jeter les données copiées. Idéalement pour nous, lorsque vous vforkle parent attend sans interruption (par le biais de wait_on_completion) l'enfant execou exit:

jesstess@aja:~$ cat uninterruptible.c 
int main() {
    vfork();
    sleep(60);
    return 0;
}
jesstess@aja:~$ gcc -o uninterruptible uninterruptible.c
jesstess@aja:~$ echo $$
13291
jesstess@aja:~$ ./uninterruptible
and in another shell:

jesstess@aja:~$ ps -o ppid,pid,stat,cmd $(pgrep -f uninterruptible)

13291  1972 D+   ./uninterruptible
 1972  1973 S+   ./uninterruptible

On voit l'enfant ( PID 1973, PPID 1972) dans un sommeil interruptible et le parent ( PID 1972, PPID 13291- la coquille) dans un sommeil ininterrompu pendant qu'il attend 60 secondes sur l'enfant.

Une chose intéressante (espiègle?) À propos de ce script est que les processus dans un sommeil ininterrompu contribuent à la moyenne de charge d'une machine. Vous pouvez donc exécuter ce script 100 fois pour donner temporairement à une machine une charge moyenne élevée de 100, comme indiqué par uptime.

mikeserv
la source
Exactement ce que l'on recherchait! Merci beaucoup!
er453r
3
le plus triste est que le processus est en état D, mais je peux le tuer avec kill: /
er453r
@ er453r - désolé, mec. Honnêtement, je ne sais pas grand-chose - la réponse était juste un copier / coller, c'est pourquoi je l'ai défini comme contenu wiki communautaire . J'ai lu votre question, j'étais moi-même curieux, puis j'ai fait une recherche sur Google et j'ai trouvé ce que je pensais être des informations assez intéressantes. C'est ce que vous voyez ci-dessus. Mais les votes et le reste ne contribuent pas à ma propre réputation parce que c'est wikied, et parce que je l'ai en quelque sorte volé. Peut-être qu'il y a plus d'informations sur cette page qui pourraient expliquer pourquoi?
mikeserv
Merci - je l'ai lu au moment où vous l'avez publié. J'ai déjà cherché sur Internet pour cela, mais tout le monde essaie de se débarrasser de ces processus, pas de les créer: P Généralement, l'échange de pile est toujours le dernier recours pour moi :)
er453r
Oui, je peux toujours tuer ça aussi: - /
Leo Ufimtsev
2

Fondamentalement, vous ne pouvez pas. Lisez cet article, intitulé: TASK_KILLABLE: Nouvel état de processus sous Linux .

extrait

Le noyau Linux® 2.6.25 a introduit un nouvel état de processus pour mettre les processus en veille appelé TASK_KILLABLE, qui offre une alternative au TASK_UNINTERRUPTIBLE efficace mais potentiellement impossible à tuer et au TASK_INTERRUPTIBLE facile à réveiller mais plus sûr.

Ce SO Q&A intitulé: Qu'est-ce qu'un processus ininterrompu? l'explique aussi.

J'ai découvert cela dans ce livre très intéressant intitulé: L'interface de programmation Linux: un manuel de programmation système Linux et UNIX .

Florent
la source
Cela ne signifie pas que vous ne pouvez pas produire un processus impossible à tuer. Cela signifie simplement que le nombre d'appels système qui ne peuvent pas être tués diminue à mesure que ces appels passent à l'utilisation du nouvel TASK_KILLABLEétat.
Martijn Pieters du