write函数过程解析
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函数过程解析的更多相关文章
- windows窗口过程函数名词解析
windows窗口过程函数名词解析 LRESULT CALLBACK WndProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam) 1. LR ...
- python+opencv->边缘提取与各函数参数解析
前情提要:作为刚入门机器视觉的小伙伴,第一节课学到机器视觉语法时觉得很难理解, 很多人家的经验,我发现都千篇一律,功能函数没解析,参数不讲解,就一个代码,所以在此将搜集的解析和案例拿出来汇总!!! 一 ...
- 曹工说Redis源码(5)-- redis server 启动过程解析,以及EventLoop每次处理事件前的前置工作解析(下)
曹工说Redis源码(5)-- redis server 启动过程解析,eventLoop处理事件前的准备工作(下) 文章导航 Redis源码系列的初衷,是帮助我们更好地理解Redis,更懂Redis ...
- 字串符相关 split() 字串符分隔 substring() 提取字符串 substr()提取指定数目的字符 parseInt() 函数可解析一个字符串,并返回一个整数。
split() 方法将字符串分割为字符串数组,并返回此数组. stringObject.split(separator,limit) 我们将按照不同的方式来分割字符串: 使用指定符号分割字符串,代码如 ...
- Delphi反汇编内部字符串处理函数/过程不完全列表
Delphi反汇编内部字符串处理函数/过程不完全列表 名称 参数 返回值 作用 等价形式 / 备注 _PStrCat EAX :目标字符串 EDX :源字符串 EAX 连接两个 Pascal 字符 ...
- Oracle中函数/过程返回结果集的几种方式
原文 Oracle中函数/过程返回结果集的几种方式 Oracle中函数/过程返回结果集的几种方式: 以函数return为例,存储过程只需改为out参数即可,在oracle 10g测试通过. ...
- NotePad++ delphi/Pascal函数过程列表插件
从cnpack上爬下来的 函数过程列表 点击下载
- MHA自动Failover过程解析(updated) 转
允许转载, 转载时请以超链接形式标明文章原始出处和网站信息 http://www.mysqlsystems.com/2012/03/figure-out-process-of-autofailover ...
- Generator函数语法解析
转载请注明出处: Generator函数语法解析 Generator函数是ES6提供的一种异步编程解决方案,语法与传统函数完全不同.以下会介绍一下Generator函数. 写下这篇文章的目的其实很简单 ...
随机推荐
- jxls实现基于excel模板的报表
此文章是基于 搭建Jquery+SpringMVC+Spring+Hibernate+MySQL平台 一. jar包介绍 1. commons-collections-3.2.jar 2. commo ...
- docker 容器启动并自启动redis
centos7.5 背景:每次开机后都要自动启动redis,也就是宿主机开机,启动容器,然后启动redis 按照网上的做法是:修改redis.conf ,修改redis的启动脚本(utils/...s ...
- git新建分支没有master分支,其他分支也看不到
git checkout -b dev git新建dev分支,发现切换到了dev分支,但是master分支没有了 git branch和git branch -a 都没有任何反应,看不到其他分支, g ...
- 第3章 css属性color的RGBA值
颜色之RGBA RGB是一种色彩标准,是由红(R).绿(G).蓝(B)的变化以及相互叠加来得到各式各样的颜色.RGBA是在RGB的基础上增加了控制alpha透明度的参数. 语法: color:rgba ...
- HTML5 localstorage和session操作
setStorage={ getLocal : function(key){ //获得localStorage里面的值 var storage = window.localStorage; if(st ...
- Luogu1261: 服务器储存信息问题
题面 传送门 Sol 我们可以考虑每种\(rank\)的点\(u\)会被哪些点\(v\)感兴趣 如果\(dis[u][v]<\)所有满足\(rank\)大于\(rank[u]\)的点到\(v\) ...
- vue学习笔记(一)
一.MVC 和 MVVM 的区别 MVC: Model(模型)应用程序中用于处理应用程序数据逻辑的部分(通常模型对象负责在数据库中存取数据). View(视图)显示数据(通常视图是依据模型数据创建的) ...
- 主动驱动事件执行--createEvent
1. createEvent(eventType)参数:eventType 共5种类型: Events :包括所有的事件. HTMLEvents:包括 'abort', 'b ...
- Revit
log file Windows Vista or Windows 7:%LOCALAPPDATA%\Autodesk\Revit\Autodesk Revit 2016\Journals
- 自学git心得-2
趁着最近还没忙起来,抓紧更新一下学习心得. 现在的情景是,我们已经在本地创建了一个Git仓库,又想在GitHub创建一个Git仓库,并且让这两个仓库进行远程同步,这样,GitHub上的仓库既可以作为备 ...