本文介绍了mmap标志MAP_UNINITIALIZED未定义的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

mmap()文档提到了标志MAP_UNINITIALIZED,但该标志似乎未定义.在Centos7和Xenial上进行了尝试,这两个发行版都没有在sys/mman.h中定义该标志.令人惊讶的是,互联网似乎没有意识到这一点.这是什么故事?

我从文档中了解到,仅在嵌入式或低安全性设备上才使用该标志,但这并不意味着不应该定义该标志...如何在可移植代码中使用它? Google公开了在不支持的情况下定义为0的代码,但在我的情况下根本没有定义.

解决方案

为了了解如何处理#include <sys/mman.h>未定义MAP_UNINITIALIZED的事实,了解内核接口的方式是有帮助的定义.

要构建内核模块,您将需要用于构建内核的内核头文件,以获取要为其构建模块的确切版本的内核.由于您希望在用户空间中运行,因此不需要它们.

为用户空间定义内核API的标头主要位于/usr/include/linux/usr/include/asm中(请参见 ="nofollow noreferrer">此).这些标头最重要的使用者之一是C标准库例如glibc,该库必须针对这些标头的某些版本构建.由于linux内核API是向后兼容的,因此您可能会针对比运行的内核更旧的这些标头构建glibc(或其他库实现).我绝不是各种发行版如何分发glibc的专家,但我的印象是,定义其用户空间API的内核头文件通常是glibc所针对的版本.

最后,glibc通过同样安装在/usr/include下的标头(例如/usr/include/sys)定义其API.我不知道为使用旧的或较新的glibc标头构建的应用程序提供什么(如果有的话)向后或向前兼容,但是我猜测当向后可比性被破坏时,库.so版本号会增加. >

因此,现在我们可以理解您的问题是,glibc标头实际上并未为您尝试的发行版/版本定义MAP_UNINITIALIZED.

但是,Linux内核API公开了MAP_UNINITIALIZED,如此补丁所示.如果glibc标头没有为您定义,则可以使用linux内核API标头,如果定义了#include <linux/mman.h>,则可以使用.请注意,除其他外,您仍然需要#include <sys/mman.h>以获得mmap的原型.

如果您的linux内核API标头没有定义MAP_UNINITIALIZED,但是您拥有实现它的内核版本,则可以自己定义它:

 #define MAP_UNINITIALIZED 0x4000000    

您不必担心自己使用的是比构建glibc更高的更新"标头,因为mmap的glibc实现非常薄:

#include <sys/types.h>
#include <sys/mman.h>
#include <errno.h>
#include <sysdep.h>

#ifndef MMAP_PAGE_SHIFT
#define MMAP_PAGE_SHIFT 12
#endif

__ptr_t
__mmap (__ptr_t addr, size_t len, int prot, int flags, int fd, off_t offset)
{
  if (offset & ((1 << MMAP_PAGE_SHIFT) - 1))
    {
      __set_errno (EINVAL);
      return MAP_FAILED;
    }
  return (__ptr_t) INLINE_SYSCALL (mmap2, 6, addr, len, prot, flags, fd,
                                   offset >> MMAP_PAGE_SHIFT);
}

weak_alias (__mmap, mmap)

它只是将您的标志直接传递到内核.

mmap() docs mentions flag MAP_UNINITIALIZED, but the flag doesn't seem to be defined.Tried on Centos7, and Xenial, neither distro has the flag defined in sys/mman.h as alleged.
Astonishingly, the internet doesn't seem to be aware of this. What's the story?

Edit: I understand from the docs that the flag is only honoured on embedded or low-security devices, but that doesn't mean the flag shouldn't be defined... How do you use it in portable code? Google has revealed code where it is defined as 0 in cases where not supported, except in my cases it's not defined at all.

解决方案

In order to understand what to do about the fact that #include <sys/mman.h> does not define MAP_UNINITIALIZED, it is helpful to understand how the interface to the kernel is defined.

To build a kernel module, you will need the kernel headers used to build the kernel for the exact version of the kernel for which you wish to build the module. As you wish to run in userspace, you won't need these.

The headers that define the kernel API for userspace are largely in /usr/include/linux and /usr/include/asm (see this for how they are generated). One of the more important consumers of these headers is the C standard library, e.g., glibc, which must be built against some version of these headers. Since the linux kernel API is backwards compatible, you may have a glibc (or other library implementation) built against an older version of these headers than the kernel you are running. I'm by no means an expert on how all the various distros distribute glibc, but it is my impression that the kernel headers defining its userspace API are generally the version that glibc has been built against.

Finally, glibc defines its API through headers also installed under /usr/include such as /usr/include/sys. I don't know exactly what, if any, backward or forward compatibility is provided for applications built with older or newer glibc headers, but I'm guessing that the library .so version number gets bumped when backward comparability would be broken.

So now we can understand your problem to be that the glibc headers don't actually define MAP_UNINITIALIZED for the distros/versions that you tried.

However, the linux kernel API has exposed MAP_UNINITIALIZED, as this patch demonstrates. If the glibc headers don't define it for you, you can use the linux kernel API headers and #include <linux/mman.h> if this defines it. Note that you will still need to #include <sys/mman.h> in order to get the prototype for mmap, among other things.

If your linux kernel API headers don't define MAP_UNINITIALIZED but you have a kernel version that implements it, you can define it yourself:

 #define MAP_UNINITIALIZED 0x4000000    

You don't have to worry that you are effectively using "newer" headers than your glibc was built with, because the glibc implementation of mmap is very thin:

#include <sys/types.h>
#include <sys/mman.h>
#include <errno.h>
#include <sysdep.h>

#ifndef MMAP_PAGE_SHIFT
#define MMAP_PAGE_SHIFT 12
#endif

__ptr_t
__mmap (__ptr_t addr, size_t len, int prot, int flags, int fd, off_t offset)
{
  if (offset & ((1 << MMAP_PAGE_SHIFT) - 1))
    {
      __set_errno (EINVAL);
      return MAP_FAILED;
    }
  return (__ptr_t) INLINE_SYSCALL (mmap2, 6, addr, len, prot, flags, fd,
                                   offset >> MMAP_PAGE_SHIFT);
}

weak_alias (__mmap, mmap)

It is just passing your flags straight through to the kernel.

这篇关于mmap标志MAP_UNINITIALIZED未定义的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

10-13 23:37