Simula un processo non verificabile in stato D.


14

Per gli scenari di test di catastrofe in un ambiente server, stiamo cercando un modo semplice per rendere un processo bloccato nello stato D (ininterrotta sospensione).

Qualche modo semplice? Un esempio di codice C sarebbe un vantaggio :)

Modifica : la prima risposta è semi-corretta, poiché il processo è mostrato in stato D, ma riceve ancora segnali e può essere ucciso



Su quale sistema operativo? Oppure stai cercando una soluzione portatile (non sei sicuro che ce ne sia una)?
derobert,

@mr_tron - questo non è "ininterrotto" :)
er453r

1
@derobert - scusa per non essere preciso - ubuntu server 12.04.4
er453r

1
Per coloro che sono alla ricerca di una soluzione "funzionante", visitare stackoverflow.com/a/22754979/2182622
noname

Risposte:


2

Ho avuto lo stesso problema e l'ho risolto creando un modulo del kernel che si blocca nello stato D.

Dato che non ho alcuna esperienza nei moduli, ho preso il codice da questo tutorial con alcune modifiche trovate da qualche parte .

Il risultato è un dispositivo su / dev / memory che si blocca in lettura ma può essere risvegliato scrivendo su di esso (ha bisogno di due scritture, non so perché ma non mi interessa).

Per usarlo solo:

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

Per sbloccare, da un altro terminale:

# 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

Codice per 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;
}

10

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

Un processo viene messo in modalità di sospensione ininterrotta (STAT D) quando deve attendere qualcosa (in genere I / O) e non deve gestire i segnali durante l'attesa. Questo significa che non puoi kill, perché tutto ciò che uccide fa è inviare segnali. Ciò potrebbe accadere nel mondo reale se si scollega il server NFS mentre altre macchine dispongono di connessioni di rete aperte.

Possiamo creare i nostri processi ininterrotti di durata limitata sfruttando la vforkchiamata di sistema. vforkè come fork, tranne per il fatto che lo spazio degli indirizzi non viene copiato dal genitore nel figlio, in previsione di uno execche semplicemente eliminerebbe i dati copiati. Convenientemente per noi, quando vforkil genitore attende ininterrottamente (a titolo di wait_on_completion) il figlio execo 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

Vediamo il bambino ( PID 1973, PPID 1972) in un sonno interrompibile e il genitore ( PID 1972, PPID 13291- il guscio) in un sonno ininterrotto mentre attende 60 secondi sul bambino.

Una cosa chiara (maliziosa?) Di questo script è che i processi in un sonno ininterrotto contribuiscono alla media del carico per una macchina. Quindi è possibile eseguire questo script 100 volte per fornire temporaneamente a una macchina un carico medio elevato di 100, come riportato da uptime.


Esattamente quello che stavo cercando! Grazie mille!
er453r,

3
la cosa triste è che il processo è in stato D, ma sono in grado di ucciderlo con kill: /
er453r

@ er453r - scusa amico. Onestamente non ne so molto - la risposta è stata solo una copia / incolla, motivo per cui l'ho impostato come contenuto wiki della community . Ho letto la tua domanda, ero curioso anche io, poi ho cercato su Google e ho scoperto quelle che pensavo fossero informazioni piuttosto interessanti. Questo è quello che vedi sopra. Ma i voti e gli altri non contribuiscono alla mia reputazione perché è un fenomeno da capogiro e perché l'ho in qualche modo rubato. Forse ci sono più informazioni su quella pagina che potrebbero spiegare perché?
Mikeserv

Grazie - l'ho letto proprio come l'hai pubblicato. Ho già cercato su Internet per questo, ma tutti cercano di sbarazzarsi di questi processi, non di crearli: P In generale, lo scambio di stack è sempre l'ultima risorsa per me :)
er453r

Sì, posso ancora uccidere anche questo: - /
Leo Ufimtsev

2

Fondamentalmente, non puoi. Leggi questo articolo, intitolato: TASK_KILLABLE: nuovo stato del processo in Linux .

estratto

Il kernel 2.6.25 di Linux® ha introdotto un nuovo stato di processo per mettere in pausa i processi chiamato TASK_KILLABLE, che offre un'alternativa all'efficiente ma potenzialmente non ricaricabile TASK_UNINTERRUPTIBLE e al TASK_INTERRUPTIBLE facile da risvegliare.

Le domande e risposte SO intitolate: Cos'è un processo ininterrotto? lo spiega anche.

L'ho scoperto in questo libro molto interessante intitolato: L'interfaccia di programmazione Linux: un manuale di programmazione del sistema Linux e UNIX .


Ciò non significa che non è possibile produrre un processo invendibile. Significa solo che il numero di chiamate di sistema che non sono verificabili sta diminuendo mentre quelle chiamate passano all'uso del nuovo TASK_KILLABLEstato.
Martijn Pieters,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.