HotSpotVM JNI 实现浅析-程序员宅基地

技术标签: #实现浅析  JavaVM  

最近想看下HotSpotVM是怎么找到一个native方法的实现的,例如Thread.start0FileChannelImpl.transferTo0,最后发现是两种不同的方式。

nativeLookup

通过JNI规范可以知道,JVM通过加载动态链接库,将native方法链接过去,具体native方法解析成哪个符号,是按下面的约定来的,

Dynamic linkers resolve entries based on their names. A native method
name is concatenated from the following components:

  • the prefix Java_
  • a mangled fully-qualified class name
  • an underscore (“_”) separator
  • a mangled method name
  • for overloaded native methods, two underscores (“__”) followed by the mangled argument signature

HotspotVM对这部分的实现在nativeLookup.cpp中,

address NativeLookup::lookup(methodHandle method, bool& in_base_library, TRAPS) {
    

  // 调用has_native_function
  if (!method->has_native_function()) {
    

    // lookup_base
    address entry = lookup_base(method, in_base_library, CHECK_NULL);

    // 调用set_native_function
    method->set_native_function(entry,
      methodOopDesc::native_bind_event_is_interesting);

    // -verbose:jni printing
    if (PrintJNIResolving) {
    
      ResourceMark rm(THREAD);
      tty->print_cr("[Dynamic-linking native method %s.%s ... JNI]",
        Klass::cast(method->method_holder())->external_name(),
        method->name()->as_C_string());
    }
  }

  return method->native_function();
}
address NativeLookup::lookup_base(methodHandle method, bool& in_base_library, TRAPS) {
    
  address entry = NULL;
  ResourceMark rm(THREAD);

  entry = lookup_entry(method, in_base_library, THREAD);
  if (entry != NULL) return entry;

  // standard native method resolution has failed.  Check if there are any
  // JVM TI prefixes which have been applied to the native method name.
  entry = lookup_entry_prefixed(method, in_base_library, THREAD);
  if (entry != NULL) return entry;

  // Native function not found, throw UnsatisfiedLinkError
  THROW_MSG_0(vmSymbols::java_lang_UnsatisfiedLinkError(),
              method->name_and_sig_as_C_string());
}
// Check all the formats of native implementation name to see if there is one
// for the specified method.
address NativeLookup::lookup_entry(methodHandle method, bool& in_base_library, TRAPS) {
    
  address entry = NULL;
  in_base_library = false;
  // Compute pure name
  char* pure_name = pure_jni_name(method);

  // Compute argument size
  int args_size = 1                             // JNIEnv
                + (method->is_static() ? 1 : 0) // class for static methods
                + method->size_of_parameters(); // actual parameters


  // 1) Try JNI short style
  entry = lookup_style(method, pure_name, "",        args_size, true,  in_base_library, CHECK_NULL);
  if (entry != NULL) return entry;

  // Compute long name
  char* long_name = long_jni_name(method);

  // 2) Try JNI long style
  entry = lookup_style(method, pure_name, long_name, args_size, true,  in_base_library, CHECK_NULL);
  if (entry != NULL) return entry;

  // 3) Try JNI short style without os prefix/suffix
  entry = lookup_style(method, pure_name, "",        args_size, false, in_base_library, CHECK_NULL);
  if (entry != NULL) return entry;

  // 4) Try JNI long style without os prefix/suffix
  entry = lookup_style(method, pure_name, long_name, args_size, false, in_base_library, CHECK_NULL);

  return entry; // NULL indicates not found
}
char* NativeLookup::pure_jni_name(methodHandle method) {
    
  stringStream st;
  // Prefix
  st.print("Java_");
  // Klass name
  mangle_name_on(&st, method->klass_name());
  st.print("_");
  // Method name
  mangle_name_on(&st, method->name());
  return st.as_string();
}

