精华内容
下载资源
问答
  • 我以安卓8.1源代码为例,对应的Linux内核版本是4.15.0 谷歌专门为android定制了C库叫做bionic库给安卓系统层的c/c++代码调用,open()函数定义在下面: bionic/libc/bionic/open.cpp int open(const char* pathname, ...

    系统调用,中断,异常是3种Linux用户空间切换到内核空间的方法
    今天以open系统调用为例,分析从用户空间层到内核空间层的完整流程
    我以安卓8.1源代码为例,对应的Linux内核版本是4.15.0

    谷歌专门为android定制了C库叫做bionic库给安卓系统层的c/c++代码调用,open()函数定义在下面:
    bionic/libc/bionic/open.cpp
    int open(const char* pathname, int flags, …) {
    mode_t mode = 0;

    if ((flags & O_CREAT) != 0) {
    va_list args;
    va_start(args, flags);
    mode = static_cast<mode_t>(va_arg(args, int));
    va_end(args);
    }

    return __openat(AT_FDCWD, pathname, force_O_LARGEFILE(flags), mode);
    }

    bionic/libc/arch-arm/syscalls/__openat.S
    #include <private/bionic_asm.h>

    ENTRY(__openat)
    mov ip, r7
    .cfi_register r7, ip
    ldr r7, =__NR_openat
    swi #0
    mov r7, ip
    .cfi_restore r7
    cmn r0, #(MAX_ERRNO + 1)
    bxls lr
    neg r0, r0
    b __set_errno_internal
    END(__openat)

    对上面的代码进行分析
    __NR_openat是对应的open系统调用号
    r7寄存器保存系统调用号,这样子内核代码可以从这里获取系统调用号,然后调用不同的实现函数
    ENTRY和END为宏定义,后面包含对应的符号地址,因此头文件对应的定义编译为C的符号
    依靠swi指令用户空间切换到内核空间
    发生swi后进入系统中断向量然后执行vector_swi,源代码分析如下(CONFIG_AEABI):
    //内核入口
    kernel/arch/arm/kernel/entry-common.S
    .align 5
    ENTRY(vector_swi)
    #ifdef CONFIG_CPU_V7M
    v7m_exception_entry
    #else
    sub sp, sp, #S_FRAME_SIZE
    stmia sp, {r0 - r12} @ Calling r0 - r12
    ARM( add r8, sp, #S_PC )
    ARM( stmdb r8, {sp, lr}^ ) @ Calling sp, lr
    THUMB( mov r8, sp )
    THUMB( store_user_sp_lr r8, r10, S_SP ) @ calling sp, lr
    mrs r8, spsr @ called from non-FIQ mode, so ok.
    str lr, [sp, #S_PC] @ Save calling PC
    str r8, [sp, #S_PSR] @ Save CPSR
    str r0, [sp, #S_OLD_R0] @ Save OLD_R0

    #endif
    zero_fp
    alignment_trap r10, ip, __cr_alignment
    enable_irq
    ct_user_exit
    get_thread_info tsk
    /*
    * Get the system call number.
    */

    #if defined(CONFIG_OABI_COMPAT)
    /*
    * If we have CONFIG_OABI_COMPAT then we need to look at the swi
    * value to determine if it is an EABI or an old ABI call.
    */
    #ifdef CONFIG_ARM_THUMB
    tst r8, #PSR_T_BIT
    movne r10, #0 @ no thumb OABI emulation
    USER( ldreq r10, [lr, #-4] ) @ get SWI instruction
    #else
    USER( ldr r10, [lr, #-4] ) @ get SWI instruction
    /*r10中存放的就是引起软中断的那条指令的机器码
    发生软中断的时候,系统自动将PC-4存放到了lr寄存器,由于是三级流水,
    并且是ARM状态,还需要减4才能得到发生软中断的那条指令的机器码所在的地址
    */
    #endif
    ARM_BE8(rev r10, r10) @ little endian instruction

    #elif defined(CONFIG_AEABI)
    /*
    * Pure EABI user space always put syscall number into scno (r7).
    /
    #elif defined(CONFIG_ARM_THUMB)
    /
    Legacy ABI only, possibly thumb mode. */
    tst r8, #PSR_T_BIT @ this is SPSR from save_user_regs
    addne scno, r7, #__NR_SYSCALL_BASE @ put OS number in
    USER( ldreq scno, [lr, #-4] )

    #else
    /* Legacy ABI only. */
    USER( ldr scno, [lr, #-4] ) @ get SWI instruction
    #endif
    uaccess_disable tbl
    adr tbl, sys_call_table @ load syscall table pointer
    /此时tbl(r8)中存放的就是sys_call_table的起始地址/

    #if defined(CONFIG_OABI_COMPAT)
    /*
    * If the swi argument is zero, this is an EABI call and we do nothing.
    *
    * If this is an old ABI call, get the syscall number into scno and
    * get the old ABI syscall table address.
    */
    bics r10, r10, #0xff000000
    eorne scno, r10, #__NR_OABI_SYSCALL_BASE
    ldrne tbl, =sys_oabi_call_table
    #elif !defined(CONFIG_AEABI)
    bic scno, scno, #0xff000000 @ mask off SWI op-code
    eor scno, scno, #__NR_SYSCALL_BASE @ check OS number

    #endif

    local_restart:
    ldr r10, [tsk, #TI_FLAGS] @ check for syscall tracing
    stmdb sp!, {r4, r5} @ push fifth and sixth args
    tst r10, #_TIF_SYSCALL_WORK @ are we tracing syscalls?
    bne __sys_trace
    invoke_syscall tbl, scno, r10, ret_fast_syscall
    add r1, sp, #S_OFF
    2: cmp scno, #(__ARM_NR_BASE - __NR_SYSCALL_BASE)
    eor r0, scno, #__NR_SYSCALL_BASE @ put OS number back
    bcs arm_syscall
    mov why, #0 @ no longer a real syscall
    b sys_ni_syscall @ not private func

    #if defined(CONFIG_OABI_COMPAT) || !defined(CONFIG_AEABI)
    /*
    * We failed to handle a fault trying to access the page
    * containing the swi instruction, but we’re not really in a
    * position to return -EFAULT. Instead, return back to the
    * instruction and re-enter the user fault handling path trying
    * to page it in. This will likely result in sending SEGV to the
    * current task.
    */
    9001:
    sub lr, lr, #4
    str lr, [sp, #S_PC]
    b ret_fast_syscall
    #endif
    ENDPROC(vector_swi)

    kernel/arch/arm/kernel/entry-header.S
    scno .req r7 @ syscall number
    tbl .req r8 @ syscall table pointer
    why .req r8 @ Linux syscall (!= 0)
    tsk .req r9 @ current thread_info
    .req 是伪汇编,以 scno .req r7 为例,表示scno是寄存器r7的别名。

    get_thread_info tsk
    其中,tsk是寄存器r9的别名,get_thread_info是一个宏定义,如下:
    1: .macro get_thread_info, rd
    2: mov \rd, sp, lsr #13
    3: mov \rd, \rd, lsl #13
    4: .endm
    即:将sp进行8KB对齐后的值赋给寄存器r9,什么意思?
    这个就涉及到Linux的内核栈了。Linux为每个进程都分配了一个8KB的内核栈,在内核栈的尾端存放有关于这个进程的struct therad_info结构:
    1: struct thread_info {
    2: unsigned long flags; /* low level flags /
    3: int preempt_count; /
    0 => preemptable, <0 => bug /
    4: mm_segment_t addr_limit; /
    address limit */
    5: struct task_struct task; / main task structure */
    6: struct exec_domain exec_domain; / execution domain /
    7: __u32 cpu; /
    cpu /
    8: __u32 cpu_domain; /
    cpu domain /
    9: struct cpu_context_save cpu_context; /
    cpu context /
    10: __u32 syscall; /
    syscall number /
    11: __u8 used_cp[16]; /
    thread used copro /
    12: unsigned long tp_value;
    13: struct crunch_state crunchstate;
    14: union fp_state fpstate attribute((aligned(8)));
    15: union vfp_state vfpstate;
    16: #ifdef CONFIG_ARM_THUMBEE
    17: unsigned long thumbee_state; /
    ThumbEE Handler Base register */
    18: #endif
    19: struct restart_block restart_block;
    20: };
    通过上面的操作,寄存器r9中就是这个进程的thread_info结构的起始地址。
    在linux内核中进程以及线程(多线程也是通过一组轻量级进程实现的)都是通过task_struct结构体来描述的,我们称它为进程描述符。而thread_info则是一个与进程描述符相关的小数据结构,它同进程的内核态栈stack存放在一个单独为进程分配的内存区域。由于这个内存区域同时保存了thread_info和stack,所以使用了联合体来定义,相关数据结构如下:
    thread_union联合体定义:
    union thread_union {
    struct thread_info thread_info;
    unsigned long stack[THREAD_SIZE/sizeof(long)];
    };
    这样设计的好处就是,得到stack,thread_info或task_struct任意一个数据结构的地址,就可以很快得到另外两个数据的地址。

    kernel/arch/arm/kernel/entry-common.S

    ENTRY(sys_call_table)
    #include “calls.S”
    #undef ABI
    #undef OBSOLETE

    /*============================================================================

    • Special system call wrappers
      */
      @ r0 = syscall number
      @ r8 = syscall table
      sys_syscall:
      bic scno, r0, #__NR_OABI_SYSCALL_BASE
      cmp scno, #__NR_syscall - __NR_SYSCALL_BASE
      cmpne scno, #NR_syscalls @ check range
      #ifdef CONFIG_CPU_SPECTRE
      movhs scno, #0
      csdb
      #endif
      stmloia sp, {r5, r6} @ shuffle args
      movlo r0, r1
      movlo r1, r2
      movlo r2, r3
      movlo r3, r4
      ldrlo pc, [tbl, scno, lsl #2]
      b sys_ni_syscall
      ENDPROC(sys_syscall)

    kernel/arch/arm/kernel/calls.S
    /* 0 / CALL(sys_restart_syscall)
    CALL(sys_exit)
    CALL(sys_fork)
    CALL(sys_read)
    CALL(sys_write)
    /
    5 / CALL(sys_open)

    /
    320 */ CALL(sys_get_mempolicy)
    CALL(sys_set_mempolicy)
    CALL(sys_openat)

    对应__openat()的是sys_openat()
    去内核源代码里搜索sys_openat的定义如下:
    kernel/include/linux/syscalls.h
    //asmlinkage是gcc标签,代表函数读取的参数来自于栈中,而非寄存器。
    asmlinkage long sys_openat(int dfd, const char __user *filename, int flags,
    umode_t mode);

    内核源代码里搜索__openat的实现如下:
    kernel/fs/open.c
    SYSCALL_DEFINE4(openat, int, dfd, const char __user *, filename, int, flags,
    umode_t, mode)
    {
    if (force_o_largefile())
    flags |= O_LARGEFILE;
    return do_sys_open(dfd, filename, flags, mode);
    }

    展开全文
  • 浅析linuxopen系统调用

    千次阅读 2014-05-18 10:29:27
    浅析linuxopen系统调用 从2.6.19的linux内核开始,内核的系统调用使用函数syscall,其函数原型为:int syscall(int number, ...)其中number是系统调用号,number后面应顺序接上该系统调用的所有参数。以x86...

    浅析linux中open系统调用


    从2.6.19的linux内核开始,内核的系统调用使用函数syscall,其函数原型为:int syscall(int number, ...)其中number是系统调用号,number后面应顺序接上该系统调用的所有参数。以x86平台为例,系统调用号在内核源码中的路径是/arch/x86/include/asm/unistd_32.h头文件中定义。其中大部分以__NR_开头,比如open的系统调用号是5。

    本文以字符设备的驱动为例,分析系统调用的执行过程,内核版本为2.6.35.

    1、用户空间到内核的转换

    系统调用需要一个从用户空间到内核空间的转换,不同平台转换的指令不同,这种特定的指令称作操作系统的陷入(operating system trap)。X86结构中使用软中断x080来实现。即汇编指令 int $0x80.通过软中断0x80 ,系统就会跳到一个预设的内核空间地址。它指向系统调用处理程序system_call,在arch/x86/kernel/entry_32.S 中以汇编语言编写,该过程主要有2个步骤。

    (1)系统启动时,对INT 0x80进行一定的初始化。

    使用汇编子程序setup_idt(linux/arch/i386/kernel/head.S)初始化idt表(中断描述符表),这时所有的入口函数偏移地址都被设为ignore_int ,如下图所示。

    (2)设置中断描述符表

    start_kernel函数中(init/main.c)调用trap_init()(arch/x86/kernel/trap.c)函数,设置中断描述符表。在trap_init()该函数里,实际上是通过调用函数set_system_trap_gate(SYSCALL_VECTOR, &system_call);来完成该项的设置的。其中的SYSCALL_VECTOR就是0x80,而system_call则是一个汇编子函数,它即是中断0x80的处理函数,主要完成两项工作:寄存器上下文的保存、跳转到系统调用处理函数。

    2、系统调用函数的入口

    syscall_call函数到系统调用服务例程:在上面执行软终端0x80时,系统调用号会被放入eax寄存器,system_call()函数读取eax寄存器获取当前系统调用的调用号。然后将其乘以4生成偏移地址,然后再以sys_call_table为基址。基址+偏移地址=>系统调用服务例程的地址。其中sys_call_table基址在文件arch/x86/kernel/syscall_table_32.S中定义。同时table表中每一项例程的地址占用4个字节,所以上面乘以4。

    到这儿system_call()就到服务例程的地址了。然后另一个问题-参数传递需要解决。由于系统调用例程在定义时时用 asmlinkage 标记了的,所以编译器仅从堆栈中获取该函数的参数。在进入system_call函数前,用户应用会把参数存放到寄存器中,system_call函数执行时会首先把这些寄存器压入堆栈。这样对系统调用服务例程可以直接从堆栈照片能够获取参数。

    3、系统调用函数的执行

    open会最终执行sys_open函数:

    里面调用do_sys_open()函数:

    在上图的L884行中,获取未被使用的文件描述符,在do_filp_open()函数中打开文件。其函数调用过程如下:

    sys_open -> do_sys_open -> do_filp_open ->do_last-> nameidata_to_filp -> __dentry_open

    4、驱动代码file_operations的调用

    在 __dentry_open函数中:

    L685中 open = f->f_op->open;即调用chrdev_open。

    在file: fs/char_dev.c中static int chrdev_open(struct inode *inode, struct file *filp)调用ret = filp->f_op->open(inode,filp);即为真正的file_operations中的open函数。

    当然,在VFS层中对open函数的操作远不止上文描述的这么简单,会进行权限和打开方式的判断等。普通的驱动开发者很少涉及对这部分代码的修改,主要还是学习和欣赏Linux内核。

    展开全文
  • open系统调用源码

    千次阅读 2010-02-21 10:30:00
    用户界面我们使用系统调用open来打开一个文件,例如:fd = open( "/home/mine/data/myfile",O_RDWR...下面来看看Linux是如何完成的,首先是系统调用代码:sys_open程序asmlinkage long sys_open(const char * filen

    用户界面
    我们使用系统调用open来打开一个文件,例如:

    fd = open( "/home/mine/data/myfile",O_RDWR|O_CREAT);

    下面来看看Linux是如何完成的,首先是系统调用的代码:

    sys_open的源程序


    asmlinkage long sys_open(const char * filename, int flags, int mode)

    {

    char * tmp;

    int fd, error;



    #if BITS_PER_LONG != 32

    flags |= O_LARGEFILE;

    #endif

    tmp = getname(filename);

    fd = PTR_ERR(tmp);

    if (!IS_ERR(tmp)) {

    fd = get_unused_fd();/*<-----------------------------------(1) */

    if (fd >= 0) {

    struct file *f = filp_open(tmp, flags, mode);/*<---(2) */

    error = PTR_ERR(f);

    if (IS_ERR(f))

    goto out_error;

    fd_install(fd, f);/*<------------------------------(3) */

    }

    out:

    putname(tmp);

    }

    return fd;



    out_error:

    put_unused_fd(fd);

    fd = error;

    goto out;

    }



    这里面完成的几个工作

    1.注意到返回一个整数fd,所以在(1)的位置获得一个整数fd。
    2.根据路径/home/mine/data/myfile找到(或创建)文件,并且创建一个结构file(见(2))。
    3.将整数fd和file结构指针f联系起来(见(3))。

    几个问题
    1.在《外设中的目录项(以EXT2为例)和内存中的"目录项"的比较
    》一文中可见,一个文件是和inode,进而和dentry联系,那么为什么要创建一个结构file?

    原因是,我们可以有多个对同一个文件操作的"会话",例如一个文件描述符fd1用于读,另一个fd2�
    糜谛础d1的位置是文件开头,fd2的位置是文件结尾等等。因此,file的目的是记录一次对文件�
    僮鞯幕峄啊�

    2.为什么不直接返回结构file指针,而要用一个描述符整数fd?

    原因返回的信息是被用户程序使用的,不可能返回结构指针。所以采用间接的方法,返回一个整数
    ,这个整数对应一个file的指针。

    下面讨论的专题

    1.Linux对文件描述符,结构file的有关实现。

    2.如何从路径找到(或创建)文件。







    我们的

    href=http://www2.linuxforum.net/ker_plan/index/main.htm>内核文档与源码学习

    文章选项: 打印

    lucian_yao 版主
    (addict)
    01-04-30 14:35
    文件描述符与file结构 新 [re: lucian_yao ]  



    一个任务(task)的描述结构task_struct中的字段files指向数据结构 files_struct,这个结构用于管理该任务的文件。

    在files_struct中,

    fd_array 就是联系描述符fd和文件结构file指针的数组,事实上,下标为fd的元素中保存了指向file结构的指针。
    open_fds 是一个标记打开文件的位图。例如,如果fd=7被已被使用,那么open_fds的bit 7就为1。这个结构用于快速查找未使用的描述符。
    close_on_exec 是一个标记位图。某个bit为1表明在这个任务执行系统调用execve的时候,这个bit对应的文件将被关闭,这个描述符将空出来。

    fd_array中包含的指向结构file的指针,如前所述,每个file描述和一个文件的会话,因此,它(通过dentry)指向文件结构inode,另外,包含了会话信息。
    如图,两个file结构指向同一个dentry,从而指向同一个inode,但是各自保留了私有的会话信息,如偏移量f_op等。



    我们的
    内核文档与源码学习

    文章选项: 打印

    lucian_yao 版主
    (addict)
    01-05-08 11:02
    files_struct这三个数组的初始化、再分配 新 [re: lucian_yao ]  

    files_struct这三个数组的初始化、再分配

    初始化
    files_struct内置了3个初始的数组:


    --------------------------------

    struct files_struct {

    ... ...

    struct file ** fd; /* current fd array */

    fd_set *close_on_exec;

    fd_set *open_fds;

    fd_set close_on_exec_init;/*<----------------------这里*/

    fd_set open_fds_init;/*<---------------------------这里*/

    struct file * fd_array[NR_OPEN_DEFAULT];/*<--------这里*/

    };

    ---------------------------------



    其中,fd_set为:

    ---------------------

    typedef __kernel_fd_set fd_set;



    typedef struct {

    unsigned long fds_bits [__FDSET_LONGS];

    } __kernel_fd_set;

    ---------------------



    1024/8=128个字节长。
    因此,当执行fork,在建立新的任务的文件结构时(copy_files),执行下面的语句:


    -----------------------------------

    static int copy_files(unsigned long clone_flags, struct task_struct * tsk)

    { ... ...

    newf = kmem_cache_alloc(files_cachep,
    SLAB_KERNEL);/*<-------------分配files_struct,自然分配了内嵌的数组*/

    ... ...



    newf->close_on_exec = &newf->close_on_exec_init;/*<----------------指向内部的数组*/

    newf->open_fds = &newf->open_fds_init;

    newf->fd = &newf->fd_array[0];



    }

    ------------------------------------



    也就自然地分配了上面3个数组,然后将files_struct的各个指针指向内部的这3个数组。

    再分配

    但是,上面初始分配的数组不一定够用,Linux采用的策略是,重新分配这3个数组(从而可以保证�
    嵌际橇�),然后将老的数组中的内容拷贝到新的数组中,再释放老的数组。
    类似remalloc的方法。
    下面举expand_fdset的例子(描述符数组的再分配是由expand_fd_array完成的):


    ----------------------------------------------------------

    /*

    * Expand the fdset in the files_struct. Called with the files spinlock

    * held for write.

    */

    int expand_fdset(struct files_struct *files, int
    nr)/*<----------------nr是要分配的大小*/

    {

    fd_set *new_openset = 0, *new_execset = 0;

    int error, nfds = 0;



    error = -EMFILE;

    if (files->max_fdset >= NR_OPEN || nr >= NR_OPEN) /*<-----------控制数组的上界*/

    goto out;



    nfds = files->max_fdset;

    write_unlock(&files->file_lock);



    /* Expand to the max in easy steps */

    do
    {/*<---------------------------------获得数组的大小,不一定等于nr,如果比一页小,则增长
    到一页,不然以2的幂增长*/

    if (nfds < (PAGE_SIZE * 8))

    nfds = PAGE_SIZE * 8;

    else {

    nfds = nfds * 2;

    if (nfds > NR_OPEN)

    nfds = NR_OPEN;

    }

    } while (nfds <= nr);



    error = -ENOMEM;

    new_openset =
    alloc_fdset(nfds);/*<-----------------------------(该行及下行)分配新的位图数组*/

    new_execset = alloc_fdset(nfds);

    write_lock(&files->file_lock);

    if (!new_openset || !new_execset)

    goto out;



    error = 0;



    /* Copy the existing tables and install the new pointers */

    if (nfds > files->max_fdset) {

    int i = files->max_fdset / (sizeof(unsigned long) * 8);

    int count = (nfds - files->max_fdset) / 8;



    /*

    * Don't copy the entire array if the current fdset is

    * not yet initialised.

    */

    if (i) {

    memcpy (new_openset, files->open_fds,
    files->max_fdset/8);/*<---将老数组拷贝到新数组里,多余部分置0*/

    memcpy (new_execset, files->close_on_exec, files->max_fdset/8);

    memset (&new_openset->fds_bits[ i ], 0, count);

    memset (&new_execset->fds_bits[ i ], 0, count);

    }



    nfds = xchg(&files->max_fdset,
    nfds);/*<-------------------更换files_struct中的大小和指针*/

    new_openset = xchg(&files->open_fds, new_openset);

    new_execset = xchg(&files->close_on_exec, new_execset);

    write_unlock(&files->file_lock);

    free_fdset (new_openset, nfds);/*<-------------------------释放老的数组*/

    free_fdset (new_execset, nfds);

    write_lock(&files->file_lock);

    return 0;

    }

    /* Somebody expanded the array while we slept ... */



    out:

    write_unlock(&files->file_lock);

    if (new_openset)

    free_fdset(new_openset, nfds);

    if (new_execset)

    free_fdset(new_execset, nfds);

    write_lock(&files->file_lock);

    return error;

    }

    --------------------------------------------------------------



    注意,释放老的数组时,如果是原来嵌在files_struct中的,则不能释放:


    ------------------------------------------

    void free_fdset(fd_set *array, int num)

    {

    int size = num / 8;



    if (!array) {

    printk (KERN_ERR __FUNCTION__ "array = 0 (num = %d)/n", num);

    return;

    }



    if (num <= __FD_SETSIZE) /* Don't free an embedded fdset
    *//*<-------内嵌的不能释放!*/

    return;

    else if (size <= PAGE_SIZE)

    kfree(array);

    else

    vfree(array);

    }

    ---------------------------------------













    我们的

    href=http://www2.linuxforum.net/ker_plan/index/main.htm>内核文档与源码学习

    文章选项: 打印

    lucian_yao 版主
    (addict)
    01-05-08 11:04
    如何获得空闲的fd 新 [re: lucian_yao ]  

    现在回到sys_open中,看看是如何获得一个未使用的文件描述符的。

    下面的函数获得一个未使用的文件描述符。

    --------------------------------------------------------------
    /*
    * Find an empty file descriptor entry, and mark it busy.
    */
    int get_unused_fd(void)
    {
    struct files_struct * files = current->files;
    int fd, error;

    error = -EMFILE;
    write_lock(&files->file_lock);

    repeat:
    fd = find_next_zero_bit(files->open_fds,
    files->max_fdset,
    files->next_fd); /*<--------从原有位图中找一个bit为0,这样它对应的描述符为空*/

    /*
    * N.B. For clone tasks sharing a files structure, this test
    * will limit the total number of files that can be opened.
    */
    if (fd >= current->rlim[RLIMIT_NOFILE].rlim_cur)/*<-------------???*/
    goto out;

    /* Do we need to expand the fdset array? */
    if (fd >= files->max_fdset) {
    /*<-------------------如果老的位图数组已经满了,那么扩大数组*/
    error = expand_fdset(files, fd);
    if (!error) {
    error = -EMFILE;
    goto repeat;
    }
    goto out;
    }

    /*
    * Check whether we need to expand the fd array.
    */
    if (fd >= files->max_fds) {/*<----------------------同样,如必要扩大描述符数组*/
    error = expand_fd_array(files, fd);
    if (!error) {
    error = -EMFILE;
    goto repeat;
    }
    goto out;
    }

    FD_SET(fd, files->open_fds);/*<----------------------将该描述符对应位图的bit置1*/
    FD_CLR(fd,
    files->close_on_exec);/*<-----------------将该位清0,如果要置1,需调用fcntl*/
    files->next_fd = fd + 1;/*<--------------------------下次查找从下一个bit开始*/
    #if 1
    /* Sanity check */
    if (files->fd[fd] != NULL) {
    printk("get_unused_fd: slot %d not NULL!/n", fd);
    files->fd[fd] = NULL;
    }
    #endif
    error = fd;

    out:
    write_unlock(&files->file_lock);
    return error;
    }
    --------------------------------------------------------------------------------


    在sys_open中,当获得空的描述符fd以及一个file结构指针f后,调用

    fd_install(fd, f)

    将描述符数组下标为fd的元素置为指针f:


    ---------------------------------------------------

    static inline void fd_install(unsigned int fd, struct file * file)

    {

    struct files_struct *files = current->files;



    write_lock(&files->file_lock);

    if (files->fd[fd])

    BUG();

    files->fd[fd] = file;/*<-------------------设置指针*/

    write_unlock(&files->file_lock);

    }

    ----------------------------------------------------



    这个线索下面的帖子是讨论fork和execve的,关于sys_open的内容在系统调用open下。

    我们的

    href=http://www2.linuxforum.net/ker_plan/index/main.htm>内核文档与源码学习

    文章选项: 打印

    lucian_yao 版主
    (addict)
    01-05-08 11:10
    文件指针的复制 新 [re: lucian_yao ]  

    文件指针的复制

    在fork(以及clone)中,有件非常重要的工作,就是复制文件指针,从而使得父子任务可以对相同的文件操作。

    fork与clone是不同的。如图:

    在clone中,复制了父任务的task_struct,这样父子指向同样的files_struct,以后无论父子谁打开/关闭文件,对另一方来说,也相当于有同样的操作。也许就是所谓在同一环境中吧。


    而在fork中,又复制了files_struct,并且复制了files_struct指向的fd_array,这样两个fd_array相同的描述符都指向同样的文件。
    与clone不同的是,在fork结束时,父子初始状态是一致的,但是以后父子可以有独立的打开/关闭文件的状态(以及close_on_exec状态),进而可以更换某个文件描述符对应文件指针使得同样的描述符对应不同的file结构。

    我们的
    内核文档与源码学习

    文章选项: 打印

    lucian_yao 版主
    (addict)
    01-05-08 11:11
    "文件指针的复制"的源码 新 [re: lucian_yao ]  

    "文件指针的复制"的源码

    -------------------------------------------------------------------------
    static int copy_files(unsigned long clone_flags, struct task_struct * tsk)
    {
    struct files_struct *oldf, *newf;
    struct file **old_fds, **new_fds;
    int open_files, nfds, size, i, error = 0;

    /*
    * A background process may not have any files ...
    */
    oldf = current->files;
    if (!oldf)
    goto out;

    if (clone_flags & CLONE_FILES) {/*<-------------------------------如果是clone,则不需要复制数组*/
    atomic_inc(&oldf->count);
    goto out;
    }

    tsk->files = NULL;
    error = -ENOMEM;
    newf = kmem_cache_alloc(files_cachep, SLAB_KERNEL);/*<-------------分配一个新的files_struct*/
    if (!newf)
    goto out;

    atomic_set(&newf->count, 1);

    newf->file_lock = RW_LOCK_UNLOCKED;
    newf->next_fd = 0;
    newf->max_fds = NR_OPEN_DEFAULT;
    newf->max_fdset = __FD_SETSIZE;
    newf->close_on_exec = &newf->close_on_exec_init;/*<---------------首先指向内置的数组,但是由于父亲的数组可能已经比这个大了,所以可能还会扩展*/
    newf->open_fds = &newf->open_fds_init;
    newf->fd = &newf->fd_array[0];

    /* We don't yet have the oldf readlock, but even if the old
    fdset gets grown now, we'll only copy up to "size" fds */
    size = oldf->max_fdset;
    if (size > __FD_SETSIZE) {/*<---------------------------------------如果父任务的位图数组已经扩展过了,那么子任务的就不能用内置数组了,也要扩展*/
    newf->max_fdset = 0;
    write_lock(&newf->file_lock);
    error = expand_fdset(newf, size-1);
    write_unlock(&newf->file_lock);
    if (error)
    goto out_release;
    }
    read_lock(&oldf->file_lock);

    open_files = count_open_files(oldf, size);

    /*
    * Check whether we need to allocate a larger fd array.
    * Note: we're not a clone task, so the open count won't
    * change.
    */
    nfds = NR_OPEN_DEFAULT;
    if (open_files > nfds) {/*<----------------------------------------同样,如果父亲的已经扩展过了,子任务的也要扩展*/
    read_unlock(&oldf->file_lock);
    newf->max_fds = 0;
    write_lock(&newf->file_lock);
    error = expand_fd_array(newf, open_files-1);/*<------------注意这里,并没有完全照搬父亲的描述符数组,可能有所缩减(由open_files决定)*/
    write_unlock(&newf->file_lock);
    if (error)
    goto out_release;
    nfds = newf->max_fds;
    read_lock(&oldf->file_lock);
    }

    old_fds = oldf->fd;
    new_fds = newf->fd;

    memcpy(newf->open_fds->fds_bits, oldf->open_fds->fds_bits, open_files/8);/*<------------------复制位图数组*/
    memcpy(newf->close_on_exec->fds_bits, oldf->close_on_exec->fds_bits, open_files/8);

    for (i = open_files; i != 0; i--) {/*<-------------------------------------------------------复制描述符数组(不一 定完全复制,由open_files决定)*/
    struct file *f = *old_fds++;
    if (f)/*<----------------------------------------------------------------------------增加file结构的引用数*/
    get_file(f);
    *new_fds++ = f;
    }
    read_unlock(&oldf->file_lock);

    /* compute the remainder to be cleared */
    size = (newf->max_fds - open_files) * sizeof(struct file *);

    /* This is long word aligned thus could use a optimized version */
    memset(new_fds, 0, size);

    if (newf->max_fdset > open_files) {
    int left = (newf->max_fdset-open_files)/8;
    int start = open_files / (8 * sizeof(unsigned long));

    memset(&newf->open_fds->fds_bits[start], 0, left);
    memset(&newf->close_on_exec->fds_bits[start], 0, left);
    }

    tsk->files = newf;
    error = 0;
    out:
    return error;

    out_release:
    free_fdset (newf->close_on_exec, newf->max_fdset);
    free_fdset (newf->open_fds, newf->max_fdset);
    kmem_cache_free(files_cachep, newf);
    goto out;
    }


    我们的
    内核文档与源码学习

    文章选项: 打印

    lucian_yao 版主
    (addict)
    01-05-08 11:14
    close_on_exec的使用 新 [re: lucian_yao ]  

    看看close_on_exec标志是如何使execve关闭对应的文件的


    do_execve 中调用 search_binary_handler,后者有下面的语句:


    -------------------------------------

    int (*fn)(struct linux_binprm *, struct pt_regs *) = fmt->load_binary;

    ... ...

    retval = fn(bprm, regs);

    -------------------------------------



    而fmt->load_binary在下面定义:


    ------------------------------------

    struct linux_binfmt {

    struct linux_binfmt * next;

    struct module *module;

    int (*load_binary)(struct linux_binprm *, struct pt_regs *
    regs);/*<----------这里定义*/

    int (*load_shlib)(struct file *);

    int (*core_dump)(long signr, struct pt_regs * regs, struct file * file);

    unsigned long min_coredump; /* minimal dump size */

    };



    static struct linux_binfmt elf_format = {

    NULL, THIS_MODULE, /*defined here!*/load_elf_binary, load_elf_library, elf_core_dump,
    ELF_EXEC_PAGESIZE

    };

    ------------------------------------



    load_elf_binary 调用 flush_old_exec,后者调用 flush_old_files(current->files);
    flush_old_files做的事情是:

    把那些对应标志为1的文件关闭。


    ------------------------------------------------------

    static inline void flush_old_files(struct files_struct * files)

    {

    long j = -1;



    write_lock(&files->file_lock);

    for (;;) {

    unsigned long set, i;



    j++;

    i = j * __NFDBITS;

    if (i >= files->max_fds || i >= files->max_fdset)

    break;

    set = files->close_on_exec->fds_bits[j];

    if (!set)

    continue;

    files->close_on_exec->fds_bits[j] = 0;

    write_unlock(&files->file_lock);

    for ( ; set ; i++,set >>= 1) {

    if (set & 1) {

    /*这里!*/ sys_close(i);

    }

    }

    write_lock(&files->file_lock);



    }

    write_unlock(&files->file_lock);

    }

    --------------------------------------------------------




    我们的

    href=http://www2.linuxforum.net/ker_plan/index/main.htm>内核文档与源码学习

    文章选项: 打印

    lucian_yao 版主
    (addict)
    01-05-08 11:21
    小结 新 [re: lucian_yao ]  

    在sys_open中,我们要完成几件事:
    1.找到一个未使用的fd
    2.根据open中提供的路径字符串找到(创建)对应的dentry(从而找到inode)
    3.创建file结构,使得它指向这个dentry
    4.在描述符数组中fd的位置设为指向file结构的指针

    其中,1,4前面已经详细研究。2,3在sys_open中调用的filp_open中实现的。其中,

    根据路径找dentry是由open_namei完成的
    创建file结构是由dentry_open完成的

    下面将分别详细研究open_namei,也就是"路径解析",以及创建file结构

    我们的
    内核文档与源码学习

    展开全文
  • 公开的 Open Silicon Tracer是ARM / Linux系统调用跟踪程序原型
  • android-linux系统调用原理之整体系统框架实现原理之OPEN实现.docx 【 相关源码版本: LINUX内核源码版本:linux-3.0.86 UBOOT版本:uboot-2010.12. Android系统源码版本:Android-5.0.2】   对于安桌系统来...
    

    android-linux系统调用原理之整体系统框架实现原理之OPEN实现.docx

    相关源码版本:

    LINUX内核源码版本:linux-3.0.86

    UBOOT版本:uboot-2010.12.

    Android系统源码版本:Android-5.0.2

     

    对于安桌系统来说系统调用就是指:Andriod层调用Linux函数。由于安桌层在文件系统层,而LINUX层在内核层,两者是在不同的地址空间的。内核空间是高地址1G空间,余下的就是应用空间。对于32ARM则最大能寻址空间是4G。则高1G共内核调用低3G供应用层调用。应用层通过系统调用可以陷入内核空间(SWI指令),因此间接的可以认为应用程序可以访问全部4G地址空间。如下图所示:

     

    从上面分析可知系统调用在安桌层和内核层必须是一一对应的。下面列出在不同层原函数文件位置

    安桌层:

    系统调用序号文件列表:diordnr5.0.2\bionic\libc\kernel\uapi\asm-arm\asm\Unistd.h(内核根据应用传过来的这个索引号来调用对应于应用层调用的系统调用函数)

    函数申明路径:diordnr5.0.2\bionic\libc\bionic\*(不同系统调用对应不同的函数文件 eg:open.cpp 等)

    内核层:

    系统调用序号文件列表:include\asm-generic\Unistd.h

    系统调用函数列表:arch\arm\kernel\calls.S

    函数申明路径:include\linux\ syscalls.h

     

     

     

    下面以open系统调用为例,描述OPEN函数的实现流程(我们只分析主线 结节不是本文目的)

    安桌层:

    bionic\libc\bionic\open.cpp:    Open()--->__openat()--->

    bionic\libc\arch-arm\syscalls\__openat.S:   ENTRY(__openat)--->SWI陷入内核

    内核层:由应用层发起的中断进入内核中断向量的软件中断地址入口处

    arch\arm\kernel\entry-armv.S :   __vectors_start:W(ldr) pc, .LCvswi + stubs_offset--->

    arch\arm\kerne\entry-common.S:   ENTRY(vector_swi)--->adr tbl, sys_call_table @ load syscall table pointer--->#include "calls.S"--->ldrcc pc, [tbl, scno, lsl #2] @ call sys_* routine(根据应用层传入的索引号去调用calls.s中的不同函数)--->calls.S:  CALL(sys_open==SYSCALL_DEFINE4(openat, int, dfd, const char __user *, filename, int, flags,

    int, mode))--->

    \fs\open.c:SYSCALL_DEFINE4(openat, int, dfd, const char __user *, filename, int, flags,

    int, mode)//经过内核系统中的宏定义经过转换之后上面宏等于

    asmlinkage long sys_openat(int dfd, const char __user *filename, int flags,

       int mode);这个原型。这儿就与应用层调用的参数对应上了。

    经过上面流程就进入了内核的open(应用层是OPEN原函数,实际经过转换之后进入内核的函数是__openat,内核层函数原型名字始终是应用层函数的前面加上sys_)真正处理的地方子。下面就来专分析

    asmlinkage long sys_openat(int dfd, const char __user *filename, int flags,

       int mode);的实现原理。

    \fs\open.c:SYSCALL_DEFINE4(openat, int, dfd, const char __user *, filename, int, flags,

    int, mode)--->do_sys_open():fd(需返回给应用层的文件描述符)--->struct file *f = do_filp_open(dfd, tmp, &op, lookup);打开从应层传进来的文件路径的文件,并且得到一个f文件指针,根据UDEV设备模型,F文件的f->const struct file_operations *f_op;原素(如果是驱动文件)就是指向我们注册驱动注册的驱动函数。这个是fd返回给应用层最后通过能调用驱动操作函数的根本原理。--->fd_install(fd, f);让打开的文件得到的文件指针与要返回的文件描述符fd关键起来。只有这儿关键来了当在应用层通过f操作时才能进入内核调用对应的注册函数。--->return fd;--->返回安桌swi陷入下一个地址处继续执行。(从应用层进入内核层:SWI陷入后应用层就停止等到内核返回。这儿可以看成进程的上下文件,上文是应用层,下文是内核层。上文会得到下文返回才会继续执行。并且可中断可抢占。这样就才可以实现多程)。应用层得到fd,再来操作这个fd时通过陷入进入内核,内核会去查找这个fd对应用的f文件并且得到f_ops元素,当然对应的系统调用还会有他自己的一些执行流程。

     

     

     

     

     

     

    asmlinkagegcc标签,代表函数读取的参数来自于栈中,而非寄存器。

     

    安卓系统中进程与线程异同:

    进程:每个App在启动前必须先创建一个进程,该进程是由Zygote fork出来的,进程具有独立的资源空间,用于承载App上运行的各种Activity/Service等组件。进程对于上层应用来说是完全透明的,这也是google有意为之,让App程序都是运行在Android Runtime。大多数情况一个App就运行在一个进程中,除非在AndroidManifest.xml中配置Android:process属性,或通过native代码fork进程。

    线程:线程对应用开发者来说非常熟悉,比如每次new Thread().start()都会创建一个新的线程,该线程并没有自己独立的地址空间,而是与其所在进程之间资源共享。从Linux角度来说进程与线程都是一个task_struct结构体,除了是否共享资源外,并没有其他本质的区别。

    system_server进程:是用于管理整个Java framework层,包含ActivityManagerPowerManager等各种系统服务;

    Zygote进程:是Android系统的首个Java进程,Zygote是所有Java进程的父进程,包括system_server进程以及所有的App进程都是Zygote的子进程,注意这里说的是子进程,而非子线程。

    Fork创建是创建进程,pthread创建是线程。

    展开全文
  • Linux系统调用是操作系统所实现的应用编程接口(Application Programming Interface,API),说简单点就是Linux内核对外提供的接口函数,对外就是指对一个个进程而言,进程通过调用系统调用完成自身需的全部功能。...
  • 浅谈Linux系统调用代码相关

    千次阅读 2014-04-14 19:37:52
    内核代码的跳跃性造就了代码的难懂,本文对Linux中的系统调用实现代码做了简单分析,还有好多不懂,等待高手指点~~
  • 注:本文分析基于3.10.0-693.el7内核版本,即CentOS 7.4,glibc...而我们平时经常用的open、close、read、write等函数,其实并不是linux提供的系统调用,而是glibc提供的接口,它封装了linux的相关系统调用,以一个...
  • eCryptfs - open系统调用

    2016-07-29 10:24:06
    入口(系统调用include/linux/syscall.h)由于安全上的一些问题,2.6和3.x系类的系统调用改动幅度有些偏大。系统调用均经过如下代码进行“过滤”:#define SYSCALL_DEFINE1(name, ...) SYSCALL_DEFINEx(1, _##name, ...
  • linux内核mount系统调用源码分析

    千次阅读 2014-12-16 14:38:56
    mount是Linux很常见的命令,本文将从用户空间的命令行开始,一步一步切入到内核的源代码,解释一个文件系统是如果挂载的。本文基于linux 2.6.32 1.SYSCALL_DEFINE5, 系统调用 Linux kernel通过系统调用的方式为...
  • open 系统调用

    千次阅读 2012-04-08 15:56:54
    进程要访问一个文件,必须首先获得一个文件描述符,这是通过open系统调用来完成的。文件描述符是有限的资源,所以在不用时应该及时释放。 该系统调用是用来获得欲访问文件的文件描述符,如果文件并不存在,则还
  • 转自:中国Linux论坛 原作者:lucian_yao 内容有...下面来看看Linux是如何完成的,首先是系统调用代码:sys_open程序asmlinkage long sys_open(const char * filename, int flags, int mode) { char * tmp;
  • Linux read系统调用

    千次阅读 2015-06-19 10:03:07
    最近一个项目做了一个模拟u盘的设备,但是在read虚拟u盘的内容时必须每次都从磁盘内读取,而不是从系统的cache中读取,由于这个问题,就查资料看了下read的系统调用,以及文件系统的一些内容。由于文件系统涉及面较...
  • Linux 系统调用open(一)

    千次阅读 2018-02-23 00:59:25
    本文仅以下面简单代码为例,看Linux内核如何完成open系统调用的(参考内核版本:4.14.13,x86_64)。为了简单起见,本文中暂时忽略并发性访问的同步问题以及错误处理(一般情况下,上述事例中的操作都会成功,除非...
  • linux系统调用的过程

    2019-07-04 16:25:11
    系统调用的过程(从open到sys_open,其实glibc使用的是sys_openat后面会说)3.1 先写一个小程序(test_syscall.c)3.2 查看编译链接后的汇编代码3.3 动态库中的open函数3.4 在动态库的源码中查找open函数(当然是在...
  • Linux系统调用

    千次阅读 2013-11-16 23:37:41
    使用 Linux 系统调用的内核命令 深入理解Linux系统调用 Linux系统调用接口、系统调用例程和内核服务例程之间的关系 向linux内核中添加三个系统调用(Ubuntu9.10) ++++++++++++++++++++++++++++++++++ 使用 ...
  • linux新增系统调用

    2016-10-13 10:50:06
     在linux中,系统调用是用户空间访问内核的一种手段,除异常和中断外,他们是进入内核的合法入口。系统调用的数量很少,在i386上只有大概300个左右。  应用程序员通过C库中的应用程序接口(API)而不是直接通过系统...
  • Linuxopen系统调用实现原理 2012-11-29 23:03:48 分类: 原文地址:Linuxopen系统调用实现原理 作者:up哥小号 目录 OPEN系统调用过程 Open在内核里面的入口函数时sys_open Sys_open函数内容 ...
  • linux添加系统调用总结(内核版本4.4.4)

    万次阅读 多人点赞 2016-03-09 19:34:33
    Linux添加系统调用总结最近在做操作系统的课程设计,其中一题就是给linux系统添加一个copy功能的系统调用,折腾之后总结一下:其实一开始我是不打算写的,但是发现老师的课设指导实在是太过时了,linux内核这都更新...
  • linux添加系统调用,网上一搜一大堆,我很久之前也试图添加过并且按照网上的教程一步一步做,但是都没有成功过,因为网上的教程大体都是一个样子,一份创造多分副本,照着做也不明白为什么,而且linux内核版本从...
  • 操作系统 -- open系统调用实现原理

    千次阅读 2017-01-05 11:51:52
    转载自:... 目录 OPEN系统调用过程 Open在内核里面的入口函数时sys_open Sys_open函数内容 do_sys_open(AT_FDCWD, filename, flags, mode) 1. 找到一个本进程没有使用的文件描述符fd(int型)
  • linux 内核源码 系统调用宏定义

    千次阅读 2015-09-28 17:07:05
    linux 内核源码中关于系统调用源码阅读
  • Linux系统调用的实现

    千次阅读 2016-08-27 22:46:17
    Linux系统调用的实现 Unix/Linux操作系统的体系结构及系统调用介绍 什么是操作系统系统调用  操作系统是从硬件抽象出来的虚拟机,在该虚拟机上用户可以运行应用程序。它负责直接与硬件交互,向用户...
  • 一、 系统调用的概念 系统调用在Wikipedia中的解释为: In computing, a system call is the programmatic way in which a computer program requests a service from the kernel of the operating system it is...
  • Linux系统调用指南

    千次阅读 2019-01-13 17:45:35
    Linux系统调用指南 格式后头慢慢调,暂时先这样 原文链接: blog.packagecloud.io https://zcfy.cc/article/the-definitive-guide-to-linux-system-calls-670.html?t=new 这篇blog解释linux程序如何调用linux内核...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 98,960
精华内容 39,584
关键字:

linuxopen系统调用源码

linux 订阅