write函数作为用户向终端或者文件进行写数据的重要函数,有着重要的作用。

|------|          |---------|      |---------|     |----------| 
| write |----->|sys_write|-------->|vfs_write|------->|ext4_write|
|------|          |---------|             |---------|             |----------|

上面是write函数从用户空间到内核空间的过程。比如下面这个具体的例子write_test.c:

#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>

int main(void)
{
    char buffer[10] = "hello\n";
    int count;
    int fd = open ("ab.txt",O_RDWR);
    if (fd == -1)
    {
        fprintf(stderr,"can't open file:[%s]\n","ab.txt");
        exit(EXIT_FAILURE);
    }
    count = write(fd,buffer,strlen(buffer));
    if (count == -1)
    {
        fprintf(stderr,"write error\n");
        exit(EXIT_FAILURE);
    }

exit(EXIT_SUCCESS);
}

采用gcc静态编译的方式:gcc -static -o read_write  read_write.c  【使用静态编译的方式是为了更好的观看汇编代码】

接下来进行如下:root@node1:~/unixprogram# objdump  -d write_test > assemble   【注:因显示的汇编代码有11万行,所以保存为文件,采用动态链接库的方式文件小很多,这是默认的gcc编译选项】

查看assemble文件:【挑选重要的行】

08048250 <main>:
......
80482d9:    89 44 24 04              mov    %eax,0x4(%esp)
80482dd:    8b 44 24 18              mov    0x18(%esp),%eax
80482e1:    89 04 24                 mov    %eax,(%esp)
80482e4:    e8 e7 77 00 00           call   804fad0 <__libc_write>
......

上面是对应于main函数中的汇编,显示的是调用的是__libc_write函数

0804fad0 <__libc_write>:
 804fad0:    65 83 3d 0c 00 00 00     cmpl   $0x0,%gs:0xc
 804fad7:    00
 804fad8:    75 21                    jne    804fafb <__write_nocancel+0x21>

上面对应的是__libc_write函数的汇编代码,在__libc_write函数中显示它调用了__write_nocancel函数

0804fada <__write_nocancel>:
 804fada:    53                       push   %ebx
 804fadb:    8b 54 24 10              mov    0x10(%esp),%edx
 804fadf:    8b 4c 24 0c              mov    0xc(%esp),%ecx
 804fae3:    8b 5c 24 08              mov    0x8(%esp),%ebx
 804fae7:    b8 04 00 00 00           mov    $0x4,%eax
 804faec:    cd 80                    int    $0x80
 ......

从__write_nocancel函数中我们可以看到,在这里传递给eax寄存器立即数4,这是系统调用write的调用number,然后就进行软中断int $0x80进入到内核状态,进行的是system_call()中断处理函数。

【补充说明】在glibc-2.11.2中只有__libc_write函数原型,而对于__write_nocancel是根据系统调用模板生成的,在源码中没有,其中__libc_write在glibc-2.11.2中的io/write.c中有定义。如下

/* Write NBYTES of BUF to FD.  Return the number written, or -1.  */
ssize_t
  __libc_write (int fd, const void *buf, size_t nbytes)
  {
    if (nbytes == 0)
        return 0;
    if (fd < 0)
      {
        __set_errno (EBADF);
        return -1;
      }
    if (buf == NULL)
      {
        __set_errno (EINVAL);
        return -1;
      }

   __set_errno (ENOSYS);
    return -1;
  }
  libc_hidden_def (__libc_write)
  stub_warning (write)

  weak_alias (__libc_write, __write)
  libc_hidden_weak (__write)
  weak_alias (__libc_write, write)
  #include <stub-tag.h>

而系统调用部分是根据如下几个文件生成:glibc-2.11.2/sysdeps/unix/syscall.S  glibc-2.11.2/sysdeps/unix/syscalls.list  glibc-2.11.2/sysdeps/unix/syscall-template.S

对于系统调用处理的汇编代码位于linux源码中的 arch/x86/kernel/entry_32.S 【注:内核版本为2.6.39 处理器:x86中的32位】

ENTRY(system_call)
    RING0_INT_FRAME            # can't unwind into user space anyway
    pushl_cfi %eax            # save orig_eax
    SAVE_ALL
    GET_THREAD_INFO(%ebp)
                    # system call tracing in operation / emulation
    testl $_TIF_WORK_SYSCALL_ENTRY,TI_flags(%ebp)
    jnz syscall_trace_entry
    cmpl $(nr_syscalls), %eax
    jae syscall_badsys
syscall_call:
    call *sys_call_table(,%eax,4)
    movl %eax,PT_EAX(%esp)        # store the return value
......

这是系统调用的处理函数,通过找到系统调用表中的write系统调用,然后进入到sys_write。

/*
 * This file contains the system call numbers.
 */

#define __NR_restart_syscall      0
#define __NR_exit          1
#define __NR_fork          2
#define __NR_read          3
#define __NR_write          4
#define __NR_open          5
#define __NR_close          6
#define __NR_waitpid          7
#define __NR_creat          8
........

上面的是linux下的系统调用号列表,位于linux-2.6.39/arch/x86/include/asm/unistd_32.h中。

而对于系统调用表中的write函数位于linux-2.6.39/fs/read_write.c中,如下:

SYSCALL_DEFINE3(write, unsigned int, fd, const char __user *, buf,
        size_t, count)
{
    struct file *file;
    ssize_t ret = -EBADF;
    int fput_needed;

file = fget_light(fd, &fput_needed);
    if (file) {
        loff_t pos = file_pos_read(file);
        ret = vfs_write(file, buf, count, &pos);
        file_pos_write(file, pos);
        fput_light(file, fput_needed);
    }

return ret;
}