char* NativeLookup::long_jni_name(methodHandle method) {
    
  // Signature ignore the wrapping parenteses and the trailing return type
  stringStream st;
  Symbol* signature = method->signature();
  st.print("__");
  // find ')'
  int end;
  for (end = 0; end < signature->utf8_length() && signature->byte_at(end) != ')'; end++);
  // skip first '('
  mangle_name_on(&st, signature, 1, end);
  return st.as_string();
}
address NativeLookup::lookup_style(methodHandle method, char* pure_name, const char* long_name, int args_size, bool os_style, bool& in_base_library, TRAPS) {
    
  address entry;
  // Compute complete JNI name for style
  stringStream st;
  if (os_style) os::print_jni_name_prefix_on(&st, args_size);
  st.print_raw(pure_name);
  st.print_raw(long_name);
  if (os_style) os::print_jni_name_suffix_on(&st, args_size);
  char* jni_name = st.as_string();

  // If the loader is null we have a system class, so we attempt a lookup in
  // the native Java library. This takes care of any bootstrapping problems.
  // Note: It is critical for bootstrapping that Java_java_lang_ClassLoader_00024NativeLibrary_find
  // gets found the first time around - otherwise an infinite loop can occure. This is
  // another VM/library dependency
  Handle loader(THREAD,
                instanceKlass::cast(method->method_holder())->class_loader());
  if (loader.is_null()) {
    
    entry = lookup_special_native(jni_name);
    if (entry == NULL) {
    

       // 查找本地动态链接库
       entry = (address) os::dll_lookup(os::native_java_library(), jni_name);

    }
    if (entry != NULL) {
    
      in_base_library = true;
      return entry;
    }
  }

  // Otherwise call static method findNative in ClassLoader
  KlassHandle   klass (THREAD, SystemDictionary::ClassLoader_klass());
  Handle name_arg = java_lang_String::create_from_str(jni_name, CHECK_NULL);

  JavaValue result(T_LONG);
  JavaCalls::call_static(&result,
                         klass,
                         vmSymbols::findNative_name(),
                         vmSymbols::classloader_string_long_signature(),
                         // Arguments
                         loader,
                         name_arg,
                         CHECK_NULL);
  entry = (address) (intptr_t) result.get_jlong();

  if (entry == NULL) {
    
    // findNative didn't find it, if there are any agent libraries look in them
    AgentLibrary* agent;
    for (agent = Arguments::agents(); agent != NULL; agent = agent->next()) {
    
      entry = (address) os::dll_lookup(agent->os_lib(), jni_name);
      if (entry != NULL) {
    
        return entry;
      }
    }
  }

  return entry;
}

最后便是找到本地动态链接库,调用os::dll_lookup查找符号表。先看查找本地动态链接库的逻辑,os::native_java_library

static void* _native_java_library = NULL;

void* os::native_java_library() {
    
  if (_native_java_library == NULL) {
    
    char buffer[JVM_MAXPATHLEN];
    char ebuf[1024];

    // Try to load verify dll first. In 1.3 java dll depends on it and is not
    // always able to find it when the loading executable is outside the JDK.
    // In order to keep working with 1.2 we ignore any loading errors.
    dll_build_name(buffer, sizeof(buffer), Arguments::get_dll_dir(), "verify");
    dll_load(buffer, ebuf, sizeof(ebuf));

    // Load java dll
    dll_build_name(buffer, sizeof(buffer), Arguments::get_dll_dir(), "java");
    _native_java_library = dll_load(buffer, ebuf, sizeof(ebuf));
    if (_native_java_library == NULL) {
    
      vm_exit_during_initialization("Unable to load native library", ebuf);
    }

#if defined(__OpenBSD__)
    // Work-around OpenBSD's lack of $ORIGIN support by pre-loading libnet.so
    // ignore errors
    dll_build_name(buffer, sizeof(buffer), Arguments::get_dll_dir(), "net");
    dll_load(buffer, ebuf, sizeof(ebuf));
#endif
  }
  static jboolean onLoaded = JNI_FALSE;
  if (onLoaded) {
    
    // We may have to wait to fire OnLoad until TLS is initialized.
    if (ThreadLocalStorage::is_initialized()) {
    
      // The JNI_OnLoad handling is normally done by method load in
      // java.lang.ClassLoader$NativeLibrary, but the VM loads the base library
      // explicitly so we have to check for JNI_OnLoad as well
      const char *onLoadSymbols[] = JNI_ONLOAD_SYMBOLS;
      JNI_OnLoad_t JNI_OnLoad = CAST_TO_FN_PTR(
          JNI_OnLoad_t, dll_lookup(_native_java_library, onLoadSymbols[0]));
      if (JNI_OnLoad != NULL) {
    
        JavaThread* thread = JavaThread::current();
        ThreadToNativeFromVM ttn(thread);
        HandleMark hm(thread);
        jint ver = (*JNI_OnLoad)(&main_vm, NULL);
        onLoaded = JNI_TRUE;
        if (!Threads::is_supported_jni_version_including_1_1(ver)) {
    
          vm_exit_during_initialization("Unsupported JNI version");
        }
      }
    }
  }
  return _native_java_library;
}

