系统调用初识

本文最后更新于:2020年10月28日 晚上

linux内核编译及添加系统调用

本文图片已经失效,同步更新到CSDN的文章仍是完整的,点击前往

注:文章共四部分,分别是

1、编译更换内核

2、添加一个简单系统系统调用

3、添加读取/修改nice值的系统调用

4、自己设计简单(真的简单)系统调用

四个部分结构相似,请根据自身需求自行选择观看。

Tip: 预安装的内核尽量选择与原系统内核版本相近的内核,可选择版本稍微高些的。内核版本差异过大将会出现很多意料之外的错误。如本实验环境中linux1604内核为4.12版本,可考虑使用4.16作为预安装版本

相关实验资源:
1、kernel内核源码文档
https://elixir.bootlin.com/linux/latest/source/include

2、Linux系统版本:Ubuntu16.04
ubuntu1604 清华源镜像

3、预安装内核linux-4.16.1
linux-4.16内核清华源镜像


(一)、下载新内核并编译、更换:

第一步:下载解压,进入文件夹

1
2
3
4
# wget https://cdn.kernel.org/pub/linux/kernel/v4.x/linux-4.16.1.tar.xz (或者手动下载压缩包解压) 
# xz –d linux-4.16.1.tar.xz
# tar –xvf linux-4.16.1.tar
# cd linux-4.16.1

第二步 :清楚残留的 .config和 .o文件

每次编译出错或者重新编译最好都清理,不清理很占空间,会出现虚拟机存储空间不够的情况

1
# make mrproper

报错提醒安装ncurses,重新执行make mrproper

1
# apt-get install libncurses5-dev   

第三步:配置内核

1
# make menuconfig 

根据报错提示安装组件,缺啥装啥

1
2
3
4
# sudo apt install build-essential //安装make和gcc等
# apt-get install libncurses5-dev //安装ncurses-devel
# sudo apt-get install flex //安装flex
# sudo apt-get install bison //安装bison

没有报错后再执行

1
# make menuconfig 

出现配置的对话框,直接保存(save),文件名也默认.config, 退出。

第四步:编译内核,生成启动映像文件

1
2
# make -j4 //-j4是指四线程,用于加快编译速度。
ps:嫌慢不如试试-j32,听说即便没有这么多线程也能正常运行的

报错提示要openssl,安装完再次执行命令即可

1
# apt-get install libssl-dev 

第五步:编译模块

这一步要好久(2-3小时,可能虚拟机配置太低吧)。。。。睡一觉回来就好了

1
# make modules

第六步:安装内核、模块

1
2
安装模块:# make modules_install 
安装内核:# make install

第七步:配置 grub 引导程序

只需要执行如下命令:该命令会自动修改 grub

1
# update-grub2

最后一步重启:

1
# reboot -n

查看内核版本

1
# uname -a

成功更换内核!

(二)、添加简单系统调用

第一步:修改源程序

1
2
# cd linux-4.16.1 //进入linux解压包(我下的版本是4.16.1)
# vim arch/x86/entry/syscalls/syscall_64.tbl //进入该文件分配系统调用号 (注意别写在最后面,x64部分约300多行,别写到后面x32那一块里面)

1
# vim include/linux/syscalls.h 进入该文件,添加服务例程的原型声明(shift+g快速跳到最后一行)

1
# vim kernel/sys.c 实现系统调用服务例程

SYSCALL_DEFINE后的数字代表参数个数,这里0个参数(void)

第二步:编译安装内核

1
2
3
4
5
6
# make menuconfig 配置内核
# make –j2 编译内核
# make modules 编译模块
# make modules_install 和 make install 安装模块和安装内核
# update-grub2(好像虚拟机不需要这一步)
# reboot –n 立即重启

第三步:新系统调用测试

这里编写一个test.c文件来测试(文件存放位置可以任意)

1
# vim test.c

编译

1
# gcc test.c -o test //-o test指定编译输出文件名为test

执行文件

1
# ./test

查看信息

1
# dmesg

可见系统调用成功执行

(三)、添加API对指定进程的 nice 值的读取功能

1
注:nice值表示进程可被执行的优先级的修正数值,加入nice值后,将会使得PRI变为:PRI(new)=PRI(old)+nice。这样,当nice值为负值的时候,那么该程序将会优先级值将变小,即其优先级会变高,则其越快被执行。

pid:进程ID

flag:等于1表示修改,等于0表示读取

nicevalue:为指定进程设置新的nice值

prionice:指向进程当前优先级prio及nice值

1.修改源程序

添加系统调用号

添加声明

具体代码实现

copy_to_user函数则是从内核空间拷贝内容到用户空间,用户空间的进程无法直接访问内核空间的内容。这个函数做了数据合法判断。然后进行拷贝。

1
static inline int task_nice(const struct task_struct *p)

用于获取当前task的nice值,并返回nice值,nice值的范围是[ -20 … 0 … 19 ]
其使用的例程如下:

1
2
3
4
5
6
7
8
9
10
void set_user_nice(struct task_struct *p, long nice)
{
bool queued, running;
int old_prio, delta;
struct rq_flags rf;
struct rq *rq;

if (task_nice(p) == nice || nice < MIN_NICE || nice > MAX_NICE)
return;
}

2.编译安装内核

编译安装

1
2
3
4
5
6
# make mrproper
# make -j4
# make modules
# make modules_install
# make install
# reboot -n

3.编写测试程序

1
# vim test-nice.c  //在哪创建没有特别要求

1
2
# gcc nice-test.c -o nice-test   //编译,格式gcc <c代码文件> -o <输出文件的文件名>
# ./nice-test //执行所生成的文件

1
# dmesg			//查看信息

(四)、自己设计系统调用

CONFIG_NR_CPUS是内核被配置支持的CPU个数,而实际设备的CPU个数是在系统启动过程当中去动态监测的。也就是说你配置系统支持32个CPU那么CONFIG_NR_CPUS就等于32,而num_online_cpus()则是当前设备激活可调度的CPU个数。

利用内核函数:

由于大致流程与前面相似,这里便不再详述

分配系统调用号(335)

添加服务例程原型声明

实现系统调用服务例程

编译安装

1
2
3
4
5
6
# make mrproper
# make -j4
# make modules
# make modules_install
# make install
# reboot -n

编写测试程序

编译执行后, dmesg查看信息,如图


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!