LINUX.ORG.RU

А вот опять я, со своей маленькой прогой...


0

0

Есть текст програмки, в предыдущем посте. При insmod test.c ps axuf ничего не выдаёт (то есть я не вижу такого [test]). НО... load average с этим модулем сразу даёт еденицу, по top (в нём нажмём клвишу i) он показывется как работающий процес... Вот мне интересно это что глючит, ядро, статичтика или ещё что??? Или всё таки этот процесс постоянно в очереди в планировщике. Что я сделал дальше. Вот как исправил программу:

#define __KERNEL__
#define MODULE
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/interrupt.h>

int init_module(void)
{
daemonize ();
reparent_to_init ();
strcpy (current->comm, "testd");
current->state = TASK_UNINTERRUPTIBLE;
schedule_timeout(10 * HZ);
return -EIO;
}

И вот я получил, то что мне требовалось, процесс в состоянии DW. Почему, что такое, что за глюк???? При ps axuf этот процес мы видим.
Вот Makefile чтоб скомпилировалось:

all: test.o

test.o: test.c
gcc -c test.c -I/lib/modules/`uname -r`/build/include

Понятно что перед строчкой gcc -с ... должна быть табуляция.

З.Ы. Сам я не хрена языка не знаю... Просто пытаюсь разобраться с одним сложным модулем, хочу понять что с ним происходит, из-за него LA всегда больше еденицы и он всегда считается работающим. Сорри за сумбурность..

anonymous

> При insmod test.c ps axuf ничего не выдаёт
> (то есть я не вижу такого [test]).

зато он должен выдать insmod, это и есть наш процесс

> НО... load average с этим модулем сразу даёт еденицу

потому что proc думает, что процесс IO-bound, т.к.
в TASK_UNINTERRUPTIBLE процесс может быть очень недолго,
если нет ошибки в ядре, а мы такую "ошибку" создали.

> Вот как исправил программу:
> ...
> daemonize()

этот daemonize() сделал exit_mm(), теперь у процесса
mm == 0, отсюда "W" у ps.

> reparent_to_init ()

в данном конкретном случае лишнее

> strcpy (current->comm, "testd");

и теперь он виден не как insmod, а как testd.

idle ★★★★★
()
Ответ на: комментарий от idle

Почему же мы создали ошибку в ядре, ну вот у меня наприер ждёт тред, какого-то сигнла, чтоб выполнить какую-то работу, ждёт долго вплоть до нескольких суток, то есть написно типа такого :
while (!exit_flg) {
set_task_state (current, TASK_UNINTERRUPTIBLE);
schedule ();
if (!exit_fg) {
<Здесь какие то нужные действия>
}
}

Почему так делать нельзя????

anonymous
()
Ответ на: комментарий от anonymous

> Почему же мы создали ошибку в ядре

Ну я ведь слово "ошибка" в кавычках писал.
никто не может вам запретить спать в UNINTERRUPTIBLE
сколько угодно, хоть это и не очень полезно.
но этот процесс будет учитываться в loadavg в 2.4,
и в этом есть определенный смысл.

Хотя в приведенном коде действительно есть ошибка:
daemonize() в контексте insmod, я потому и упоминал
kernel_thread() в первом ответе.

> ну вот у меня наприер ждёт тред, какого-то сигнла
> ...
> set_task_state (current, TASK_UNINTERRUPTIBLE);
> schedule ();

Это никак не может работать, попрощайтесь с current,
schedule() не вернет управление никогда.
разве что процесс уже сидит в какой-то очереди, и кто-то
выставляет exit_flg и делает wake_up(). но даже и в этом
случае код все равно неправильный: событие может произойти
между while (!exit_flg) и schedule().

см wait_event_uninterruptible().

anonymous
()
Ответ на: комментарий от anonymous

Это работает... Смысл такой.. Загружается модуль, после того как загрузили его, модулем создаётся поток, который зпускает на определённое событие внешнюю программу.. Это сделано для того, чтоб из самого модуля не вызывать call_usermodehelper, вызывает call_usermodehelper поток этого модуля.

Вопросы:

1. В 2.6.x такой проблемы нет c LA???

2. Я правильно понимаю что в моём последнем примере поток (конечно коряво обьясняю) как бы находится в функции schedule () и ждёт пока выполнится wake_up?? То есть сам цикл не выполняется постоянно (а как бы заторможен до wake_up) и следовательно такты процессора никак не используются этим циклом во время простоя, и не стоит в очереди на обработку, не зависимо от того что LA показывает...

anonymous
()
Ответ на: комментарий от anonymous

> То есть сам цикл не выполняется постоянно
> такты процессора никак не используются этим циклом
> независимо от того что LA показывает

Да.

> В 2.6.x такой проблемы нет c LA???

Какой проблемы??? нет никакой проблемы. proc/loadavg
очень неформальная метрика. если там большие числа,
это намек администратору, что его машина перегружена.
поэтому там считаются RUNNING и UNINTERRUPTIBLE
процессы, хотя последние и не используют CPU и потому
не учитываются как nr_running (4-е поле).