dll_build_name的时候,因为各个平台的动态链接库文件格式不同,例如上面加载的java动态链接库,在Win下是java.dll文件,而Linux下则是libjava.so,Linux平台下的这个逻辑在os_linux.cpp中实现,

void os::dll_build_name(char* buffer, size_t buflen,
                        const char* pname, const char* fname) {
    
  // Copied from libhpi
  const size_t pnamelen = pname ? strlen(pname) : 0;

  // Quietly truncate on buffer overflow.  Should be an error.
  if (pnamelen + strlen(fname) + 10 > (size_t) buflen) {
    
      *buffer = '\0';
      return;
  }

  if (pnamelen == 0) {
    
    snprintf(buffer, buflen, "lib%s.so", fname);
  } else if (strchr(pname, *os::path_separator()) != NULL) {
    
    int n;
    char** pelements = split_path(pname, &n);
    for (int i = 0 ; i < n ; i++) {
    
      // Really shouldn't be NULL, but check can't hurt
      if (pelements[i] == NULL || strlen(pelements[i]) == 0) {
    
        continue; // skip the empty path values
      }
      snprintf(buffer, buflen, "%s/lib%s.so", pelements[i], fname);
      if (file_exists(buffer)) {
    
        break;
      }
    }
    // release the storage
    for (int i = 0 ; i < n ; i++) {
    
      if (pelements[i] != NULL) {
    
        FREE_C_HEAP_ARRAY(char, pelements[i], mtInternal);
      }
    }
    if (pelements != NULL) {
    
      FREE_C_HEAP_ARRAY(char*, pelements, mtInternal);
    }
  } else {
    
    snprintf(buffer, buflen, "%s/lib%s.so", pname, fname);
  }
}

动态链接库文件格式不同,dll_lookup查找符号表的实现肯定也不同,Linux下的实现还是在os_linux.cpp中,

/*
 * glibc-2.0 libdl is not MT safe.  If you are building with any glibc,
 * chances are you might want to run the generated bits against glibc-2.0
 * libdl.so, so always use locking for any version of glibc.
 */
void* os::dll_lookup(void* handle, const char* name) {
    
  pthread_mutex_lock(&dl_mutex);
  void* res = dlsym(handle, name);
  pthread_mutex_unlock(&dl_mutex);
  return res;
}

dlsym是Linux的系统调用,参考man

Thread.start0

Thread.java的开头有这么几行代码,

    /* Make sure registerNatives is the first thing <clinit> does. */
    private static native void registerNatives();
    static {
    
        registerNatives();
    }

根据上面的规则,在libjava.so中应该存在Java_java_lang_Thread_registerNatives这样的方法,那么这个方法在哪?不是在OpenJDK的hotspot子项目下,而是在jdk子项目下,src/share/native/java/lang/Thread.c

