printk("Spying on UID:%d\n", uid);
/* Get the system call for getuid */
getuid_call = sys_call_table[__NR_getuid];
return 0;
}
/* Cleanup - unregister the appropriate file from /proc */
void cleanup_module() {
/* Return the system call back to normal */
if (sys_call_table[__NR_open] != our_sys_open) {
printk("Somebody else also played with the ");
printk("open system call\n");
printk("The system may be left in ");
printk("an unstable state.\n");
}
sys_call_table[__NR_open] = original_call;
}
Отложенные процессы
Что Вы делаете, когда кто-то просит Вас о чем-то, что Вы не можете сделать сразу же? Если вы человек, и вы обеспокоены, единственное, что Вы можете сказать: «Не сейчас, я занят.». Но если вы модуль, Вы имеете другую возможность. Вы можете поместить процесс в спячку, чтобы он бездействовал, пока Вы не сможете обслужить его. В конце концов, процессы помещаются в спячку и пробуждаются ядром постоянно.
Этот модуль является примером этого подхода. Файл (названный /proc/sleep) может быть открыт только одним процессом сразу. Если файл уже открыт, модуль называет module_interruptible_sleep_on[7]. Эта функция изменяет состояние задачи (задача является структурой данных в ядре, которая хранит информацию относительно процесса и системного вызова) в состояние TASK_INTERRUPTIBLE, что означает, что задача не будет выполняться пока не будет пробуждена так или иначе, и добавляет процесс к WaitQ, очереди задач ждущих, чтобы обратиться к файлу. Затем функция обращается к планировщику за контекстным переключателем другого процесса, который может использовать CPU, то есть управление передается другому процессу.
Когда процесс закончит работу с файлом, тот закрывается, и вызывается module_close. Эта функция пробуждает все процессы в очереди (нет никакого механизма, чтобы пробудить только одни из них). Управление возвращается и процесс, который только закрыл файл, может продолжать выполняться. Планировщик решает, что тот процесс поработал достаточно и передает управление другому процессу. В конечном счете, один из процессов, который был в очереди, получит управление. Он продолжит выполнение с той точки, в которой был вызван module_interruptible_sleep_on[8]. Он может установить глобальную переменную, чтобы сообщить всем другим процессам, что файл является все еще открытым. Когда другие процессы получат часть времени CPU, они увидят глобальную переменную и продолжат спячку.
Чтобы сделать нашу жизнь более интересной, module_close не имеет монополии на пробуждение процессов которые ждут, чтобы обратиться к файлу. Сигнал Ctrl-C (SIGINT) может также пробуждать процесс[9]. В таком случае, мы хотим возвратить -EINTR немедленно. Это важно, так как пользователи могут, например, уничтожить процесс прежде, чем он получит доступ к файлу.
Имеется еще одна хитрость. Некоторые процессы не хотят спать: они хотят или получать то, что они хотят, немедленно или сообщить, что действие не может быть выполнено. Такие процессы используют флажок O_NONBLOCK при открытии файла. Ядро отвечает, возвращая код ошибки -EAGAIN из операций, которые иначе блокировали бы, типа открытия файла в этом примере. Программа cat_noblock, доступная в исходном каталоге для этой главы, может использоваться, чтобы открыть файл с флагом O_NONBLOCK.
/* sleep.c - create a /proc file, and if several
* processes try to open it at the same time, put all
* but one to sleep */
/* Copyright (C) 1998-99 by Ori Pomerantz */
/* The necessary header files */
/* Standard in kernel modules */
#include <linux/kernel.h> /* We're doing kernel work */
#include <linux/module.h> /* Specifically, a module */
/* Deal with CONFIG_MODVERSIONS */
#if CONFIG_MODVERSIONS==1
#define MODVERSIONS
#include <linux/modversions.h>
#endif
/* Necessary because we use proc fs */
#include <linux/proc_fs.h>
/* For putting processes to sleep and waking them up */
#include <linux/sched.h>
#include <linux/wrapper.h>
/* In 2.2.3 /usr/include/linux/version.h includes a
* macro for this, but 2.0.35 doesn't - so I add it
* here if necessary. */
#ifndef KERNEL_VERSION
#define KERNEL_VERSION(a,b,c) ((a)*65536+(b)*256+(c))
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
#include <asm/uaccess.h> /* for get_user and put_user */
#endif
/* The module's file functions ********************** */
/* Here we keep the last message received, to prove
* that we can process our input */
#define MESSAGE_LENGTH 80
static char Message[MESSAGE_LENGTH];
/* Since we use the file operations struct, we can't use
* the special proc output provisions - we have to use
* a standard read function, which is this function */
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
static size_t module_output(
struct file *file, /* The file read */
char *buf, /* The buffer to put data to (in the user segment) */
size_t len, /* The length of the buffer */
loff_t *offset) /* Offset in the file - ignore */
#else
static int module_output(
struct inode *inode, /* The inode read */
struct file *file, /* The file read */
char *buf, /* The buffer to put data to (in the user segment) */
int len) /* The length of the buffer */
#endif
{
static int finished = 0;
int i;
char message[MESSAGE_LENGTH+30];
/* Return 0 to signify end of file - that we have
* nothing more to say at this point. */
if (finished) {
finished = 0;
return 0;
}
/* If you don't understand this by now, you're
* hopeless as a kernel programmer. */
sprintf(message, "Last input:%s\n", Message);
for(i=0; i<len && message[i]; i++) put_user(message[i], buf+i);
finished = 1;
return i; /* Return the number of bytes "read" */
}
/* This function receives input from the user when
* the user writes to the /proc file. */
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
static ssize_t module_input(
struct file *file, /* The file itself */
8
Это означает, что процесс — все еще в ядерном режиме — насколько процесс затронут, он выдал системный вызов open и системный вызов все же не возвратился. Процесс не знает, что кто-то еще использовал CPU в течение времени между моментом, выдачи обращения и возвратом
9
Это потому что мы использовали module_interruptible_sleep_on. Мы могли бы использовать module_sleep_on вместо него, но это закончится чрезвычайно сердитыми пользователями, чей Ctrl-C игнорируется.