В 2.6 просто больше информации в proc/stat, т.к.
явно учитываются процессы ожидающие завершения I/O,
см io_schedule().

еще раз. если процесс в D state, это значит, что он ожидает
события, которое должно обязательно "скоро" случиться, и что
отменить это событие невозможно (или трудно реализовать).

технически это значит, что планировщик этот процесс в упор
не видит, и сигналы его не будят (но запоминаются).
получит управление он только тогда, когда его кто-то
явно разбудит.

поэтому если процесс долго в D, это говорит о проблемах.
вам имеет смысл уходить в schedule() с TASK_INTERRUPTIBLE,
и проверять signal_pending() после возврата.

> Это сделано для того, чтоб из самого модуля не вызывать
> call_usermodehelper, вызывает call_usermodehelper поток
> этого модуля.

что значит, из самого модуля??? нет такого контекста.
если поток только для call_usermodehelper(), от него
можно избавиться. call_usermodehelper() все равно будет
работать в контексте keventd, а для запуска программы
будет создан отдельный поток.

idle ★★★★★
()
Ответ на: комментарий от idle

Вот текст самого модуля, мне его Murr писал на скорую руку, поэтому ошибки могут быть. Смысл в нём такой, когда квота становится нулём, мне необходимо запустить внешнюю программу.

/*
* netfilter module to enforce network quotas
*
* Sam Johnston <samj@samj.net>
*/

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kmod.h>
#include <linux/sched.h>
#include <linux/skbuff.h>
#include <linux/spinlock.h>
#include <linux/interrupt.h>

#include <linux/netfilter_ipv4/ip_tables.h>
#include <linux/netfilter_ipv4/ipt_quota.h>

static struct task_struct * my_thread_tsk = 0;
static int my_thread_exit_flag = 0;

static spinlock_t quota_lock = SPIN_LOCK_UNLOCKED;

static int
match(const struct sk_buff *skb,
const struct net_device *in,
const struct net_device *out,
const void *matchinfo,
int offset, const void *hdr, u_int16_t datalen, int *hotdrop)
{
int totlen;
struct ipt_quota_info *q = (struct ipt_quota_info *) matchinfo;

spin_lock_bh(&quota_lock);

totlen = ntohs(skb->nh.iph->tot_len);

if (q->quota > totlen) {
/* we can afford this one */
q->quota -= totlen;
spin_unlock_bh(&quota_lock);

#ifdef DEBUG_IPT_QUOTA
printk("IPT Quota OK: %llu datlen %d \n", q->quota, datalen);
#endif
return 1;
}
if (q->quota > 0) {
if (my_thread_tsk) // AP: yes, it cannot be zero, but still we are wary ...
wake_up_process (my_thread_tsk);
}

/* so we do not allow even small packets from now on */
q->quota = 0;

#ifdef DEBUG_IPT_QUOTA
printk("IPT Quota Failed: %llu datlen %d \n", q->quota, datalen);
#endif

spin_unlock_bh(&quota_lock);
return 0;
}

static int
checkentry(const char *tablename,
const struct ipt_ip *ip,
void *matchinfo, unsigned int matchsize, unsigned int hook_mask)
{
/* TODO: spinlocks? sanity checks? */
return (matchsize == IPT_ALIGN(sizeof (struct ipt_quota_info)));
}

static struct ipt_match quota_match
= { {NULL, NULL}, "quota", &match, &checkentry, NULL, THIS_MODULE };

static int my_thread (void * unused) {

daemonize ();
reparent_to_init ();
strcpy (current->comm, "quotad"); // AP: change the name to whatever you want your ps to show ;)

while (!my_thread_exit_flag) {
set_task_state (current, TASK_UNINTERRUPTIBLE); // AP: TASK_UNINTERRUPTIBLE for some stupid not to wake us with a signal
schedule ();
/* AP: I assume here we have a small noncritical race with both signal from match() and my_thread_exit_flag
force exit in that case */
if (!my_thread_exit_flag) {
char my_path[256] = "/usr/local/forvpn/vpn_off";
char * envp[] = {"HOME=/", "TERM=linux", "PATH=/sbin:/usr/sbin:/bin:/usr/bin", 0};
char *argv[] = {my_path, 0};

call_usermodehelper(my_path, argv, envp);
}
}
my_thread_exit_flag = 0;
return 0;
}

static inline void my_thread_done (void) {
my_thread_exit_flag = 1;
wake_up_process (my_thread_tsk);
while (my_thread_exit_flag) // AP: Polling is the best case here, i suppose
schedule ();
}

static int __init init(void) {
int rc;
pid_t m;

m = kernel_thread (my_thread, 0, SIGCHLD);
if (m<0)
return -EAGAIN;

my_thread_tsk = find_task_by_pid (m);
if (my_thread_tsk == 0)
return -EAGAIN;

rc = ipt_register_match(&quota_match);

if (rc<0) {
my_thread_done ();
return rc;
}

return 0;
}

static void __exit fini(void) {
ipt_unregister_match(&quota_match);
my_thread_done ();
}

module_init(init);
module_exit(fini);

MODULE_LICENSE("GPL");

anonymous
()
Ответ на: комментарий от anonymous

> Вот текст самого модуля

ну посмотрел бегло...

ну ошибка в my_thread(), wake_up() может произойти
перед schedule(), и событие будет пропущено, я уже
писал вам об этом, когда вы приводили псевдокод.

в my_thread_done() тоже ошибка, хотя вероятность ее
проявления мала, возможен deadlock. кроме того, крах может
быть при CONFIG_PREEMPT, если переключение контекста
произойдет в my_thread() сразу после my_thread_exit_flag=0;
но, повторю, вероятность очень мала.

на мой взгляд проще было бы без этого потока. прямо в функции
match(): в 2.6 можно просто использовать call_usermodehelper()
с параметром wait = 0. в 2.4 можно вызвать schedule_task(),
tq_struct->routine пусть зовет call_usermodehelper().

но если работает и вас устраивает чего менять.

все, утомила меня эта тема :)))

idle ★★★★★
()
Ответ на: комментарий от idle

Ладно, всё понял... Спасибо... Понял что вам надоело.. Ок.

Ну на всякий случай ещё вопросы, можете не отвечать, серьёзно:

1. Если промежуток между двумя событиями не может быть менее 1 минуты, событие же не потеряется??? Просто если эти события возникают чаще чем раз в 1 минуту, то мне даже лучше, чтоб они пропускались. :-))

2. Если я для 2.6 уберу тред и строки где происходит wake_up_process

if (my_thread_tsk)
wake_up_process (my_thread_tsk);

заменю на

call_usermodehelper(my_path, argv, envp, 0);

То по идеи у меня в 2.6 не должно быть kernel panic, как было в 2.4 когда я сам, не зная Си пытался переделать модуль под свои нужды и делал именно это только без последнего параметра???

3. То что нужно сделать для 2.4 я не понял, ну оно мне наверное не понадобится, так как надеюсь в скором времени перейти на ядро 2.6

А вообще, большое спасибо. Удачи вам.

anonymous
()
Ответ на: комментарий от anonymous

Где я писал про одну минуту, это я имел ввиду, что если события происходят слишком часто, то хорошо что они теряются. А если не теряются, то это тоже не плохо. Главное чтоб не терялись события которые проимходят реже чем раз в минуту, вот что я имел в виду.

anonymous
()
Ответ на: комментарий от anonymous

Я так понимаю что CONFIG_PREEMPT это как раз в ядре 2.6??

anonymous
()
Ответ на: комментарий от anonymous

> Если я для 2.6 уберу тред и строки где происходит wake_up_process
> заменю на call_usermodehelper(my_path, argv, envp, 0);
> То по идеи у меня в 2.6 не должно быть kernel panic, как было в 2.4

боюсь, соврал я :))) посмотрел еще раз call_usermodehelper(),
там безусловный wait_for_completion(), wait другое означает.

ох... сейчас напишу подробнее как надо

idle ★★★★★
()
Ответ на: комментарий от idle

итак...

static void call(void *unused)
{
.............
call_usermodehelper(my_path, argv, envp, 0);
}

static DECLARE_WORK(work, call, 0); // для 2.6
static struct tq_struct work = { routine: call }; // для 2.4

теперь в match():

if (q->quota > 0)
schedule_work(&work); // 2.6
schedule_task(&work); // 2.4

в fini(), после ipt_unregister_match():

flush_scheduled_work(); // 2.6
flush_scheduled_tasks(); // 2.4


у меня 2.2 стоит, писал, глядя на исходники, так что
тоже, наверное, где-то ошибся :)

если события будут приходить быстрее, чем мы успеваем
запустить нашу программу, они будут теряться. впрочем,
та же проблема в коде с kernel_thread(). atomic_t cnt
решит проблему.

Все таки, любопытно, чем вас не устраивает имеющийся
вариант, он же работает, я полагаю?

Как в этом форуме отступы сохранить ?????????????????

idle ★★★★★
()
Ответ на: комментарий от idle

В принцепе сейчас устраивает.. До этого я думал раз LA 1, то следовательно процесс постоянно что то делает, хоть и показывает загрузку проца 0. Вот и хотелось чтоб он ничего не делал... Потом собирался переходить на ядро 2.6.х и подумал что этот вриант который сейчас есть, может не заработать на этом ядре. А вообше, всегда хочется чтобы было лучше чем уже есть.. :-)))

Большое вам СПАСИБО.

З.Ы. Отступы как делать, не знаю.

Спасибо. Буду реализовывть ваш вариант.

anonymous
()
Ответ на: комментарий от idle

На ядре 2.4.22 всё прекрассно заработало, правда при компиляции компилер на call_usermodehelper поругался, что лишний параметр, я нолик убрал и всё хорошо скомпилилось, потестировал, всё в порядке... Осталось поставить ядро 2.6.x и на нём тестить, второй вариант...

Ещё раз СПАСИБО!!!

anonymous
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.