static JNINativeMethod methods[] = {
    
    {
    "start0",           "()V",        (void *)&JVM_StartThread},
    {
    "stop0",            "(" OBJ ")V", (void *)&JVM_StopThread},
    {
    "isAlive",          "()Z",        (void *)&JVM_IsThreadAlive},
    {
    "suspend0",         "()V",        (void *)&JVM_SuspendThread},
    {
    "resume0",          "()V",        (void *)&JVM_ResumeThread},
    {
    "setPriority0",     "(I)V",       (void *)&JVM_SetThreadPriority},
    {
    "yield",            "()V",        (void *)&JVM_Yield},
    {
    "sleep",            "(J)V",       (void *)&JVM_Sleep},
    {
    "currentThread",    "()" THD,     (void *)&JVM_CurrentThread},
    {
    "countStackFrames", "()I",        (void *)&JVM_CountStackFrames},
    {
    "interrupt0",       "()V",        (void *)&JVM_Interrupt},
    {
    "isInterrupted",    "(Z)Z",       (void *)&JVM_IsInterrupted},
    {
    "holdsLock",        "(" OBJ ")Z", (void *)&JVM_HoldsLock},
    {
    "getThreads",        "()[" THD,   (void *)&JVM_GetAllThreads},
    {
    "dumpThreads",      "([" THD ")[[" STE, (void *)&JVM_DumpThreads},
    {
    "setNativeName",    "(" STR ")V", (void *)&JVM_SetNativeThreadName},
};

JNIEXPORT void JNICALL
Java_java_lang_Thread_registerNatives(JNIEnv *env, jclass cls)
{
    
    (*env)->RegisterNatives(env, cls, methods, ARRAY_LENGTH(methods));
}

看代码发现,并没有一个Java_java_lang_Thread_start0这样的方法,但是在Java_java_lang_Thread_registerNatives中调用了RegisterNatives,这货干嘛用的?先看它的定义,在jni.h中,是JNINativeInterface_中的一个函数指针(C中都是通过这种方式实现接口抽象的),

struct JNINativeInterface_ {
    
    ...
    jint (JNICALL *RegisterNatives)
      (JNIEnv *env, jclass clazz, const JNINativeMethod *methods,
       jint nMethods);
    ...
}

具体实现在jni.cpp中,

struct JNINativeInterface_ jni_NativeInterface = {
    
    ...
    jni_RegisterNatives,
    ...
}
JNI_ENTRY(jint, jni_RegisterNatives(JNIEnv *env, jclass clazz,
                                    const JNINativeMethod *methods,
                                    jint nMethods))
  JNIWrapper("RegisterNatives");
#ifndef USDT2
  DTRACE_PROBE4(hotspot_jni, RegisterNatives__entry, env, clazz, methods, nMethods);
#else /* USDT2 */
  HOTSPOT_JNI_REGISTERNATIVES_ENTRY(
                                    env, clazz, (void *) methods, nMethods);
#endif /* USDT2 */
  jint ret = 0;
  DT_RETURN_MARK(RegisterNatives, jint, (const jint&)ret);

  KlassHandle h_k(thread, java_lang_Class::as_klassOop(JNIHandles::resolve_non_null(clazz)));

  for (int index = 0; index < nMethods; index++) {
    
    const char* meth_name = methods[index].name;
    const char* meth_sig = methods[index].signature;
    int meth_name_len = (int)strlen(meth_name);

    // The class should have been loaded (we have an instance of the class
    // passed in) so the method and signature should already be in the symbol
    // table.  If they're not there, the method doesn't exist.
    TempNewSymbol  name = SymbolTable::probe(meth_name, meth_name_len);
    TempNewSymbol  signature = SymbolTable::probe(meth_sig, (int)strlen(meth_sig));

    if (name == NULL || signature == NULL) {
    
      ResourceMark rm;
      stringStream st;
      st.print("Method %s.%s%s not found", Klass::cast(h_k())->external_name(), meth_name, meth_sig);
      // Must return negative value on failure
      THROW_MSG_(vmSymbols::java_lang_NoSuchMethodError(), st.as_string(), -1);
    }

    // 注册本地方法
    bool res = register_native(h_k, name, signature,
                               (address) methods[index].fnPtr, THREAD);
    if (!res) {
    
      ret = -1;
      break;
    }
  }
  return ret;
JNI_END
static bool register_native(KlassHandle k, Symbol* name, Symbol* signature, address entry, TRAPS) {
    
  methodOop method = Klass::cast(k())->lookup_method(name, signature);
  if (method == NULL) {
    
    ResourceMark rm;
    stringStream st;
    st.print("Method %s name or signature does not match",
             methodOopDesc::name_and_sig_as_C_string(Klass::cast(k()), name, signature));
    THROW_MSG_(vmSymbols::java_lang_NoSuchMethodError(), st.as_string(), false);
  }
  if (!method->is_native()) {
    
    // trying to register to a non-native method, see if a JVM TI agent has added prefix(es)
    method = find_prefixed_native(k, name, signature, THREAD);
    if (method == NULL) {
    
      ResourceMark rm;
      stringStream st;
      st.print("Method %s is not declared as native",
               methodOopDesc::name_and_sig_as_C_string(Klass::cast(k()), name, signature));
      THROW_MSG_(vmSymbols::java_lang_NoSuchMethodError(), st.as_string(), false);
    }
  }

  if (entry != NULL) {
    

    // 调用set_native_function
    method->set_native_function(entry,
      methodOopDesc::native_bind_event_is_interesting);

  } else {
    
    method->clear_native_function();
  }
  if (PrintJNIResolving) {
    
    ResourceMark rm(THREAD);
    tty->print_cr("[Registering JNI native method %s.%s]",
      Klass::cast(method->method_holder())->external_name(),
      method->name()->as_C_string());
  }
  return true;
}

有没有发现最后调用set_native_function似曾相识,是的在nativeLookup::lookup中也会做相同的事情。回去看lookup的代码,你会发现,假如我们先使用了RegisterNatives的方式注册了本地方法,那么是不需要走到lookup_base里面去查找动态链接库了。set_native_function的定义是在methodOop.hpp

class methodOopDesc : public oopDesc {
    
 friend class methodKlass;
 ...
  // native function (used for native methods only)
  enum {
    
    native_bind_event_is_interesting = true
  };
  address native_function() const                {
     return *(native_function_addr()); }
  address critical_native_function();

  // Must specify a real function (not NULL).
  // Use clear_native_function() to unregister.
  void set_native_function(address function, bool post_event_flag);
  bool has_native_function() const;
  void clear_native_function();
  ...
}

也就是说每一个Java的native方法,最后会调用哪个本地方法,其实是被放到methodOopDesc::native_function中的,不管是按约定名称解析的方式,还是RegisterNatives的方式。其实在JNI规范中已经给出了RegisterNatives方式的说明,

The programmer can also call the JNI function RegisterNatives() to register the native methods associated with a class. The RegisterNatives() function is particularly useful with statically linked functions.

相对于查找动态链接库的方式,这是一种静态链接的方式。

回过头去看下给Thread.start0注册的本地方法JVM_StartThread,在jvm.cpp中实现,

