Linux 系统调用

用户空间的程序无法直接执行内核代码。它们不能直接调用内核空间中的函数,因为内核驻留在受保护的地址空间上。如果进程可以直接在内核的地址空间上读写的话,系统安全就会失去控制。所以,应用程序应该以某种方式通知系统,告诉内核自己需要执行一个系统调用,希望系统切换到内核态,这样内核就可以代表应用程序来执行该系统调用了。

通知内核的机制是靠软件中断实现的。首先,用户程序为系统调用设置参数。其中一个参数是系统调用编号。参数设置完成后,程序执行“系统调用”指令。x86系统上的软中断由int产生。这个指令会导致一个异常:产生一个事件,这个事件会致使处理器切换到内核态并跳转到一个新的地址,并开始执行那里的异常处理程序。此时的异常处理程序实际上就是系统调用处理程序。它与硬件体系结构紧密相关。

下图是系统调用过程示意图 :

下面我们以open为例,来看看kernel的调用过程的。

系统调用号与系统调用程序的对应关系定义在include/uapi/asm-generic/unistd.h

#define __NR_open 1024
__SYSCALL(__NR_open, sys_open)

include/linux/syscall.h重定义了系统调用相关的宏

#define SYSCALL_DEFINE0(sname) \
  SYSCALL_METADATA(_##sname, 0); \
  asmlinkage long sys_##sname(void)

#define SYSCALL_DEFINE1(name, ...) SYSCALL_DEFINEx(1, _##name, __VA_ARGS__)
#define SYSCALL_DEFINE2(name, ...) SYSCALL_DEFINEx(2, _##name, __VA_ARGS__)
#define SYSCALL_DEFINE3(name, ...) SYSCALL_DEFINEx(3, _##name, __VA_ARGS__)
#define SYSCALL_DEFINE4(name, ...) SYSCALL_DEFINEx(4, _##name, __VA_ARGS__)
#define SYSCALL_DEFINE5(name, ...) SYSCALL_DEFINEx(5, _##name, __VA_ARGS__)
#define SYSCALL_DEFINE6(name, ...) SYSCALL_DEFINEx(6, _##name, __VA_ARGS__)

#define SYSCALL_DEFINEx(x, sname, ...) \
  SYSCALL_METADATA(sname, x, __VA_ARGS__) \
  __SYSCALL_DEFINEx(x, sname, __VA_ARGS__)

#define __SYSCALL_DEFINEx(x, name, ...) \
  asmlinkage long sys##name(__MAP(x,__SC_DECL,__VA_ARGS__)) \
  __attribute__((alias(__stringify(SyS##name)))); \
  static inline long SYSC##name(__MAP(x,__SC_DECL,__VA_ARGS__)); \
  asmlinkage long SyS##name(__MAP(x,__SC_LONG,__VA_ARGS__)); \
  asmlinkage long SyS##name(__MAP(x,__SC_LONG,__VA_ARGS__)) \
  { \
    long ret = SYSC##name(__MAP(x,__SC_CAST,__VA_ARGS__)); \
    __MAP(x,__SC_TEST,__VA_ARGS__); \
    __PROTECT(x, ret,__MAP(x,__SC_ARGS,__VA_ARGS__)); \
    return ret; \
} \
static inline long SYSC##name(__MAP(x,__SC_DECL,__VA_ARGS__))

在fs/open.c中,

SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, umode_t, mode)//由上面的宏,可看出此处是sys_open的实现地方。
{
  if (force_o_largefile())
  flags |= O_LARGEFILE;

  return do_sys_open(AT_FDCWD, filename, flags, mode);
}

long do_sys_open(int dfd, const char __user *filename, int flags, umode_t mode)
{
  struct open_flags op;
  int fd = build_open_flags(flags, mode, &op);
  struct filename *tmp;

  if (fd)
    return fd;

  tmp = getname(filename);//获取文件名称
  if (IS_ERR(tmp))
    return PTR_ERR(tmp);

  fd = get_unused_fd_flags(flags);//获取可用的fd
  if (fd >= 0) {
    struct file *f = do_filp_open(dfd, tmp, &op);//创建struct file结构,打开文件
    if (IS_ERR(f)) {
      put_unused_fd(fd);//打开文件失败,释放fd
      fd = PTR_ERR(f);
    } else {
      fsnotify_open(f);//将文件加到监控系统中,监控文件打开关闭。
      fd_install(fd, f);//将struct file 指针加到以fd为idx的array中,以便后续对文件操作。
    }
  }
  putname(tmp);
  return fd;
}

struct file *do_filp_open(int dfd, struct filename *pathname,
const struct open_flags *op)
{
  struct nameidata nd;
  int flags = op->lookup_flags;
  struct file *filp;

  set_nameidata(&nd, dfd, pathname);
  filp = path_openat(&nd, op, flags | LOOKUP_RCU);//open文件。
  if (unlikely(filp == ERR_PTR(-ECHILD)))
  filp = path_openat(&nd, op, flags);
  if (unlikely(filp == ERR_PTR(-ESTALE)))
  filp = path_openat(&nd, op, flags | LOOKUP_REVAL);
  restore_nameidata();
  return filp;
}

static struct file *path_openat(struct nameidata *nd,
const struct open_flags *op, unsigned flags)
{
  const char *s;
  struct file *file;
  int opened = 0;
  int error;

  file = get_empty_filp();//分配struct file
  if (IS_ERR(file))
    return file;

  file->f_flags = op->open_flag;

  if (unlikely(file->f_flags & __O_TMPFILE)) {
    error = do_tmpfile(nd, flags, op, file, &opened);
    goto out2;
  }

  if (unlikely(file->f_flags & O_PATH)) {
    error = do_o_path(nd, flags, file);//如果文件打开的flag是O_PATH,可能是directory.在函数里会调用vfs_open
    if (!error)
    opened |= FILE_OPENED;
    goto out2;
  }

  s = path_init(nd, flags);
  if (IS_ERR(s)) {
    put_filp(file);
    return ERR_CAST(s);
  }
  while (!(error = link_path_walk(s, nd)) &&//解析文件名,转换成dentry
    (error = do_last(nd, file, op, &opened)) > 0) {//open的最后一步,通过dentry查找inode,并最后调用vfs_open
    nd->flags &= ~(LOOKUP_OPEN|LOOKUP_CREATE|LOOKUP_EXCL);
    s = trailing_symlink(nd);
    if (IS_ERR(s)) {
      error = PTR_ERR(s);
      break;
    }
  }
  terminate_walk(nd);
out2:
  if (!(opened & FILE_OPENED)) {
    BUG_ON(!error);
    put_filp(file);
  }
  if (unlikely(error)) {
    if (error == -EOPENSTALE) {
      if (flags & LOOKUP_RCU)
        error = -ECHILD;
      else
        error = -ESTALE;
    }
  file = ERR_PTR(error);
  }
  return file;
}

int vfs_open(const struct path *path, struct file *file,
const struct cred *cred)
{
  struct dentry *dentry = d_real(path->dentry, NULL, file->f_flags);

  if (IS_ERR(dentry))
    return PTR_ERR(dentry);

  file->f_path = *path;
  return do_dentry_open(file, d_backing_inode(dentry), NULL, cred);//打开文件
}

static int do_dentry_open(struct file *f,
struct inode *inode,
int (*open)(struct inode *, struct file *),
const struct cred *cred)
{
  static const struct file_operations empty_fops = {};
  int error;

  f->f_mode = OPEN_FMODE(f->f_flags) | FMODE_LSEEK |
  FMODE_PREAD | FMODE_PWRITE;

  path_get(&f->f_path);
  f->f_inode = inode;
  f->f_mapping = inode->i_mapping;

  if (unlikely(f->f_flags & O_PATH)) {//如果flag包含O_PATH.则struct file_operations是空的
    f->f_mode = FMODE_PATH;
    f->f_op = &empty_fops;
    return 0;
  }

  if (f->f_mode & FMODE_WRITE && !special_file(inode->i_mode)) {
    error = get_write_access(inode);
    if (unlikely(error))
      goto cleanup_file;
    error = __mnt_want_write(f->f_path.mnt);
    if (unlikely(error)) {
      put_write_access(inode);
      goto cleanup_file;
    }
    f->f_mode |= FMODE_WRITER;
}

/* POSIX.1-2008/SUSv4 Section XSI 2.9.7 */
  if (S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode))
    f->f_mode |= FMODE_ATOMIC_POS;

  f->f_op = fops_get(inode->i_fop);//获取inode对应的struct file_operations结构
  if (unlikely(WARN_ON(!f->f_op))) {
    error = -ENODEV;
    goto cleanup_all;
  }

  error = security_file_open(f, cred);
  if (error)
    goto cleanup_all;

    error = break_lease(inode, f->f_flags);
    if (error)
      goto cleanup_all;

  if (!open)
    open = f->f_op->open;//inode所对应的open函数,如果是设备文件,则是驱动程序的open函数。
  if (open) {
    error = open(inode, f);//调用inode所对应的open函数。
  if (error)
    goto cleanup_all;
  }
  if ((f->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ)
    i_readcount_inc(inode);
  if ((f->f_mode & FMODE_READ) &&likely(f->f_op->read || f->f_op->read_iter))
    f->f_mode |= FMODE_CAN_READ;
  if ((f->f_mode & FMODE_WRITE) &&likely(f->f_op->write || f->f_op->write_iter))
    f->f_mode |= FMODE_CAN_WRITE;

  f->f_flags &= ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC);

    file_ra_state_init(&f->f_ra, f->f_mapping->host->i_mapping);

  return 0;

cleanup_all:
  fops_put(f->f_op);
  if (f->f_mode & FMODE_WRITER) {
    put_write_access(inode);
    __mnt_drop_write(f->f_path.mnt);
  }
cleanup_file:
  path_put(&f->f_path);
  f->f_path.mnt = NULL;
  f->f_path.dentry = NULL;
  f->f_inode = NULL;
return error;
}

时间: 01-16

Linux 系统调用的相关文章

Linux系统调用列表(转)

以下是Linux系统调用的一个列表,包含了大部分常用系统调用和由系统调用派生出的的函数.这可能是你在互联网上所能看到的唯一一篇中文注释的Linux系统调用列表,即使是简单的字母序英文列表,能做到这么完全也是很罕见的. 按照惯例,这个列表以man pages第2节,即系统调用节为蓝本.按照笔者的理解,对其作了大致的分类,同时也作了一些小小的修改,删去了几个仅供内核使用,不允许用户调用的系统调用,对个别本人稍觉不妥的地方作了一些小的修改,并对所有列出的系统调用附上简要注释. 其中有一些函数的作用完全

Linux系统调用探究(上)

:first-child { margin-top: 0; } blockquote > :last-child { margin-bottom: 15px; } h1 { text-transform: uppercase; font-weight: bold; border-bottom: 1px solid; } h2 { border-bottom: 1px solid; } h3, h4, h5, h6 { border-bottom: none; } html * { color:

linux平台学x86汇编(十七):在汇编中使用linux系统调用

[版权声明:尊重原创,转载请保留出处:blog.csdn.net/shallnet,文章仅供学习交流,请勿用于商业用途] 在前面章节我们已经看到,启动系统调用需要使用INT指令.linux系统调用位于中断0x80,执行INT指令时,所有操作转移到内核中的系统调用处理程序,完成后执行转移到INT指令之后的下一条指令. linux的系统调用在如下文件(32位系统)可以查看: $ cat /usr/include/asm/unistd_32.h #ifndef _ASM_X86_UNISTD_32_H

添加新的linux系统调用

上一篇详细解释了linux系统调用的原理,接下来根据上一篇的原理简单介绍如何创建新的linux系统调用 向内核中添加新的系统调用,需要执行3个步骤: 1. 添加新的内核函数 2. 更新头文件unistd.h 3. 针对这个新函数更新系统调用表calls.S 1. 在kernel/sys.c中添加函数: asmlinkage int sysMul(int a, int b) { int c; c = a*b; return c; } 2.在arch/arm/include/asm/unistd.h

[Linux]Linux系统调用列表

本文列出了大部分常见的Linux系统调用,并附有简要中文说明. 以下是Linux系统调用的一个列表,包含了大部分常用系统调用和由系统调用派生出的的函数.这可能是你在互联网上所能看到的唯一一篇中文注释的Linux系统调用列表,即使是简单的字母序英文列表,能做到这么完全也是很罕见的. 按照惯例,这个列表以man pages第2节,即系统调用节为蓝本.按照笔者的理解,对其作了大致的分类,同时也作了一些小小的修改,删去了几个仅供内核使用,不允许用户调用的系统调用,对个别本人稍觉不妥的地方作了一些小的修改

[Linux]系统调用理解(1)

本文是Linux系统调用专栏系列文章的第一篇,对Linux系统调用的定义.基本原理.使用方法和注意事项大概作了一个介绍,以便读者对Linux系统调用建立一个大致的印象. 什么是系统调用? Linux内核中设置了一组用于实现各种系统功能的子程序,称为系统调用.用户可以通过系统调用命令在自己的应用程序中调用它们.从某种角度来看,系统调用和普通的函数调用非常相似.区别仅仅在于,系统调用由操作系统核心提供,运行于核心态:而普通的函数调用由函数库或用户自己提供,运行于用户态.二者在使用方式上也有相似之处,

Linux系统调用--getrlimit()与setrlimit()函数详解

http://www.cnblogs.com/niocai/archive/2012/04/01/2428128.html 功能描述:获取或设定资源使用限制.每种资源都有相关的软硬限制,软限制是内核强加给相应资源的限制值,硬限制是软限制的最大值.非授权调 用进程只可以将其软限制指定为0~硬限制范围中的某个值,同时能不可逆转地降低其硬限制.授权进程可以任意改变其软硬限制.RLIM_INFINITY的 值表示不对资源限制. 用法: #include <sys/resource.h>int getr

linux系统调用原理分析

自己想看看别人开发的项目,吸收下经验,然后找到开源中国,有些网上的大牛自己也开发出了开源中国的客户端 在网上看到很多网友说,下载下来安装不了之类的东西,在我这里我带个路,希望对那些朋友有些帮助. https://github.com/jimneylee/JLRubyChina-iPhone   开源中国源码地址 界面如下: 很多的朋友会直接点击Download ZIP,然后下载下来,打开,打开后就直接CocoaPods更新运行如下命令行 $ pod install 下载了其他的sdk下来后,以为

Linux系统调用和库函数调用的区别

Linux下对文件操作有两种方式:系统调用(system call)和库函数调用(Library functions).系统调用实际上就是指最底层的一个调用,在linux程序设计里面就是底层调用的意思.面向的是硬件.而库函数调用则面向的是应用开发的,相当于应用程序的api,采用这样的方式有很多种原因,第一:双缓冲技术的实现.第二,可移植性.第三,底层调用本身的一些性能方面的缺陷.第四:让api也可以有了级别和专门的工作面向. 1.系统调用 系统调用提供的函数如open, close, read,

Linux系统调用及用户编程接口(API)

系统调用 所谓系统调用是指操作系统提供给用户程序调用的一组"特殊"接口,用户程序可以通过这组"特殊"接口来获得操作系统内核提供的服务.例如用户可以通过进程控制相关的系统调用来创建进程.实现进程调度.进程管理等. 为什么用户程序不能直接访问系统内核提供的服务呢?这是由于在Linux中,为了更好地保护内核空间,将程序的运行空间分为内核空间和用户空间(也就是常称的内核态和用户态),它们分别运行在不同的级别上,在逻辑上是相互隔离的.因此,用户进程在通常情况下不允许访问内核数