0 前言
《Linux系统调用》整体介绍了系统调用,本文重点分析其中mmap的实现与使用方法。
1 定义
1.1 x86
(1)linux-2.6.31-
采用老式定义方法:
asmlinkage long sys_mmap(unsigned long addr, unsigned long len,unsigned long prot, unsigned long flags,unsigned long fd, unsigned long off)
{long error;struct file *file;error = -EINVAL;if (off & ~PAGE_MASK)goto out;error = -EBADF;file = NULL;flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);if (!(flags & MAP_ANONYMOUS)) {file = fget(fd);if (!file)goto out;} down_write(¤t->mm->mmap_sem);error = do_mmap_pgoff(file, addr, len, prot, flags, off >> PAGE_SHIFT);up_write(¤t->mm->mmap_sem);if (file)fput(file);
out:return error;
}
// @file: linux-2.6.31/arch/x86/kernel/sys_x86_64.c
(2) linux-2.6.32
改用SYSCALL_DEFINE6宏进行定义:
SYSCALL_DEFINE6(mmap, unsigned long, addr, unsigned long, len,unsigned long, prot, unsigned long, flags,unsigned long, fd, unsigned long, off)
{long error;struct file *file;error = -EINVAL;if (off & ~PAGE_MASK)goto out;error = -EBADF;file = NULL;flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);if (!(flags & MAP_ANONYMOUS)) {file = fget(fd);if (!file)goto out;}down_write(¤t->mm->mmap_sem);error = do_mmap_pgoff(file, addr, len, prot, flags, off >> PAGE_SHIFT);up_write(¤t->mm->mmap_sem);if (file)fput(file);
out:return error;
}
// @file: linux-2.6.32/arch/x86/kernel/sys_x86_64.c
(3)linux-2.6.33 ~ 4.16
linux-2.6.33新增sys_mmap_pgoff()并将主要移致其中:
SYSCALL_DEFINE6(mmap, unsigned long, addr, unsigned long, len,unsigned long, prot, unsigned long, flags,unsigned long, fd, unsigned long, off)
{long error;error = -EINVAL;if (off & ~PAGE_MASK)goto out;error = sys_mmap_pgoff(addr, len, prot, flags, fd, off >> PAGE_SHIFT);
out:return error;
}
// @file: linux-2.6.33/arch/x86/kernel/sys_x86_64.c
(4)linux-4.17+
sys_mmap_pgoff更名为ksys_mmap_pgoff:
SYSCALL_DEFINE6(mmap, unsigned long, addr, unsigned long, len,unsigned long, prot, unsigned long, flags,unsigned long, fd, unsigned long, off)
{long error;error = -EINVAL;if (off & ~PAGE_MASK)goto out;error = ksys_mmap_pgoff(addr, len, prot, flags, fd, off >> PAGE_SHIFT);
out:return error;
}
// @file: linux-4.17/arch/x86/kernel/sys_x86_64.c
1.2 arm
旧内核采用汇编定义,后面逐渐改用C定义:
(1)linux-2.6.12 ~ 2.6.32
/** Note: off_4k (r5) is always units of 4K. If we can't do the requested* offset, we return EINVAL.*/
sys_mmap2:
#if PAGE_SHIFT > 12tst r5, #PGOFF_MASKmoveq r5, r5, lsr #PAGE_SHIFT - 12streq r5, [sp, #4]beq do_mmap2mov r0, #-EINVALRETINSTR(mov,pc, lr)
#elsestr r5, [sp, #4]b do_mmap2
#endif
// @file: linux-2.6.12/arch/arm/kernel/entry-common.S
可见具体实现为do_mmap2,它是一个C函数:
/* common code for old and new mmaps */
inline long do_mmap2(unsigned long addr, unsigned long len,unsigned long prot, unsigned long flags,unsigned long fd, unsigned long pgoff)
{int error = -EINVAL;struct file * file = NULL;flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);if (flags & MAP_FIXED && addr < FIRST_USER_ADDRESS)goto out;error = -EBADF;if (!(flags & MAP_ANONYMOUS)) {file = fget(fd);if (!file)goto out;}down_write(¤t->mm->mmap_sem);error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);up_write(¤t->mm->mmap_sem);if (file)fput(file);
out:return error;
}
// @file: linux-2.6.12/arch/arm/kernel/sys_arm.c
(2)linux-2.6.33+
将具体实现由do_mmap2改为sys_mmap_pgoff:
/** Note: off_4k (r5) is always units of 4K. If we can't do the requested* offset, we return EINVAL.*/
sys_mmap2:
#if PAGE_SHIFT > 12tst r5, #PGOFF_MASKmoveq r5, r5, lsr #PAGE_SHIFT - 12streq r5, [sp, #4]beq sys_mmap_pgoffmov r0, #-EINVALmov pc, lr
#elsestr r5, [sp, #4]b sys_mmap_pgoff
#endif
ENDPROC(sys_mmap2)
// @file: linux-2.6.33/arch/arm/kernel/entry-common.S
1.3 arm64
(1)linux-4.18
采用老式定义方法:
asmlinkage long sys_mmap(unsigned long addr, unsigned long len,unsigned long prot, unsigned long flags,unsigned long fd, off_t off)
{if (offset_in_page(off) != 0)return -EINVAL;return ksys_mmap_pgoff(addr, len, prot, flags, fd, off >> PAGE_SHIFT);
}
// @file: linux-4.18/arch/arm64/kernel/sys.c
(2)linux-4.19+
改用SYSCALL_DEFINE6宏定义: