内核模块与进程通信实验

前言

每一次OS作业(除了第一次放水的作业外),都是内心的淬炼…



添加内核模块

查询syscall_table的地址:

sudo cat /proc/kallsyms | grep sys_call_table

在这里插入图片描述
这里的 ffffffff8ae002e0 要记住(不同环境地址不一样),待会儿写代码要用。


先找一找可用的系统调用号,然后在cat一下看看。

sudo find / -name unistd_64.h



cat /usr/src/linux-headers-5.4.0-53-generic/arch/x86/include/generated/uapi/asm/unistd_64.h

在这里插入图片描述

这里我们可以看到436以后(包括436)的调用号系统没用上,不过当时做的时候没留意,选了个384。结果没啥问题,可能384不是关键的系统调用号吧。。懒得改了。(不过不建议读者这么搞



然后hello.c文件:

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/unistd.h>
#include <linux/sched.h>

MODULE_LICENSE("Dual BSD/GPL");

#define SYS_CALL_TABLE_ADDRESS 0xffffffff8ae002e0 //sys_call_table对应的地址
#define NUM 384  //系统调用号为384
int orig_cr0;  //用来存储cr0寄存器原来的值
unsigned long *sys_call_table_my=0;
static int(*anything_saved)(void);  //定义一个函数指针,用来保存一个系统调用
static int clear_cr0(void) //使cr0寄存器的第17位设置为0(内核空间可写)
{
    
    
      unsigned int cr0=0;
      unsigned int ret;
      asm volatile("movq %%cr0,%%rax":"=a"(cr0));//将cr0寄存器的值移动到eax寄存器中,同时输出到cr0变量中
      ret=cr0;
      cr0&=0xfffffffffffeffff;//将cr0变量值中的第17位清0,将修改后的值写入cr0寄存器
      asm volatile("movq %%rax,%%cr0"::"a"(cr0));//将cr0变量的值作为输入,输入到寄存器eax中>,同时移动到寄存器cr0中
      return ret;
}

static void setback_cr0(int val) //使cr0寄存器设置为内核不可写
{
    
    
      asm volatile("movq %%rax,%%cr0"::"a"(val));
}

static int sys_mycall(void){
    
    
        printk("*********** 2018*******");
        printk("Andy Dennis");
        return 0;
}

static int __init call_init(void)
{
    
    
        sys_call_table_my=(unsigned long*)(SYS_CALL_TABLE_ADDRESS);
        printk("call_init......\n");
        anything_saved=(int(*)(void))(sys_call_table_my[NUM]);//保存系统调用表中的NUM位置上的系统调用
        orig_cr0=clear_cr0();//使内核地址空间可写
        sys_call_table_my[NUM]=(unsigned long) &sys_mycall;//用自己的系统调用替换NUM位置上的系统调用
        setback_cr0(orig_cr0);//使内核地址空间不可写
        return 0;
}

static void __exit call_exit(void)
{
    
    
        printk("call_exit......\n");
        orig_cr0=clear_cr0();
        sys_call_table_my[NUM]=(unsigned long)anything_saved;//将系统调用恢复
        setback_cr0(orig_cr0);
}

module_init(call_init);
module_exit(call_exit);

(注:下面路径和上面相比有点变化,是因为我把文件挪到 1 的文件下了)

sudo vim Makefile
ifneq ($(KERNELRELEASE),)
 #kbuild syntax. dependency relationshsip of files and target modules are listed here.
  mymodule-objs := hello.o
   obj-m := hello.o
   else
        PWD  := $(shell pwd)
        KVER ?= $(shell uname -r)
KDIR := /lib/modules/$(KVER)/build
all:
         $(MAKE) -C $(KDIR) M=$(PWD)
clean:
        rm -rf .*.cmd *.o *.mod.c *.ko .tmp_versions
test:
        gcc test.c -o test.o
        ./test.o
remod: 
        rmmod hello.ko
        insmod hello.ko
        lsmod | grep hello
endif

然后

sudo make


查看一下该目录下有啥:

ls



发现太多文件了,这里我们过滤一下:

ls | grep 'hello.*'

可以看到此时已经编译成功了。

插入模块:

sudo insmod hello.ko

查看是否插入成功

lsmod | grep hello

在这里插入图片描述


如果想卸载内核模块: (我说的是如果,不是必须的)

rmmod  hello.ko




接着我们来写一下测试文件:

#include<stdio.h>
#include<stdlib.h>
#include<linux/kernel.h>
#include<sys/syscall.h>
#include<unistd.h>

int main()
{
    
    
      unsigned long x = 0;
      x = syscall(384);        //测试384号系统调用
      printf("Andy Dennis- syscall result: %ld\n", x);
      return 0;
}

由于在Makefile环境我已经写了脚本,所以我这里直接

sudo make test

也可以下面两句,效果一样: (这里只不过是想说还可以将一些脚本写到Makefile中提高效率)

gcc test.c -o test.o
./test.o

在这里插入图片描述

查看一下内核输出信息:

dmesg

在这里插入图片描述





四种单机 IPC 方式

1.信号-signal

sudo vim message.c

然后写好下面的代码:

#include<stdio.h>
#include<stdlib.h>
#include<signal.h>

int k1;
void int_fun1(){
    
    
        k1=0;
}

main(){
    
    
        int k, p1;
        while((p1=fork())==-1);
        if(p1>0){
    
     // 子进程
                for(k=1;k<4;k++){
    
    
                        printf("How are you!\n");
                        sleep(1);
                }
                kill(p1, 12);
                wait(0);
                printf("OK!\n");
                exit(0);
        } else {
    
      // 父进程
                signal(12, int_fun1); // 只是注册,等待子进程调用kill
                k1=1;
                while(k1==1){
    
    
                        printf("I am child\n");
                        sleep(1);
                }
                printf("Child exited!\n");
                exit(0);
        }
}

接着编译一下

sudo gcc message.c -o message.exe

可以看到有警告,但是不管了
在这里插入图片描述
反正是生成了message.exe.
在这里插入图片描述
运行message.exe

在这里插入图片描述


2.管程

sudo vim pipe.c

pipe.c文件中

#include<stdlib.h>
#include<string.h>

main(){
    
    
        int p1, fd[2];
        char outpipe[50];
        char inpipe[50];
        pipe(fd);
        while((p1=fork())==-1);
        if(p1==0){
    
    
                strcpy(inpipe, "This is a message!");
                write(fd[1], inpipe, 50);
                exit(0);
        } else {
    
    
                wait(0);
                read(fd[0], outpipe, 50);
                printf("%s\n", outpipe);
                exit(0);
        }
}

然后编译一下

sudo gcc -c pipe.c -o pipe.exe

运行

./pipe.exe

在这里插入图片描述


3.消息队列

sudo vim sndfile.c

sndfile.c文件中:

#include<string.h>
#include<unistd.h>
#include<sys/types.h>
#include<linux/msg.h>
#define MAXMSG 512
struct my_msg{
    
    
        long int my_msg_type;
        int i;
        char some_text[MAXMSG];
}msg;

main(){
    
    
        int msgid;
        char buffer[BUFSIZ];
        // 创建消息队列
        msgid = msgget(12, 0666|IPC_CREAT); 

        while(1){
    
    
                puts("Enter some text:");
                fgets(buffer, BUFSIZ, stdin);
                msg.i++;
                printf("i=%d\n", msg.i);
                msg.my_msg_type=3;
                strcpy(msg.some_text, buffer);
                // 发送消息到消息队列
                msgsnd(msgid, &msg, MAXMSG, 0);
                if(strncmp(msg.some_text, "end", 3)==0)
                        break;
        }
        exit(0);
}

编译一下

sudo gcc sndfile.c -o sndfile.exe

再写个rcvfile.c

sudo vim rcvfile.c

rcvfile.c中:

#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/types.h>
#include<linux/msg.h>

#define MAXMSG 512
struct my_msg{
    
    
        long int my_msg_type;
        int i;
        char some_text[MAXMSG];
}msg;

main(){
    
    
        int msgid;
        msg.my_msg_type=3;

        msgid=msgget(12, 0666|IPC_CREAT); // 获取消息队列
        while(1){
    
    
        		// 接受消息
                msgrcv(msgid, &msg, BUFSIZ, msg.my_msg_type, 0);
                printf("You wrote:%s and i=%d\n",msg.some_text, msg.i);
                if(strncmp(msg.some_text, "end", 3)==0)
                        break;
        }
        // 删除消息队列
        msgctl(msgid, IPC_RMID, 0);
        exit(0);
}
sudo gcc rcvfile.c -o rcvfile.exe

先运行一下sndfile.exe文件:

./sndfile.exe

在这里插入图片描述
在这里插入图片描述



4.共享内存

sudo vim sndshm.c

这里不得不说一下一个大坑,就是这里的shm.h的头文件,新版的应该是<sys/shm.h>, 旧版的是<linux/shm.h>。

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/shm.h>

main(){
    
    
        int shmid;
        char *viraddr;
        char buffer[BUFSIZ];
		
		// 创建共享内存
        shmid=shmget(1234, BUFSIZ, 0666|IPC_CREAT);
        // 附接共享内存
        viraddr=(char*)shmat(shmid, 0, 0);

        while(1){
    
    
                puts("Enter some text:");
                fgets(buffer, BUFSIZ, stdin);
                // 采用追加的方式写到共享内存
                strcat(viraddr, buffer);
                if(strncmp(buffer, "end", 3)==0)
                        break;
        }
        // 切断与共享内存的链接
        shmdt(viraddr);
        exit(0);
}
sudo vim rcvshm.c
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/shm.h>

main(){
    
    
        int shmid;
        char *viraddr;
		
		// 创建共享内存
        shmid=shmget(1234, BUFSIZ, 0666|IPC_CREAT);
        // 附接到共享内存
        viraddr=(char*)shmat(shmid, 0, 0);
        printf("Your message is: %s", viraddr);

		// 切断与共享内存的链接
        shmdt(viraddr);
        // 释放共享内存
        shmctl(shmid, IPC_RMID, 0);
        exit(0);
}

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_43850253/article/details/109603014