JVM_ENTRY(void, JVM_StartThread(JNIEnv* env, jobject jthread))
  JVMWrapper("JVM_StartThread");
  JavaThread *native_thread = NULL;

  // We cannot hold the Threads_lock when we throw an exception,
  // due to rank ordering issues. Example:  we might need to grab the
  // Heap_lock while we construct the exception.
  bool throw_illegal_thread_state = false;

  // We must release the Threads_lock before we can post a jvmti event
  // in Thread::start.
  {
    
    // Ensure that the C++ Thread and OSThread structures aren't freed before
    // we operate.
    MutexLocker mu(Threads_lock);

    // Since JDK 5 the java.lang.Thread threadStatus is used to prevent
    // re-starting an already started thread, so we should usually find
    // that the JavaThread is null. However for a JNI attached thread
    // there is a small window between the Thread object being created
    // (with its JavaThread set) and the update to its threadStatus, so we
    // have to check for this
    if (java_lang_Thread::thread(JNIHandles::resolve_non_null(jthread)) != NULL) {
    
      throw_illegal_thread_state = true;
    } else {
    
      // We could also check the stillborn flag to see if this thread was already stopped, but
      // for historical reasons we let the thread detect that itself when it starts running

      jlong size =
             java_lang_Thread::stackSize(JNIHandles::resolve_non_null(jthread));
      // Allocate the C++ Thread structure and create the native thread.  The
      // stack size retrieved from java is signed, but the constructor takes
      // size_t (an unsigned type), so avoid passing negative values which would
      // result in really large stacks.
      size_t sz = size > 0 ? (size_t) size : 0;
      native_thread = new JavaThread(&thread_entry, sz);

      // At this point it may be possible that no osthread was created for the
      // JavaThread due to lack of memory. Check for this situation and throw
      // an exception if necessary. Eventually we may want to change this so
      // that we only grab the lock if the thread was created successfully -
      // then we can also do this check and throw the exception in the
      // JavaThread constructor.
      if (native_thread->osthread() != NULL) {
    
        // Note: the current thread is not being used within "prepare".
        native_thread->prepare(jthread);
      }
    }
  }

  if (throw_illegal_thread_state) {
    
    THROW(vmSymbols::java_lang_IllegalThreadStateException());
  }

  assert(native_thread != NULL, "Starting null thread?");

  if (native_thread->osthread() == NULL) {
    
    // No one should hold a reference to the 'native_thread'.
    delete native_thread;
    if (JvmtiExport::should_post_resource_exhausted()) {
    
      JvmtiExport::post_resource_exhausted(
        JVMTI_RESOURCE_EXHAUSTED_OOM_ERROR | JVMTI_RESOURCE_EXHAUSTED_THREADS,
        "unable to create new native thread");
    }
    THROW_MSG(vmSymbols::java_lang_OutOfMemoryError(),
              "unable to create new native thread");
  }

  Thread::start(native_thread);

JVM_END

FileChannelImpl.transferTo0

FileChannelImpl.transferTo0则是通过动态链接的方式实现,具体代码实现在src/solaris/native/sun/nio/ch/FileChannelImpl.c中,

