Archive for the ‘Linux’ category

Linux中的虚拟地址转换物理地址实现

November 26th, 2014

Linux使用一种4级页表机制,即页全局目录(Page Global Directory)、页上级目录(Page Upper Directory)、页中间目录(Page Middle Directory)和页表(Page Table)。在这种分页机制下.

一个完整的线性地址被分为五部分:页全局目录、页上级目录、页中间目录、页表和偏移量,但是对于每个部分所占的位数则是不定的,这跟系统所在的体系架构有关。

Linux分别采用pgd_t、pud_t 、pmd_t和pte_t四种数据结构来表示页全局目录项、页上级目录项、页中间目录项和页表项。这四种数据结构本质上都是无符号长整型,Linux为了更严格数据类型检查,将无符号长整型分别封装成四种不同的页表项。

static void get_pgtable_macro(void)
{
	printk("PAGE_OFFSET = 0x%lx\n", PAGE_OFFSET);
	printk("PGDIR_SHIFT = %d\n", PGDIR_SHIFT);
	printk("PUD_SHIFT = %d\n", PUD_SHIFT);
	printk("PMD_SHIFT = %d\n", PMD_SHIFT);
	printk("PAGE_SHIFT = %d\n", PAGE_SHIFT);

	printk("PTRS_PER_PGD = %d\n", PTRS_PER_PGD);
	printk("PTRS_PER_PUD = %d\n", PTRS_PER_PUD);
	printk("PTRS_PER_PMD = %d\n", PTRS_PER_PMD);
	printk("PTRS_PER_PTE = %d\n", PTRS_PER_PTE);

	printk("PAGE_MASK = 0x%lx\n", PAGE_MASK);
}

static unsigned long vaddr2paddr(unsigned long vaddr)
{
	pgd_t *pgd;
	pud_t *pud;
	pmd_t *pmd;
	pte_t *pte;
	unsigned long paddr = 0;
        unsigned long page_addr = 0;
	unsigned long page_offset = 0;

	pgd = pgd_offset(current->mm, vaddr);
	printk("pgd_val = 0x%lx\n", pgd_val(*pgd));
	printk("pgd_index = %lu\n", pgd_index(vaddr));
	if (pgd_none(*pgd)) {
		printk("not mapped in pgd\n");
		return -1;
	}

	pud = pud_offset(pgd, vaddr);
	printk("pud_val = 0x%lx\n", pud_val(*pud));
	if (pud_none(*pud)) {
		printk("not mapped in pud\n");
		return -1;
	}

	pmd = pmd_offset(pud, vaddr);
	printk("pmd_val = 0x%lx\n", pmd_val(*pmd));
	printk("pmd_index = %lu\n", pmd_index(vaddr));
	if (pmd_none(*pmd)) {
		printk("not mapped in pmd\n");
		return -1;
	}

	pte = pte_offset_kernel(pmd, vaddr);
	printk("pte_val = 0x%lx\n", pte_val(*pte));
	printk("pte_index = %lu\n", pte_index(vaddr));
	if (pte_none(*pte)) {
		printk("not mapped in pte\n");
		return -1;
	}

	//页框物理地址机制 | 偏移量
	page_addr = pte_val(*pte) & PAGE_MASK;
	page_offset = vaddr & ~PAGE_MASK;
	paddr = page_addr | page_offset;
	printk("page_addr = %lx, page_offset = %lx\n", page_addr, page_offset);
        printk("vaddr = %lx, paddr = %lx\n", vaddr, paddr);

	return paddr;
}

通过代码我们发现线性地址寻值过程:pgd_t -> pud_t ->pmd_t ->  pte_t  

具体Dmesg打印信息:

dmesg.log

目前在X86_64环境下,虽然支持64位,但是系统实际上目前远远使用不了如此大的内存,真正使用的只有48位,即

0xffff000000000000

 

然后使用上面的宏函数进行移位操作,拼接出下一级的页表物理基地址。然后再和后面的pud,pmd,pte部分地址相加,得到下一级的物理基地址。直到最后拿到页框的物理地址与pte或,找到真实的物理地址。

参考:

arch/x86/include/asm/pgtable_64_types.h

 

RHEL系(Fedora,CentOS) rpm打包

November 6th, 2014

现在Linux下越来越倾向于打包二进制安装文件,rpm,deb都是这种类型。

但是作为内核开发人员我们要对程序进行调试,就要有debug-info package,但是默认内核人是不提供debug-info的包,所以需要我们自动生成。

