Simule un proceso imposible de matar en estado D


14

Para escenarios de prueba de catástrofe en nuestro entorno de servidor, estamos buscando una manera fácil de hacer que un proceso se atasque en estado D (suspensión ininterrumpida).

¿Alguna forma fácil? Un ejemplo de código de ejemplo C sería un plus :)

Editar : la primera respuesta es semi-correcta, ya que se muestra que el proceso está en estado D, pero aún recibe señales y puede ser eliminado



¿En qué sistema operativo? ¿O está buscando una solución portátil (no estoy seguro si hay una)?
derobert

@mr_tron - esto no es "ininterrumpido" :)
er453r

1
@derobert - perdón por no ser preciso - ubuntu server 12.04.4
er453r

1
Para aquellos que buscan una solución "funcional", visite stackoverflow.com/a/22754979/2182622
noname

Respuestas:


2

Tuve el mismo problema y lo resolví creando un módulo de kernel que se atasca en estado D.

Como no tengo experiencia en módulos, tomé el código de esta publicación con algunas modificaciones encontradas en algún lugar .

El resultado es un dispositivo en / dev / memory que se atasca en la lectura pero se puede activar escribiendo en él (necesita dos escrituras, no sé por qué pero no me importa).

Para usarlo solo:

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

Para desbloquear, desde otra 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

Código para 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

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

Un proceso se pone en suspensión ininterrumpida (STAT D) cuando necesita esperar algo (generalmente E / S) y no debe manejar señales mientras espera. Esto significa que no puedes killhacerlo, porque todo lo que mata es enviarle señales. Esto podría suceder en el mundo real si desconecta su servidor NFS mientras otras máquinas tienen conexiones de red abiertas.

Podemos crear nuestros propios procesos ininterrumpibles de duración limitada aprovechando la vforkllamada al sistema. vforkes como fork, excepto que el espacio de direcciones no se copia del padre en el hijo, en anticipación de una execque simplemente arrojaría los datos copiados. Convenientemente para nosotros, cuando usted vforkel padre espera ininterrumpidamente (a modo de wait_on_completion) en el niño 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

Vemos al niño ( PID 1973, PPID 1972) en un sueño interrumpible y al padre ( PID 1972, PPID 13291- el caparazón) en un sueño ininterrumpible mientras espera 60 segundos en el niño.

Una cosa interesante (¿traviesa?) De este script es que los procesos en reposo ininterrumpible contribuyen al promedio de carga de una máquina. Por lo tanto, podría ejecutar este script 100 veces para proporcionar temporalmente a una máquina un promedio de carga elevado en 100, según lo informado por uptime.


¡Exactamente lo que estaba buscando! ¡Muchas gracias!
er453r

3
Lo triste es que el proceso está en estado D, pero puedo matarlo con kill: /
er453r

@ er453r - lo siento, hombre. Honestamente, no sé mucho sobre eso: la respuesta fue solo copiar / pegar, por eso lo configuré como contenido wiki comunitario . Leí tu pregunta, tuve curiosidad, busqué en Google y descubrí lo que pensé que era información bastante interesante. Eso es lo que ves arriba. Pero los votos y el resto no contribuyen a mi propia reputación porque es wikied, y porque lo robé. Tal vez hay más información en esa página que podría explicar por qué.
mikeserv

Gracias. Lo he leído tal como lo publicaste. Ya he buscado esto en Internet, pero todos intentan deshacerse de estos procesos, no crearlos: P Generalmente, el intercambio de pila siempre es el último recurso para mí :)
er453r

Sí, todavía puedo matar esto también: - /
Leo Ufimtsev

2

Básicamente, no puedes. Lea este artículo, titulado: TASK_KILLABLE: Nuevo estado del proceso en Linux .

extracto

El kernel de Linux® 2.6.25 introdujo un nuevo estado de proceso para poner los procesos en suspensión llamado TASK_KILLABLE, que ofrece una alternativa a la eficiente pero potencialmente imposible de matar TASK_UNINTERRUPTIBLE y la TASK_INTERRUPTIBLE fácil de despertar pero más segura.

Este SO Q&A titulado: ¿Qué es un proceso ininterrumpido? También lo explica.

Descubrí esto en este libro muy interesante titulado: La interfaz de programación de Linux: un manual de programación del sistema Linux y UNIX .


Eso no significa que no pueda producir un proceso indestructible. Simplemente significa que la cantidad de llamadas al sistema que no se pueden matar está disminuyendo a medida que esas llamadas cambian al uso del nuevo TASK_KILLABLEestado.
Martijn Pieters
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.