JNIEXPORT jlong JNICALL
Java_sun_nio_ch_FileChannelImpl_transferTo0(JNIEnv *env, jobject this,
                                            jint srcFD,
                                            jlong position, jlong count,
                                            jint dstFD)
{
    
#if defined(__linux__)
    off64_t offset = (off64_t)position;
    jlong n = sendfile64(dstFD, srcFD, &offset, (size_t)count);
    if (n < 0) {
    
        if (errno == EAGAIN)
            return IOS_UNAVAILABLE;
        if ((errno == EINVAL) && ((ssize_t)count >= 0))
            return IOS_UNSUPPORTED_CASE;
        if (errno == EINTR) {
    
            return IOS_INTERRUPTED;
        }
        JNU_ThrowIOExceptionWithLastError(env, "Transfer failed");
        return IOS_THROWN;
    }
    return n;
#elif defined (__solaris__)
    sendfilevec64_t sfv;
    size_t numBytes = 0;
    jlong result;

    sfv.sfv_fd = srcFD;
    sfv.sfv_flag = 0;
    sfv.sfv_off = (off64_t)position;
    sfv.sfv_len = count;

    result = sendfilev64(dstFD, &sfv, 1, &numBytes);

    /* Solaris sendfilev() will return -1 even if some bytes have been
     * transferred, so we check numBytes first.
     */
    if (numBytes > 0)
        return numBytes;
    if (result < 0) {
    
        if (errno == EAGAIN)
            return IOS_UNAVAILABLE;
        if (errno == EOPNOTSUPP)
            return IOS_UNSUPPORTED_CASE;
        if ((errno == EINVAL) && ((ssize_t)count >= 0))
            return IOS_UNSUPPORTED_CASE;
        if (errno == EINTR)
            return IOS_INTERRUPTED;
        JNU_ThrowIOExceptionWithLastError(env, "Transfer failed");
        return IOS_THROWN;
    }
    return result;
#elif defined(__APPLE__)
    off_t numBytes;
    int result;

    numBytes = count;

#ifdef __APPLE__
    result = sendfile(srcFD, dstFD, position, &numBytes, NULL, 0);
#endif

    if (numBytes > 0)
        return numBytes;

    if (result == -1) {
    
        if (errno == EAGAIN)
            return IOS_UNAVAILABLE;
        if (errno == EOPNOTSUPP || errno == ENOTSOCK || errno == ENOTCONN)
            return IOS_UNSUPPORTED_CASE;
        if ((errno == EINVAL) && ((ssize_t)count >= 0))
            return IOS_UNSUPPORTED_CASE;
        if (errno == EINTR)
            return IOS_INTERRUPTED;
        JNU_ThrowIOExceptionWithLastError(env, "Transfer failed");
        return IOS_THROWN;
    }

    return result;
#else
    return IOS_UNSUPPORTED_CASE;
#endif
}
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/kisimple/article/details/44204201

智能推荐

试用WPS(不是金山WPS,是World Programming System)-程序员宅基地

文章浏览阅读231次。虽然自己开发了个山寨版的web-based SAS IDE,仍旧比SAS Editor好不了哪里去:只用语法高亮(CodePress已实现)而没有代码辅助功能。本来打算把MDK Editor(一个javascript写的在线代码编辑器,语法高亮和代码辅助功能兼而有之)hack一下,整合进SIDE的。自从上个礼拜发现WPS后,这个念头打消了。 WPS采用了强大的Eclipse开发平台,是我见到的..._world pragramming

Android P中的AVB校验(二)_avbtool-程序员宅基地

文章浏览阅读2k次。Android O/P 版本以来,谷歌加入了A/B system的特性,此时ramdisk和system是一起放在同一个system.img镜像中的。而系统起来之后也就不存在system分区了,而是直接把system镜像挂载到/根目录上。那么这个操作是怎么进行的呢?system.img默认是需要使能dm-verity来挂载的,那么这就涉及到如何使能dm-verity来挂载/根分区。还有一个重要的点,如果我们想要禁用dm-verity功能又要如何操作?这又涉及更深的层次,如果。_avbtool

目标跟踪学习笔记_4(particle filter初探3)_a boosted particle filter: multitarget detection a-程序员宅基地