一般我们要用root用户进行编译,但是编译rpm包,至少需要20GB的临时文件空间!

一键操作:make rpm ———–>自动身成rpmtree,kernel.spec文件,自动编译。

——————————

详细定制:

下面开始创建:

1.用所在的用户输入rpmdev-setuptree,然后会在该用户的目录下生成${HOME}/rpmbuild/SOURCES ,${HOME}/rpmbuild/SPECS 和 ${HOME}/rpmbuild/BUILD 。

2.在多数源玛目录下会自动带有kernel.spec的文件,将这个文件放到${HOME}/rpmbuild/SPECS中,将kernel-3.14.8.tar.gz 放入${HOME}/rpmbuild/SOURCES中,然后在SOURCE中放入.config,

默认生成的spec带着”%debug_package %{nil}” 这就意味着不会生成带debug_info的包。如果需要,我们就要注释掉。

cp .config ~/rpmbuild/SOURCES/config-`uname -m`-generic

3.切换到SPECS目录下。

建立所有内核配置:
rpmbuild -bb –target=`uname -m` kernel.spec

关闭指定的内核配置(为了更快的建立):
rpmbuild -bb –without <option> –target=`uname -m` kernel.spec

其中“ option ”的有效值包括xen、smp、up、pae、kdump、debug 和 debuginfo。指定 –without debug 会剔除内核中的调试代码,指定 –without debuginfo 会禁止建立 kernel-debuginfo 包。

只建立一个特定的内核:
rpmbuild -bb –with <option> –target=`uname -m` kernel.spec
“ option ”的有效值包括 xenonly、 smponly 和 baseonly 。

例如,只建立 kernel 和 kernel-devel 包的命令是:

rpmbuild -bb –with baseonly –without debuginfo –target=`uname -m` kernel.spec
建立时包含固件,用如下命令:

rpmbuild -bb –with baseonly –with firmware –without debuginfo  –target=`uname -m` kernel.spec
建立的过程需要很长时间。会在屏幕上打印大量的信息。这些信息可以被忽略,除非建立过程因为一个 error 而停止。如果成功完成建立过程,一个新的内核包会出现在 ~/rpmbuild/RPMS 目录。

 

参考:

https://fedoraproject.org/wiki/Building_a_custom_kernel
https://fedoraproject.org/wiki/How_to_create_an_RPM_package/zh-cn
http://blog.csdn.net/justlinux2010/article/details/9905425
http://www.linuxfly.org/post/130/

kdump 在Debian/Fedora/CentOS下的配置

November 1st, 2014

Update 2014/11/6

0.why we use kdump?

Kdump is a kernel crash dumping mechanism and is very reliable because the crash dump is captured from the context of a freshly booted kernel and not from the context of the crashed kernel. Kdump uses kexec to boot into a second kernel whenever system crashes. This second kernel, often called the crash kernel, boots with very little memory and captures the dump image.

The first kernel reserves a section of memory that the second kernel uses to boot. Kexec enables booting the capture kernel without going through the BIOS, so contents of the first kernel’s memory are preserved, which is essentially the kernel crash dump.
使用mce-inject注入fatal错误的时候,system会直接panic。不像UCNA,SRAO等可以通过mcelog,rasDamon查看。
system在进去machine check 后,内核调用,用户态下的mcelog,rasDamon都已经无法使用,导致dmesg信息MCE解码信息都无法查看。

kdump原理就是在dump kernel panic后,重新启动后利用kexec载入debug kernel 然后利用这个内核去dump出现panic错误的内核信息。

然后system使用正常kernel启动后,可以分析该dump文件,包括查看demsg/memory/PCIe等各种kernel信息等。
用来诊断kernel出现了哪些致命错误。
在本项目中主要利用该工具用来分析注入mce 错误产生的dumpcore信息。
————————————————————————–

首先我们分清要使用的系统:

1.Fedora Configure

由于Fedora的软件比较新,如果你用的是是Fedora 20

我们直接可以 yum install –enablerepo=fedora-debuginfo –enablerepo=updates-debuginfo kexec-tools crash kernel-debuginfo
下载安装好kdump crash kexec工具

2.然后我们在/etc/default/grub里面的