write函数过程解析的更多相关文章

  1. windows窗口过程函数名词解析

    windows窗口过程函数名词解析 LRESULT CALLBACK WndProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam) 1. LR ...

  2. python+opencv->边缘提取与各函数参数解析

    前情提要:作为刚入门机器视觉的小伙伴,第一节课学到机器视觉语法时觉得很难理解, 很多人家的经验,我发现都千篇一律,功能函数没解析,参数不讲解,就一个代码,所以在此将搜集的解析和案例拿出来汇总!!! 一 ...

  3. 曹工说Redis源码(5)-- redis server 启动过程解析,以及EventLoop每次处理事件前的前置工作解析(下)

    曹工说Redis源码(5)-- redis server 启动过程解析,eventLoop处理事件前的准备工作(下) 文章导航 Redis源码系列的初衷,是帮助我们更好地理解Redis,更懂Redis ...

  4. 字串符相关 split() 字串符分隔 substring() 提取字符串 substr()提取指定数目的字符 parseInt() 函数可解析一个字符串,并返回一个整数。

    split() 方法将字符串分割为字符串数组,并返回此数组. stringObject.split(separator,limit) 我们将按照不同的方式来分割字符串: 使用指定符号分割字符串,代码如 ...

  5. Delphi反汇编内部字符串处理函数/过程不完全列表

    Delphi反汇编内部字符串处理函数/过程不完全列表 名称 参数 返回值 作用 等价形式 / 备注   _PStrCat EAX :目标字符串 EDX :源字符串 EAX 连接两个 Pascal 字符 ...

  6. Oracle中函数/过程返回结果集的几种方式

    原文 Oracle中函数/过程返回结果集的几种方式 Oracle中函数/过程返回结果集的几种方式:    以函数return为例,存储过程只需改为out参数即可,在oracle 10g测试通过.    ...

  7. NotePad++ delphi/Pascal函数过程列表插件

    从cnpack上爬下来的 函数过程列表 点击下载

  8. MHA自动Failover过程解析(updated) 转

    允许转载, 转载时请以超链接形式标明文章原始出处和网站信息 http://www.mysqlsystems.com/2012/03/figure-out-process-of-autofailover ...

  9. Generator函数语法解析

    转载请注明出处: Generator函数语法解析 Generator函数是ES6提供的一种异步编程解决方案,语法与传统函数完全不同.以下会介绍一下Generator函数. 写下这篇文章的目的其实很简单 ...

随机推荐

  1. 数组之reduce()和reduceRight()

    1.reduce()和reduceRight()方法使用指定的函数将数组元素进行组合,生成单个值. reduce()可以传入两个参数,第一个是执行化简操作的函数.同样这个函数可以有参数,第一个参数代表 ...

  2. webpack_hmr报错 cannot load 状态500

    使用vue-cli时,启动本地环境,然后页面没有关闭,直接切换到线上环境,过一会会发起一个http://xxx/__webpack_hmr请求,会报cannot load原因:它属于nodejs中的一 ...

  3. Fundmentals in Stream Computing

    Spark programs are structured on RDDs: they invole reading data from stable storage into the RDD for ...

  4. 01_Nginx入门

    [Nginx概述] Nginx是一款轻量级的Web服务器/反向代理服务器及电子邮件(IMAP/POP3)代理服务器,并在一个BSD-Like协议下发行.其特点是占有内存少,并发能力强,事实上Nginx ...

  5. JS高级程序设计第三版——变量、作用域和内存问题

    JavaScript变量: 由于JavaScript变量松散类型的本质,决定了它只是在特定时间用于保存特定值的一个名字而已.由于不存在定义某个变量必须要保存何种数据类型值的规则,变量的值及其数据类型可 ...

  6. HTTP Status 500 - Handler processing failed; nested exception is java.lang.NoClassDefFoundError: javax/servlet/jsp/jstl/core/Config

    今天在用idea搭建spring mvc时候报了这个错误,缺少jstl的依赖包.由于是使用的spring-core的依赖,而spring-core里面是有jstl的.这就郁闷了,最后是在自己的pom. ...

  7. WPF&Silverlight5 常用功能差异

    一晃从Wpf转到sl也有半年多了,总想总结一下wpf和sl的差异,今天终于下笔. 首先来个整体图: 通过上图可以发现其实sl只是使用了wpf的一小部分,只是sl依赖的freamwork有很大部分都一样 ...

  8. easyui datagrid行编辑中数据联动

    easyui的datagrid中行内编辑使用数据联动.即:当编辑产品编号时,该行的产品名称自动根据产品编号显示出来. 在编辑中获取当前行的索引 function getRowIndex(target) ...

  9. js笔记 标签: javascript 2016-08-01 13:30 75人阅读 评论(0) 收藏

    typeof可以用来检测给定变量的数据类型,typeof是一个操作符而不是函数,所以圆括号可以省略. Undefined类型只有一个值,即特殊的undefined.在使用var声明变量但未对其加以初始 ...

  10. 使用 Sinamics S120 驱动脚本配置扩展报文

    为了传输故障代码.电流.温度等信息.通常需要使用扩展报文的方式来发送这些信息.在驱动数量较少的情况下,可以进行手动配置. 如果驱动数量很多,可以使用脚本script的方式来配置扩展报文. 驱动编号 注 ...