# Linux下实现一个系统调用
Date: 2019-06-02


# 环境：

环境： cenos7

kernel： 3.10.0-862.el7.x86\_64

gcc： gcc version 4.8.5 20150623 (Red Hat 4.8.5-36) (GCC)

新版内核： [linux-5.2-rc2](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/snapshot/linux-5.2-rc2.tar.gz)

# 准备：

- 安装相关软件：

```
yum install make automake vim perl openssl* elfutils-libelf- curl gcc wget flex git build-essential ncurses-devel xz-utils libssl-dev bc flex libelf-dev bison elfutils-libelf-devel -y
```

- 下载内核源码并解压：

tar -xf linux-5.2-rc2.tar.gz

# 编写系统调用：

- ###   新增系统调用表条目：
    

 cp syscall\_64.tbl syscall\_64.tbl.backup

 linux-5.2-rc2/arch/x86/entry/syscalls/syscall\_64.tbl 新增下面一条

 +  434        common     iadd\_test        \_\_x64\_sys\_iadd\_test

其中434为新增的调用号

- ### 增加函数声明
    

 linux-5.2-rc2/include/linux/syscalls.h：新增一行

 asmlinkage long sys\_iadd\_test(int one, int two);

- ### 新增系统调用定义
    

kernel/linux-5.2-rc2/fs/iadd\_test.c

```
#include <linux/printk.h>
#include <linux/syscalls.h>
#include "internal.h"

long do_iadd(const int one, const int two )
{
long sum = 0L;
sum = one + two;
return sum;
}

SYSCALL_DEFINE2(iadd_test, const int, one, const int, two)
{
printk("call iaddtest...");
return do_iadd(one, two);
}
```

- ### 修改Makfile
    

增加iadd\_test.o

```
 9 obj-y :=        open.o read_write.o file_table.o super.o \
10                 char_dev.o stat.o exec.o pipe.o namei.o fcntl.o \
11                 ioctl.o readdir.o select.o dcache.o inode.o \
12                 attr.o bad_inode.o file.o filesystems.o namespace.o \
13                 seq_file.o xattr.o libfs.o fs-writeback.o \
14                 pnode.o splice.o sync.o utimes.o d_path.o \
15                 stack.o fs_struct.o statfs.o fs_pin.o nsfs.o \
16                 fs_types.o fs_context.o fs_parser.o fsopen.o iadd_test.o
17
```

编译内核：

- ### 更新相关头文件
    

        /linux-5.2-rc2/arch/x86/include/generated/uapi/asm

```
cd /usr/include/asm
cp unistd_64.h  unistd_64.h.backup
cd linux-5.2-rc2/arch/x86/include/generated/uapi/asm 
cp /unistd_64.h /usr/include/asm/unistd_64.h
```

```
# 进入linux-5.2-rc2，将当前内核配置信息拷贝过来
cp /boot/config-3.10.0-862.el7.x86_64 .config
# 配置内核，这里没有修改
make menuconfig    
# 清除暂存文档，编译核心，模组
make -j 4 clean bzImage modules
# 安装模组
make modules_install
# 安装核心
make install
# 生成开机菜单
grub2-mkconfig -o /boot/grub2/grub.cfg
# 重启验证
reboot
# 查看内核版本是否更新
uname -r
```

#  验证：

  编写测试程序验证

```
      1 #define _GNU_SOURCE
      2 #include <stdio.h>
      3 #include <linux/kernel.h>
      4 #include <sys/syscall.h>
      5 #include <unistd.h>
      6
      7 long iadd_test(const int one, const int two)
      8 {
      9         return  syscall(__NR_iadd_test, one, two);
     10 }
     11 int main()
     12 {
     13
     14         printf("sum = %ld\n", iadd_test(1,1));
     15         printf("sum = %ld\n", iadd_test(100,100));
     16         printf("sum = %ld\n", iadd_test(1000,1000));
     17         return 0;
     18 }

```

\[root@centosgpt syscall\]# gcc test.c -o test \[root@centosgpt syscall\]# ./test sum = 2 sum = 200 sum = 2000

# 问题：

1. 内核编译很慢， 可以根据实际情况调整 make -j 4 clean bzImage modules，支持多个核心同时编译。
2. 验证程序开始编写时， 直接使用iadd\_test函数名， 这里编写的是系统调用， 不像用户态编写的库可以方便引用，需要通过glibc syscall 进行系统调用。 

[极客时间](https://time.geekbang.org/) 刘超《趣谈Linux操作系统》的课堂作业， 练习后整理了相关知识点。

另外，编译一次内核时间太长了。：（

# 参考：

[How to Compile & Install Linux Kernel 5.0 on CentOS7/RHEL7](https://medium.com/@tamirsuliman/how-to-compile-install-linux-kernel-5-0-on-centos7-rhel7-b97bd5dbfa2b)

[鸟哥私房菜 第24章](http://linux.vbird.org/linux_basic/0540kernel.php#make)

[Linux系统添加系统调用](https://blog.csdn.net/weixin_39924920/article/details/80413571)

[centos 7 增加系统调用](https://blog.csdn.net/qq_34977392/article/details/78756294)

[Adding a Hello World System Call to Linux Kernel](https://medium.com/anubhav-shrimal/adding-a-hello-world-system-call-to-linux-kernel-dad32875872)

