Simule um processo inábil no estado D

14

Para cenários de teste de catástrofe no ambiente de servidor externo, estamos procurando uma maneira fácil de manter um processo parado no estado D (suspensão ininterrupta).

Alguma maneira fácil? Um exemplo de código de exemplo C seria uma vantagem :)

Editar - a primeira resposta é semi-correta, pois o processo é mostrado no estado D, mas ainda recebe sinais e pode ser eliminado

er453r
fonte
Em qual sistema operacional? Ou você está procurando uma solução portátil (não tem certeza se existe alguma)?
21414 derobert
@mr_tron - isto não é "ininterrupta" :)
er453r
1
@derobert - desculpe por não ser preciso - ubuntu server 12.04.4
er453r
1
Para aqueles que procuram uma solução "funcional", acesse stackoverflow.com/a/22754979/2182622
noname

Respostas:

2

Eu tive o mesmo problema e resolvi criando um módulo do kernel que fica preso no estado D.

Como não tenho experiência em módulos, peguei o código desse histórico com algumas modificações encontradas em algum lugar .

O resultado é um dispositivo em / dev / memory que fica preso na leitura, mas pode ser acordado gravando nele (ele precisa de duas gravações, não sei por que, mas não me importo).

Para usá-lo apenas:

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

Para desbloquear, de outro 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;
}
Figo
fonte
10

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

Um processo é colocado em um sono ininterrupto (STAT D) quando precisa esperar algo (normalmente E / S) e não deve manipular sinais enquanto aguarda. Isso significa que você não pode kill, porque tudo que mata é enviar sinais. Isso pode acontecer no mundo real se você desconectar o servidor NFS enquanto outras máquinas tiverem conexões de rede abertas.

Podemos criar nossos próprios processos ininterruptos de duração limitada, aproveitando a vforkchamada do sistema. vforké como fork, exceto que o espaço de endereço não é copiado do pai para o filho, em antecipação a um execque apenas jogaria fora os dados copiados. Convenientemente para nós, quando vforko pai espera ininterruptamente (por meio de wait_on_completion) a criança 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

Vemos a criança ( PID 1973, PPID 1972) em um sono interrompível e os pais ( PID 1972, PPID 13291- a concha) em um sono ininterrupto enquanto aguarda 60 segundos na criança.

Uma coisa interessante (travessa?) Sobre esse script é que os processos em um sono ininterrupto contribuem para a média de carga de uma máquina. Portanto, você pode executar esse script 100 vezes para fornecer temporariamente a uma máquina uma carga média elevada em 100, conforme relatado por uptime.

mikeserv
fonte
Exatamente o que estava procurando! Muito obrigado!
er453r
3
o triste é que o processo está no estado D, mas eu posso matá-lo com kill: /
er453r
@ er453r - desculpe, cara. Eu não sei muito sobre isso honestamente - a resposta foi apenas uma cópia / pasta, e é por isso que eu a defino como conteúdo wiki da comunidade . Eu li sua pergunta, fiquei curioso, depois pesquisei no Google e descobri o que eu pensava ser uma informação bastante interessante. Isso é o que você vê acima. Mas os votos e o resto não contribuem para a minha própria reputação, porque é wikied, e porque eu meio que a roubei. Talvez haja mais informações nessa página que possam explicar o porquê?
mikeserv
Obrigado - eu li como você publicou. Eu já procurei na internet para isso, mas cada um tenta se livrar de estes processos, não criá-los: P Geralmente, a troca de pilha é sempre o último recurso para mim :)
er453r
Sim, ainda posso matar isso também: - /
Leo Ufimtsev
2

Basicamente, você não pode. Leia este artigo, intitulado: TASK_KILLABLE: Novo estado do processo no Linux .

excerto

O kernel Linux® 2.6.25 introduziu um novo estado de processo para adormecer os processos chamado TASK_KILLABLE, que oferece uma alternativa ao TASK_UNINTERRUPTIBLE eficiente, mas potencialmente inábil, e ao TASK_INTERRUPTIBLE fácil de despertar, mas mais seguro.

Este SO Q&A intitulado: O que é um processo ininterrupto? também explica isso.

Eu descobri isso neste livro muito interessante, intitulado: A Interface de Programação Linux: Um Manual de Programação de Sistemas Linux e UNIX .

Florent
fonte
Isso não significa que você não pode produzir um processo inabalável. Isso significa apenas que o número de chamadas do sistema que não são passíveis de queda está diminuindo à medida que essas chamadas passam a usar o novo TASK_KILLABLEestado.
Martijn Pieters