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. Nginx 504错误总结

    Nginx 504错误(Gateway time-out  网关超时)的含义是所请求的网关没有请求到,简单来说就是没有请求到可以执行的PHP-CGI. 一般看来, 这种情况可能是由于nginx默认的f ...

  2. hdu 1712 (分组背包)

    http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=17676 这个问题让我对分组背包更清晰了一点,主要是这个问题: 使用一维数组的 ...

  3. unity3d之如何控制人物移动、旋转和动画播放

    代码源自噩梦射手,记录一下方便后续使用,顺便将老师的解释给备注上去_(:з」∠)_ using UnityEngine; using UnitySampleAssets.CrossPlatformIn ...

  4. 第8章 CSS3中的变形与动画(上)

    变形--旋转 rotate() 旋转rotate()函数通过指定的角度参数使元素相对原点进行旋转.它主要在二维空间内进行操作,设置一个角度值,用来指定旋转的幅度.如果这个值为正值,元素相对原点中心顺时 ...

  5. lib 和 dll

    dll 和 lib只有在windows平台才会出现,老是忘记他们的区别和联系,记录一下,以备不时之需,也加深一下印象.  在弄懂两者的区别之前,需要知道两个概念: static library 和 d ...

  6. <Android 应用 之路> 百度地图API使用(3)

    前言 上一篇讲解并实践了百度地图基本显示,地图类型,实时交通图,热力图,地图控制和手势控制,今天看下在地图上添加view和覆盖物. 地图Logo不允许遮挡,可通过mBaiduMap.setPaddin ...

  7. C#中的Sealed和J#中的Final比较(转载)

    Sealed与Final修饰符其实并不是一个语言平台的产物,他们有着各自所属的语言环境,但这两个关键字都是.Net平台中不可或缺的,那么二者用法几何,随本文一探究竟. 一.Sealed sealed ...

  8. Fundmentals in Stream Computing

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

  9. 【Python】Python3基本语法入门学习

    0.Python概述 1.First Word Game 2.变量与字符串 3.improved game 4.Python数据类型 5.常用操作符 6.分支与循环 7.列表 8.元组 9.字符串内置 ...

  10. 使用spring aop遇到的坑

    1.aop 切点配置无误但只对控制器无效. 检测你的aop开启配置是否放在了spring配置文件中,如果是请把它移到mvc配置文件中. 我们知道当spring项目使用了spring mvc时,项目是存 ...