深入理解系统调用

作业要求:

  • 找一个系统调用,系统调用号为学号最后2位相同的系统调用
  • 通过汇编指令触发该系统调用
  • 通过gdb跟踪该系统调用的内核处理过程
  • 重点阅读分析系统调用入口的保存现场、恢复现场和系统调用返回,以及重点关注系统调用过程中内核堆栈状态的变化

一、选择系统调用

本人学号尾数为31,但是查找syscall_32.tbl表后发现31号系统调用为stty,进一步搜素在系统调用描述文件里面找到此系统调用和32号gtty都为sys_ni_syscall,进一步查资料发现上述两个系统调用已经被淘汰,所以它所对应的服务例程就要被指定为sys_ni_syscall
深入理解系统调用
深入理解系统调用

知识拓展:
即使31号和32号系统调用已经被淘汰了,但是我们并不能将它们的位置分配给其他的系统调用,因为一些老的代码可能还会使用到它们。否则,如果某个用户应用试图调用这些已经被淘汰的系统调用,所得到的结果,比如打开了一个文件,就会与预期完全不同,这将令人感到非常奇怪。其实,sys_ni_syscall中的"ni"即表示"not implemented(没有实现)


下面转而分析31号上面的系统调用,即30号utime

# The format is:
# <number> <abi> <name> <entry point> <compat entry point>
30	i386	utime			sys_utime32			__ia32_sys_utime32

utime作用为修改文件的访问时间和修改时间。其对应的32位entry pointsys_utime32,搜索sys_utime32utimes.c文件中找到了其实现,它是通过调用do_utimes来实现的。do_utimes的代码实现如下:

/*
 * do_utimes - change times on filename or file descriptor
 * @dfd: open file descriptor, -1 or AT_FDCWD
 * @filename: path name or NULL
 * @times: new times or NULL
 * @flags: zero or more flags (only AT_SYMLINK_NOFOLLOW for the moment)
 *
 * If filename is NULL and dfd refers to an open file, then operate on
 * the file.  Otherwise look up filename, possibly using dfd as a
 * starting point.
 *
 * If times==NULL, set access and modification to current time,
 * must be owner or have write permission.
 * Else, update from *times, must be owner or super user.
 */
long do_utimes(int dfd, const char __user *filename, struct timespec64 *times,
	       int flags)
{
	int error = -EINVAL;

	if (times && (!nsec_valid(times[0].tv_nsec) ||
		      !nsec_valid(times[1].tv_nsec))) {
		goto out;
	}

	if (flags & ~AT_SYMLINK_NOFOLLOW)
		goto out;

	if (filename == NULL && dfd != AT_FDCWD) {
		struct fd f;

		if (flags & AT_SYMLINK_NOFOLLOW)
			goto out;

		f = fdget(dfd);
		error = -EBADF;
		if (!f.file)
			goto out;

		error = utimes_common(&f.file->f_path, times);
		fdput(f);
	} else {
		struct path path;
		int lookup_flags = 0;

		if (!(flags & AT_SYMLINK_NOFOLLOW))
			lookup_flags |= LOOKUP_FOLLOW;
retry:
		error = user_path_at(dfd, filename, lookup_flags, &path);
		if (error)
			goto out;

		error = utimes_common(&path, times);
		path_put(&path);
		if (retry_estale(error, lookup_flags)) {
			lookup_flags |= LOOKUP_REVAL;
			goto retry;
		}
	}

out:
	return error;
}

深入理解系统调用

深入理解系统调用

二、 触发系统调用(直接触发+汇编触发)

使用下面的代码直接触发utime系统调用:

#include <sys/stat.h>
#include <utime.h>
#include <stdio.h>

int main(int argc, char *argv[])
{
    char *pathname;
    struct stat sb;
    struct utimbuf utb;

    if (argc != 2 || strcmp(argv[1], "--help") == 0){
        printf("%s file\n", argv[0]);
        return 1;
    }

    pathname = argv[1];

    //获取当前文件时间
    if (stat(pathname, &sb) == -1)
        return 1;

    //把最近修改时间改成访问时间
    utb.actime = sb.st_atime;
    utb.modtime = sb.st_atime;        /* Make modify time same as access time */
    // 调用utime
    if (utime(pathname, &utb) == -1)  /* Update file times */
        return 1;

    return 0;
}

深入理解系统调用

对上述的程序进行修改,使用汇编来调用utime,其实就是使用汇编指令传递utime的参数,并使用系统调用通过软中断0x80陷入内核,跳转到系统调用处理程序system_call(sys_utime32)函数,并执行相应的服务例程,但由于是代表用户进程,所以这个执行过程并不属于中断上下文,而是处于进程上下文:

#include <sys/stat.h>
#include <utime.h>
#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[])
{
    char *pathname;
    struct stat sb;
    struct utimbuf utb;

    if (argc != 2 || strcmp(argv[1], "--help") == 0){
        printf("%s file\n", argv[0]);
        return 1;
    }

    pathname = argv[1];

    //获取当前文件时间
    if (stat(pathname, &sb) == -1)
        return 1;

    //把最近修改时间改成访问时间
    utb.actime = sb.st_atime;
    utb.modtime = sb.st_atime;        /* Make modify time same as access time */
    int flag;

    asm volatile(
    "movl %1, %%ebx\n\t"  // 将pathname放入ebx
    "movl %2, %%ecx\n\t"  // 将utimbuf 的引用放入ecx
    "movl $30, %%eax\n\t" //通过EAX寄存器返回系统调用值
    "int $0x80\n\t"       // 通过软中断0x80陷入内核
    "movl %%eax, %0\n\t"  // 将输出通过eax赋值给flag
    :"=m"(flag)
    :"b"(pathname),"c"(&utb)
    );

    if (flag == -1)  /* Update file times */
        return 1;

    return 0;
}

深入理解系统调用

三、 通过gdb跟踪该系统调用的内核处理过程

四、 分析总结

参考文章:

上一篇:结合中断上下文切换和进程上下文切换分析Linux内核的一般执行过程


下一篇:【算法题】矩阵中的路径