文章浏览阅读3.6k次。目标跟踪学习笔记_4(particle filter初探3) ( 注:本文为这学期一个作业,关于粒子滤波的介绍在前面2篇博客中已提到过,即:目标跟踪学习笔记_2(particle filter初探1)目标跟踪学习笔记_3(particle filter初探2)前面2篇博客已经提到当粒子数增加时会内存报错,后面又仔细查了下程序,是代码方面的问题。所以本次的代码与前几次改变比较_a boosted particle filter: multitarget detection and tracking论文阅读

[通信原理]擾碼(Scramble)與解擾(Descramble)_scramble原理-程序员宅基地

文章浏览阅读1w次,点赞4次,收藏19次。前言:在数字信号的传输中,发送机往往要加扰码器,相对应的接收端要加解扰器。扰乱器起的作用是:如果输入数字序列是短周期的,将把它按照某种规律变换(扰乱)为长周期,并且使输出序列(以后将称为信道序列)中的过判决点(在二进制中即过零点)接近码总数的一半。解扰器在接收端将被扰乱后的序列还原为输入发送机的数字序列(消息)。最简单的扰码方法是在输入数字序列上加一个最长线性移位寄存器序列,使前者变..._scramble原理

数论--筛法变形--ACM-ICPC 2018 南京赛区网络预赛 J.Sum_acm-icpc 2018 南京赛区网络预赛 sum-程序员宅基地

文章浏览阅读184次。https://nanti.jisuanke.com/t/30999f[i]表示将i分解为2个数相乘的方案数,i = a*b,且a,b均不能有平方因子。求f[i]前缀和n = p1^a1 * p2^a2 * p3^a3 * ... * pk^ak= a * b若a1 = 1,则a可能有p1,可能没有,f[n] = f[n / p1] * 2a1 = 2,则a必须选p1(a,..._acm-icpc 2018 南京赛区网络预赛 sum

XML,HTML中输出<>等特殊符号_xml中<>怎么写-程序员宅基地

文章浏览阅读1.1k次。在values - strings里命名字符串时,有时需要输出若不加任何操作则会报错,如 ... element is not closed。需进行转义输出,将    > 换为 >  >= 换为 ≥即可正常输出。原文链接:http://www.cnblogs.com/xudong-bupt/p/3909416.html_xml中<>怎么写

随便推点

4.WML编程-程序员宅基地

文章浏览阅读621次。[转_摘编]WML的元素通常有一个首标签、内容、其它元素及一个尾标签组成。也就说,单独的标签是一个元素,成对出现的标签与其包含的内容也构成一个元素。由于元素牵涉及标签,标签又涉及属性。1 卡片、卡片组及其属性1.1 共有属性WML元素的共有属性主要有3个:id、class和xml:lang属性。WML的所有元素都有两个核心属性,即标示(id)和类(class)属性。它们主要用于

[精品毕设]基于Python的疫苗查询预约服务系统接种-程序员宅基地

文章浏览阅读24次。该项目含有源码、文档、PPT、配套开发软件、软件安装教程、项目发布教程、包运行成功!

那些在线的Jupyter_jupyterhub在线-程序员宅基地

文章浏览阅读534次。Jupyter如果你是python的初学者, 非常推荐Jupyter.Juypter是一个交互式的编程环境, 号称The Notebook has support for over 40 programming languages,including Python, R, Julia, and Scala.支持超过40种语言, 其中包括Python, R, Julia和Scala. 虽然..._jupyterhub在线

QT 的Moc Uic Rcc有什么用(360ui移植到eclipse)_322rcc-程序员宅基地

文章浏览阅读1.6k次。Moc 主要是用于 将类中包含Q_OBJECT 的类里面的 信号 和槽 处理 成moc_xxx文件。因为QT 信号与 slot 这一部分不是纯C++的 所以要通过 moc来将 Q_OBJECT的部分包含的信息编译进去。当不使用信号这些的时候 去掉Q_OBJECT 也可以编译通过。只是无法处理信号这些。Rcc 主要是将 rc资源文件 转成字节流形式 的CPP文件_322rcc

TPCTF maze——WP_create string tab and init strings-程序员宅基地

文章浏览阅读418次,点赞9次,收藏6次。先解包反编译chal.pyc核心逻辑在maze.so,chal.pyc导入了maze里面的run函数执行,maze是用Cython编译的用strings查看可以看出是cython3.0.5版本编译的。_create string tab and init strings

查找android对应的navtive so函数地址_获取 native 方法对应so-程序员宅基地

文章浏览阅读2.6k次。第一是从so文件里面查找。导出表以及符号。另外一种就是 一般会在JNI_OnLoad里面注册 native函数。native函数定义如下。typedef struct { const char* name; const char* signature; void* fnPtr;} JNINativeMethod;jint RegisterNatives(jc..._获取 native 方法对应so

推荐文章

热门文章

相关标签