GRUB_CMDLINE_LINUX=”rd.lvm.lv=fedora/swap rd.md=0 rd.dm=0 $([ -x /usr/sbin/rhcrashkernel-param ] && /usr/sbin/rhcrashkerne l-param || 🙂 rd.luks=0 vconsole.keymap=us rd.lvm.lv=fedora/root rhgb quiet crashkernel=240M”
最后加入crashkernel=240M

这个预先留出的内存大小,在下次使用就被占用。
一般 1G——80M 2G——-160M 3G——–240M

最后更新grub :$grub2-mkconfig -o /boot/grub2/grub.cfg 即可

3.重新启动,选择该内核进入system

[root@localhost mce-inject-encode]# service kdump start
Redirecting to /bin/systemctl start kdump.service
[root@localhost mce-inject-encode]# systemctl status kdump
kdump.service - Crash recovery kernel arming
Loaded: loaded (/usr/lib/systemd/system/kdump.service; enabled)
Active: active (exited) since 一 2014-10-27 09:15:31 CST; 2 days ago
Process: 652 ExecStart=/usr/bin/kdumpctl start (code=exited, status=0/SUCCESS)
Main PID: 652 (code=exited, status=0/SUCCESS)
CGroup: /system.slice/kdump.service

10月 27 09:15:22 localhost.localdomain systemd[1]: Starting Crash recovery kernel arming...
10月 27 09:15:31 localhost.localdomain kdumpctl[652]: kexec: loaded kdump kernel
10月 27 09:15:31 localhost.localdomain kdumpctl[652]: Starting kdump: [OK]
10月 27 09:15:31 localhost.localdomain systemd[1]: Started Crash recovery kernel arming.
10月 29 09:29:36 localhost.localdomain systemd[1]: Started Crash recovery kernel arming.

4.开始等待panic错误,如果发现panic错误,system会立刻切换到crashkernel收集信息,收集完毕重新启动。

5.进入到system。键入到/var/crash文件夹下有通过kdump产生的文件:

[root@localhost 127.0.0.1-2014.10.29-09:59:07]# ls
vmcore vmcore-dmesg.txt
[root@localhost 127.0.0.1-2014.10.29-09:59:07]# vim vmcore-dmesg.txt

[175451.136453] Triggering MCE exception on CPU 0
[175451.136463] [Hardware Error]: MC0 Error:
[175451.136467] [Hardware Error]: Corrupted MC0 MCE info?
[175451.136469] [Hardware Error]: Error Status: System Fatal error.
[175451.136472] [Hardware Error]: CPU:0 (f:6b:2) MC0_STATUS[-|UE|-|PCC|-]: 0xb200000000000000
[175451.136475] [Hardware Error]: cache level: RESV, tx: INSN
[175451.136480] mce: [Hardware Error]: CPU 0: Machine Check Exception: 0 Bank 0: b200000000000000
[175451.136662] mce: [Hardware Error]: TSC 15f2d1714af92
[175451.136765] mce: [Hardware Error]: PROCESSOR 2:60fb2 TIME 1414547938 SOCKET 0 APIC 0 microcode 0
[175451.136933] [Hardware Error]: MC0 Error:
[175451.137020] [Hardware Error]: Corrupted MC0 MCE info?
[175451.137132] [Hardware Error]: Error Status: System Fatal error.
[175451.137249] [Hardware Error]: CPU:0 (f:6b:2) MC0_STATUS[-|UE|-|PCC|-]: 0xb200000000000000
[175451.137412] [Hardware Error]: cache level: RESV, tx: INSN
[175451.137431] mce: [Hardware Error]: Machine check: Invalid
[175451.137431] Kernel panic - not syncing: Fatal machine check on current CPU

通过vmcore-dmesg.txt 可以查看panic时的dmesg 。如果需要对panic core 进行debug的时候,需要
使用[root@localhost 127.0.0.1-2014.10.29-09:59:07]# crash vmcore /usr/lib/debug/lib/xxxxxx/vmlinux

PS:注意这个vmcore与你的debugcore 版本要一致否则无法进行crash!

创建kernel-debuginfo-devel
https://fedoraproject.org/wiki/Building_a_custom_kernel
https://fedoraproject.org/wiki/How_to_create_an_RPM_package/zh-cn
http://blog.csdn.net/justlinux2010/article/details/9905425
http://www.linuxfly.org/post/130/

具体操作看http://www.lizhaozhong.info/archives/1143

——————————————————————————————————

CentOS 7 Configure

CentOS 7 与 Fedora很类似,这个发行版在程序安装的时候就提供了kdump配置,为了我们手动安装,我们先手动关闭。

CentOS 7 与 Fedora操作类似,都是使用yum机制安装程序。在本项目中,我制作了kernel-3.14.8.rpm包,方便后期注入使用。

CentOS 7 使用了systemctl管理系统service ,当我们安装完成3.14.8 内核,与kdump-tools等工具链。
配置文件放在/etc/sysconfig/kdump中

当然我们要在/etc/default/grub中定义crashkernel的大小,重启以后,使用systemctl enable kdump.service 设置为开机启动。
systemctl start kdump.service 启动kdump.service 服务。
systemctl status kdump.service 可以查看该服务状态:

[root@localhost sysconfig]# systemctl status kdump.service -l
kdump.service - Crash recovery kernel arming
Loaded: loaded (/usr/lib/systemd/system/kdump.service; enabled)
Active: active (exited) since 一 2014-12-22 09:10:30 EST; 1 day 11h ago
Process: 809 ExecStart=/usr/bin/kdumpctl start (code=exited, status=0/SUCCESS)
Main PID: 809 (code=exited, status=0/SUCCESS)
CGroup: /system.slice/kdump.service

12月 22 09:10:27 localhost.localdomain kdumpctl[809]: cat: /sys/kernel/security/securelevel: No such file or directory
12月 22 09:10:30 localhost.localdomain kdumpctl[809]: kexec: loaded kdump kernel
12月 22 09:10:30 localhost.localdomain kdumpctl[809]: Starting kdump: [OK]
12月 22 09:10:30 localhost.localdomain systemd[1]: Started Crash recovery kernel arming.

当遇到panic事件后,kdump会转储故障内核,重新启动后,我们以可以在/var/crash下找到vmcore

具体加载vmcore方式是使用
[root@localhost 127.0.0.1-2014.12.22-09:09:51]# crash vmcore /boot/vmlinux-3.14.8.bz2

/boot/vmlinux-3.14.8.bz2 是我们自己打包的rpm包,带有debug symbol 的 镜像,可以用于debug vmcore

————————————
Debian Configure

因为在本项目中我们使用kernel 3.14.8,所以我们要使用crash 7.0.8.在Deibian 7.6中,默认的crash 6.0.6版本中
无法分析kernel 3.6以及以后内核的SLAB内存,crash载入dump文件,会出现以下错误信息:

please wait... (gathering kmem slab cache data)
crash: invalid structure member offset: kmem_cache_s_next
FILE: memory.c LINE: 7903 FUNCTION: kmem_cache_init()
[/usr/bin/crash] error trace: 466a99 =&gt; 47b44b =&gt; 491f5c =&gt; 547d43
547d43: OFFSET_verify+164
491f5c: (undetermined)
47b44b: vm_init+10841
466a99: main_loop+239

一个方法是我们可以在/etc/apt/source.list 中将wheezy改为jessie,可以顺利将crash升级到7.0.8

Debian Configure 有一个问题是需要手动配置crashkernel 内核地址

1. aptitude install kdump-tools crash kexec-tools makedumpfile

2.在 /etc/default/grub中修改为GRUB_CMDLINE_LINUX_DEFAULT=”crashkernel=128M quiet”

3.# update-grub2
4.里面的DEBUG_KERNEL也要对应产生的vmcore版本,进行crash时vmcore与这个debug_kernel must版本一致!

主要是在编译内核的目录下使用make deb-pkg 这时会产生专门针对3.14.8内核的dbg vmlinux,以及header

root@debian:/usr/src/kernels# ls
linux-3.14.8 linux-headers-3.14.8_3.14.8-2_amd64.deb linux-libc-dev_3.14.8-2_amd64.deb
linux-3.14.8.tar linux-image-3.14.8_3.14.8-2_amd64.deb
linux-firmware-image-3.14.8_3.14.8-2_amd64.deb linux-image-3.14.8-dbg_3.14.8-2_amd64.deb

然后安装这些生成的deb.
5.在/etc/default/kdump-tools中配置为

USE_KDUMP=1
KDUMP_SYSCTL="kernel.panic_on_oops=1"
KDUMP_KERNEL=/boot/vmlinuz-3.14.8
KDUMP_INITRD=/boot/initrd.img-3.14.8
KDUMP_COREDIR="/var/crash"
KDUMP_FAIL_CMD="reboot -f"
DEBUG_KERNEL=/usr/lib/debug/boot/vmlinux-3.14.8
MAKEDUMP_ARGS="-c -d 31"

//在/etc/default/kexec中配置为:
root@debian:/usr/src/kernels# cat /etc/default/kexec
# Defaults for kexec initscript
# sourced by /etc/init.d/kexec and /etc/init.d/kexec-load

# Load a kexec kernel (true/false)
LOAD_KEXEC=true

# Kernel and initrd image
KERNEL_IMAGE="/boot/vmlinuz-3.14.8"
INITRD="/boot/initrd.img-3.14.8"

# If empty, use current /proc/cmdline
APPEND=""

# Load the default kernel from grub config (true/false)
USE_GRUB_CONFIG=false

6.重新启动:

root@debian:/usr/src/kernels# service kdump-tools restart
[ ok ] unloaded kdump kernel.
[ ok ] loaded kdump kernel.

7.kdump就开始捕捉错误。

8.关于分析dump的操作crash

This GDB was configured as "x86_64-unknown-linux-gnu"...

      KERNEL: kernel_link
    DUMPFILE: dump.201410311601  [PARTIAL DUMP]
        CPUS: 2
        DATE: Fri Oct 31 16:01:04 2014
      UPTIME: 00:09:57
LOAD AVERAGE: 0.21, 0.11, 0.07
       TASKS: 209
    NODENAME: debian
     RELEASE: 3.14.8
     VERSION: #2 SMP Fri Oct 31 14:38:20 CST 2014
     MACHINE: x86_64  (2199 Mhz)
      MEMORY: 1.7 GB
       PANIC: "Kernel panic - not syncing: Fatal machine check on current CPU"
         PID: 0
     COMMAND: "swapper/1"
        TASK: ffff88006d7ea8e0  (1 of 2)  [THREAD_INFO: ffff88006d7ee000]
         CPU: 1
       STATE: TASK_RUNNING (PANIC)

crash> help

*              files          mach           repeat         timer
alias          foreach        mod            runq           tree
ascii          fuser          mount          search         union
bt             gdb            net            set            vm
btop           help           p              sig            vtop
dev            ipcs           ps             struct         waitq
dis            irq            pte            swap           whatis
eval           kmem           ptob           sym            wr
exit           list           ptov           sys            q
extend         log            rd             task           

crash version: 7.0.8    gdb version: 7.6
For help on any command above, enter "help <command>".
For help on input options, enter "help input".
For help on output options, enter "help output".

crash>

参考:

http://wiki.incloudus.com/display/DOC/Debian+-+Kdump
http://fedoraproject.org/wiki/How_to_use_kdump_to_debug_kernel_crashes
http://debian-handbook.info/browse/stable/sect.kernel-compilation.html

C/S模型下Server 中fork()的健壮性

October 30th, 2014

C/S模型下Server 下编程非常依赖fork()子进程去处理具体的业务逻辑。 每当accept()接收到一个TCP连接时,主服务器进程就fork一个子服务器进程。子服务器进程调用相应的函数,通过client_fd(连接套接字)对客户端发来的网络请求进程处理;由于客户端的请求已被子服务进程处理,那么主服务器进程就什么也不做,通过sockfd(监听套接字)继续循环等待新的网络请求。

..........
while (1) {
	sin_size = sizeof(struct sockaddr_in);
	if ((client_fd = accept(sockfd, (struct sockaddr *)&remote_addr, &sin_size)) == -1) {
		my_error("accept", errno, __LINE__);
		continue;
	}

	if ((pid = fork()) == 0) {
		close(sockfd);
		process_client_request(client_fd);
		close(client_fd);
		exit(0);
	} else if (pid > 0)
		close(client_fd);
	else
		my_error("fork", errno, __LINE__);
}

每个文件都有一个引用计数,该引用计数表示当前系统内的所有进程打开该文件描述符的个数。套接字是一种特殊的文件,当然也有引用计数。 当fork执行后,由于子进程复制了父进程的资源,所以子进程也拥有这两个套接字描述符,则此时sockfd和client_fd的引用计数都为2。只有当子进程处理完客户请求时,client_fd的引用计数才由于close函数而变为0。 但是这里存在一个严重的问题:如果客户端意外退出,就会导致server子进程成为僵尸进程。我们设想如果server非常繁忙,就会导致system出现大量的zombie进程!system会应该耗尽系统资源而宕机! 这是因为

当一个子进程先于父进程结束运行时,它与其父进程之间的关联还会保持到父进程也正常地结束运行,或者父进程调用了wait才告终止。

子进程退出时,内核将子进程置为僵尸状态,它只保留最小的一些内核数据结构,以便父进程查询子进程的退出状态。

进程表中代表子进程的数据项是不会立刻释放的,虽然不再活跃了,可子进程还停留在系统里,因为它的退出码还需要保存起来以备父进程中后续的wait调用使用。

所以我们要处理zombie进程! 两种方式:

  • 调用wait或者waitpid函数查询子进程退出状态,此方法父进程会被挂起。
  • 如果不想让父进程挂起,可以在父进程中加入一条语句:signal(SIGCHLD,SIG_IGN);表示父进程忽略SIGCHLD信号,该信号是子进程退出的时候向父进程发送的。

当子进程终止时会给父进程发送SIGCHLD信号,因此我们可以利用信号处理函数捕获这个信号并对僵死进程进行处理。我们知道在父进程中调用wait函数可以防止先于父进程终止的子进程编程僵死进程

void sig_zchild(int signo)
{
	pid_t pid;
	int stat;

	pid = wait(&stat);
        printf("child %d terminated\n", pid);
	return;
}

修改服务器程序,在accept函数调用之前调用signal函数:

	if(listen(sockfd, BACKLOG) == -1) {

		printf("listen error!\n");
		exit(1);
	}

	if (signal(SIGCHLD, sig_zchild) == SIG_ERR) {
		printf("signal error!\n");
		exit(1);
	}

	while (1) {

		sin_size = sizeof(struct sockaddr_in);
		if ((client_fd = accept(sockfd, (struct sockaddr *)&remote_addr,
&sin_size)) == -1) {

			printf("accept error!\n");
			continue;
		}
		…… ……
	}//while

但是这个程序仍存在一个问题:当多个子进程同时退出时,会导致父进程无法同时处理SIGCHILD信号,导致有部分子进程zombie。

void sig_zchild(int signo)
{
 pid_t pid;
 int stat;

 while ((pid = waitpid(-1, &stat, WNOHANG)) > 0)
 printf("child %d terminated\n", pid);

 return;
}

使用while可以等待SIGCHILD信号,直到处理完成! 信号在内核中也是存放在队列中!

 

 

参考:http://www.cnblogs.com/mickole/p/3187770.html

VFS Data Structure关系(3) – 跨文件系统的文件操作分析

October 28th, 2014

通过对VFS 以及文件系统的分析,我们现在来分析一下跨文件系统的文件传输

http://www.lizhaozhong.info/archives/1080

http://www.lizhaozhong.info/archives/1110

比如从一个floppy传输到harddisk(ext2——>ext4)

首先我们明确一个概念:一切皆文件!

在linux中,无论是普通的文件,还是特殊的目录、设备等,VFS都将它们同等看待成文件,通过统一的文件操作界面来对它们进行操作。操作文件时需先打开;打开文件时,VFS会知道该文件对应的文件系统格式;当VFS把控制权传给实际的文件系统时,实际的文件系统再做出具体区分,对不同的文件类型执行不同的操作。

原理:将ext2格式的磁盘上的一个文件a.txt拷贝到ext4格式的磁盘上,命名为b.txt。这包含两个过程,对a.txt进行读操作,对b.txt进行写操作。读写操作前,需要先打开文件。由前面的分析可知,打开文件时,VFS会知道该文件对应的文件系统格式,以后操作该文件时,VFS会调用其对应的实际文件系统的操作方法。所以,VFS调用vfat的读文件方法将a.txt的数据读入内存;在将a.txt在内存中的数据映射mmap()到b.txt对应的内存空间后,VFS调用ext4的写文件方法将b.txt写入磁盘;从而实现了最终的跨文件系统的复制操作。

关于mmap()使用

fs/open.c

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);
    if (fd >= 0) {
        struct file *f = do_filp_open(dfd, tmp, &op);
        if (IS_ERR(f)) {
            put_unused_fd(fd);
            fd = PTR_ERR(f);
        } else {
            fsnotify_open(f);
            fd_install(fd, f);
        }
    }
    putname(tmp);
    return fd;
}

下面我们来分析下linux-3.14.8的内核(Not Finished!)

sys_open()
    |
    |------do_sys_open()
    |           |
    |           |------get_unused_fd_flags()
                |
                |------do_filp_open()
                            |
                            |----path_openat()
                            |       |
                            |       |----link_path_walk()