Имитация неубиваемого процесса в состоянии D

14

Для сценариев тестирования на катастрофы в серверной среде мы ищем простой способ перевести процесс в состояние D (непрерывный спящий режим).

Есть ли простые способы? Пример C-кода будет плюсом :)

Редактировать - первый ответ является полукорректным, поскольку показано, что процесс находится в состоянии D, но он все еще получает сигналы и может быть убит

er453r
источник
На какой операционной системе? Или вы ищете портативное решение (не уверены, что оно есть)?
Дероберт
@mr_tron - это не «бесперебойно» :)
er453r
1
@derobert - извините за неточность
er453r
1
Для тех, кто ищет «рабочее» решение, перейдите на stackoverflow.com/a/22754979/2182622
noname

Ответы:

2

У меня была та же проблема, и я решил ее, создав модуль ядра, который застрял в D-состоянии.

Поскольку у меня нет никакого опыта в модулях, я взял код из этого турориала с некоторыми модификациями, найденными где-то раньше .

В результате получается устройство в / dev / memory, которое застревает при чтении, но может быть активировано при записи на него (для этого требуется две записи, я не знаю почему, но мне все равно).

Чтобы использовать это просто:

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

Чтобы разблокировать, из другого терминала:

# 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

Код для 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

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

Процесс помещается в непрерывный сон, (STAT D) когда ему нужно что-то ждать (обычно ввод / вывод), и он не должен обрабатывать сигналы во время ожидания. Это означает, что вы не можете этого killсделать, потому что все, что делает kill - это посылает ему сигналы. Это может произойти в реальном мире, если вы отключите сервер NFS, когда другие машины имеют к нему открытые сетевые подключения.

Мы можем создавать наши собственные непрерывные процессы ограниченной продолжительности, используя преимущества vforkсистемного вызова. vforkЭто похоже на то fork, за исключением того, что адресное пространство не копируется из родительского в дочерний, в ожидании, execкоторое просто выбросит скопированные данные. Нам удобно, когда вы, vforkродитель, непрерывно (в порядке wait_on_completion) ждете ребенка execили 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

Мы видим ребенка ( PID 1973, PPID 1972) в PID 1972, PPID 13291непрерывном сне, а родителя ( - оболочку) в непрерывном сне, пока он ждет 60 секунд на ребенке.

Одна из замечательных (вредных?) Вещей в этом сценарии заключается в том, что процессы в непрерывном сне вносят вклад в среднюю нагрузку на машину. Таким образом, вы можете запустить этот сценарий 100 раз, чтобы временно увеличить нагрузку на машину в среднем на 100, как сообщает uptime.

mikeserv
источник
Именно то, что искал! Большое спасибо!
er453r
3
печально то, что процесс находится в состоянии D, но я могу убить его с помощью kill: /
er453r
@ er453r - извини, чувак. Честно говоря, я мало что знаю об этом - ответ был просто копией / вставкой, поэтому я установил его в качестве вики- контента сообщества . Я прочитал твой вопрос, мне было любопытно, а затем немного погуглил и нашел, как мне показалось, довольно интересную информацию. Это то, что вы видите выше. Но голоса и остальные не влияют на мою репутацию, потому что она вики и потому что я ее украл. Может быть, есть больше информации на этой странице, которая может объяснить, почему?
mikeserv
Спасибо - я прочитал это так же, как вы опубликовали это. Я уже искал в интернете это, но каждый пытается избавиться от этих процессов, а не создавать их: P В общем, обмен стеками для меня всегда последнее средство :)
er453r
Да, я все еще могу убить и это: - /
Лев Уфимцев
2

По сути, вы не можете. Прочтите эту статью под названием: TASK_KILLABLE: Новое состояние процесса в Linux .

выдержка

В ядре Linux® 2.6.25 введено новое состояние процессов для перевода процессов в спящий режим под названием TASK_KILLABLE, которое предлагает альтернативу эффективному, но потенциально неубиваемому TASK_UNINTERRUPTIBLE и простому для пробуждения, но более безопасному TASK_INTERRUPTIBLE.

Это SO Q & A под названием: Что такое непрерывный процесс? и объясняет это.

Я обнаружил это в этой очень интересной книге под названием: Интерфейс программирования Linux: Руководство по системному программированию для Linux и UNIX .

Флоран
источник
Это не значит, что вы не можете произвести неубиваемый процесс. Это просто означает, что количество системных вызовов, которые невозможно отменить, уменьшается, когда эти вызовы переключаются на использование нового TASK_KILLABLEсостояния.
Мартейн Питерс