内核:使用信号队列函数

Kernel: using sigqueue-functions

我尝试实现从可加载内核模块到用户应用程序的信号调用。该应用程序使用 sigaction 来处理信号调用,稍后它将使用 sival_int 来处理 switchcase.

的不同操作
void signalHandler_function (int signum,
                             siginfo_t *siginfo,
                             void *ucontext)
{
   printf("signum: %i\n", signum);
   printf("sigval: %d\n", siginfo->si_value.sival_int);
}

int main(){
   int ret;
   int pid = getpid();
   char pidc[4];
   struct sigaction sig;

   memcpy(pidc,&pid,4);

   // configure signal handler
   sig.sa_flags = SA_SIGINFO;
   sig.sa_sigaction = signalHandler_function;
   sigaction(SIGIO, &sig, NULL);

   g_fdCharDev = open(EXPDEV_DEVPATHNAME, O_RDWR);

   printf("My process ID : %d\n", pid);
   ret = write(g_fdCharDev, &pid, 1);
   ...
}

在内核端我尝试使用send_sigqueue()(连同sigqueue_alloc()sigqueue_free())。这是在 include/linux/sched.h, line 2320 as extern and defined in kernel/signal.c, line 1560 中声明的。但是链接器说这个函数是未定义的:

make -C /lib/modules/3.19.0-58-generic/build/ M=/home/alex/git/Kernel3/SignalHandling modules
make[1]: Verzeichnis »/usr/src/linux-headers-3.19.0-58-generic« wird betreten
  CC [M]  /home/alex/git/Kernel3/SignalHandling/ExpDev.o
  Building modules, stage 2.
  MODPOST 1 modules
WARNING: "sigqueue_free" [/home/alex/git/Kernel3/SignalHandling/ExpDev.ko] undefined!
WARNING: "sigqueue_alloc" [/home/alex/git/Kernel3/SignalHandling/ExpDev.ko] undefined!
WARNING: "send_sigqueue" [/home/alex/git/Kernel3/SignalHandling/ExpDev.ko] undefined!
  CC      /home/alex/git/Kernel3/SignalHandling/ExpDev.mod.o
  LD [M]  /home/alex/git/Kernel3/SignalHandling/ExpDev.ko
make[1]: Verzeichnis »/usr/src/linux-headers-3.19.0-58-generic« wird verlassen
Process terminated with status 0 (0 minute(s), 0 second(s))
0 error(s), 0 warning(s) (0 minute(s), 0 second(s))

这是 LKM 的缩短源:

#include <linux/init.h>   /// Macros used to mark up functions e.g. __init __exit
#include <linux/module.h> /// Core header for loading LKMs into the kernel
#include <linux/device.h> /// Header to support the kernel Driver Model
#include <linux/kernel.h> /// Contains types, macros, functions for the kernel
#include <linux/fs.h>     /// Header for the Linux file system support
#include <asm/uaccess.h>  /// Required for the copy to user function
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/rcupdate.h>
#include <linux/slab.h>

struct siginfo *g_sig_info;
static struct sigqueue *g_sigqueue;
int g_usp_pid;
struct task_struct *g_sig_task;

static int __init expdev_init(void)
{
  ...
  // setup signal
  g_sigqueue = sigqueue_alloc();
  g_sigqueue->info.si_signo = SIGIO;
  g_sigqueue->info.si_signo = SI_QUEUE;
  g_sigqueue->info.si_errno = 0;
  ...
}

static void __exit expdev_exit(void)
{
  sigqueue_free(g_sigqueue);
  ...
}

static ssize_t dev_write(struct file *filep,
                         const char *buffer,
                         size_t len,
                         loff_t *offset)
{
  ...
  memcpy(&g_usp_pid,buffer,4); // we know the PID comes with the buffer

  // find task to the given PID
  rcu_read_lock();
  g_sig_task = pid_task(find_pid_ns(g_usp_pid, &init_pid_ns),
                        PIDTYPE_PID);

  //send signal to user land
  g_sigqueue->info.si_value.sival_int = 33;
  ret = send_sigqueue(g_sigqueue, g_sig_task, 0);
  ...
}

为什么它不可链接?我的方法错了吗?

任何有用的 list_head 或为内核模块导出的函数都可以在 System.map 文件中找到地址。

$ sudo grep sigqueue /boot/System.map-3.19.0-58-generic
ffffffff81084880 T sigqueue_alloc
ffffffff810848b0 T sigqueue_free
ffffffff81084920 T send_sigqueue

这个函数指针地址可以用于局部函数:

#include <linux/init.h>   /// Macros used to mark up functions e.g. __init __exit
#include <linux/module.h> /// Core header for loading LKMs into the kernel
#include <linux/device.h> /// Header to support the kernel Driver Model
#include <linux/kernel.h> /// Contains types, macros, functions for the kernel
#include <linux/fs.h>     /// Header for the Linux file system support
#include <asm/uaccess.h>  /// Required for the copy to user function
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/rcupdate.h>
#include <linux/slab.h>

struct siginfo *g_sig_info;
static struct sigqueue *g_sigqueue;
int g_usp_pid;
struct task_struct *g_sig_task;

// ffffffff81084920 T send_sigqueue
int (*send_sigqueue_ptr)( struct sigqueue *,
                          struct task_struct *,
                          int group) = (void*)0xffffffff81084920;
// ffffffff81084880 T sigqueue_alloc
struct sigqueue *(*sigqueue_alloc_ptr)(void) = (void*)0xffffffff81084880;
// ffffffff810848b0 T sigqueue_free
void (*sigqueue_free_ptr)(struct sigqueue *) = (void*)0xffffffff810848b0;

static int __init expdev_init(void)
{
  ...
  // setup signal
  g_sigqueue = sigqueue_alloc_ptr();
  g_sigqueue->info.si_signo = SIGIO;
  g_sigqueue->info.si_code  = SI_QUEUE;
  g_sigqueue->info.si_errno = 0;
  ...
}

static void __exit expdev_exit(void)
{
  sigqueue_free_ptr(g_sigqueue);
  ...
}

static ssize_t dev_write(struct file *filep,
                         const char *buffer,
                         size_t len,
                         loff_t *offset)
{
  ...
  memcpy(&g_usp_pid,buffer,4); // we know the PID comes with the buffer

  // find task to the given PID
  rcu_read_lock();
  g_sig_task = pid_task(find_pid_ns(g_usp_pid, &init_pid_ns),
                        PIDTYPE_PID);

  //send signal to user land
  g_sigqueue->info.si_value.sival_int = 33;
  ret = send_sigqueue_ptr(g_sigqueue, g_sig_task, 0);
  ...
}

这仅适用于这个特殊的内核版本,并且不是很便携。是否有任何宏或其他解决方法可以获取此地址或函数?这也是编译 kernel/signal.c 的好方法吗?

如果有人知道改进它的方法。请随时发表评论和编辑。