想问下ServerSocket构造方法中的backlog参数最终在JVM中是怎么样生效的

chainhou 2013-12-29
大家好,最近在分析一个Tomcat通道相关的问题是,为了验证其backlog属性,因此看到了ServerSocket的源码。
ServerSocket包含这样一个构造方法,可以接收backlog参数
public ServerSocket(int port, int backlog, InetAddress bindAddr) throws IOException {
	setImpl();
	if (port < 0 || port > 0xFFFF)
	    throw new IllegalArgumentException(
		       "Port value out of range: " + port);
	if (backlog < 1)
	  backlog = 50;
	try {
	    bind(new InetSocketAddress(bindAddr, port), backlog);
	} catch(SecurityException e) {
	    close();
	    throw e;
	} catch(IOException e) {
	    close();
	    throw e;
	}
    }


相应的bind方法:
public void bind(SocketAddress endpoint, int backlog) throws IOException {
	...//此处省略一些内容
	if (backlog < 1)
	  backlog = 50;
	try {
	    SecurityManager security = System.getSecurityManager();
	    if (security != null)
		security.checkListen(epoint.getPort());
	    getImpl().bind(epoint.getAddress(), epoint.getPort());
	    getImpl().listen(backlog);
	    bound = true;
	} catch(SecurityException e) {
	    bound = false;
	    throw e;
	} catch(IOException e) {
	    bound = false;
	    throw e;
	}
    }

从上面可以看出最终调用了其实现类的listen方法。

该方法在类PlainSocketImpl类内,并且其调用了一个native方法,
 protected synchronized void listen(int count) throws IOException {
	socketListen(count);
    }
    private native void socketListen(int count)
	throws IOException;

看到native方法的时候,一般比较头大,相应的代码不太会找。就在OpenJDK相应的路径内找到同名的PlainSocketImpl.c,最终找到了如下内容:
/*
* Class: java_net_PlainSocketImpl
* Method: socketListen
* Signature: (I)V
*/
JNIEXPORT void JNICALL
Java_java_net_PlainSocketImpl_socketListen (JNIEnv *env, jobject this, jint count)
{
  /* this FileDescriptor fd field */
  jobject fdObj = (*env)->GetObjectField(env, this, psi_fdID);
  /* fdObj's int fd field */
  int fd;

  if (IS_NULL(fdObj)) {
    JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Socket closed");
    return;
  } else {
    fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
  }

  /*
    * Workaround for bugid 4101691 in Solaris 2.6. See 4106600.
    * If listen backlog is Integer.MAX_VALUE then subtract 1.
    */
  if (count == 0x7fffffff)
  count -= 1;

  if (JVM_Listen(fd, count) == JVM_IO_ERR) {
    NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", "Listen failed");
  }
}

一看其又调用了JVM_Listen,这个时候是真不知道怎么入手了,就开始按内容搜索,找到了下面的内容:
openjdk\hotspot\src\share\vm\prims\jvm.cpp
JVM_LEAF(jint, JVM_Listen(jint fd, jint count))
JVMWrapper2("JVM_Listen (0x%x)", fd);
//%note jvm_r6
return os::listen(fd, count);
JVM_END

一来对C的语法不熟悉,看到这个还是不知道如何找到最终的实现代码。
调试代码时如果跟到了JDK的native代码,这时候一般怎么下手找到关注的内容?

PS:R大的博客里有一系列关于了解学习JVM的好内容,但我没找到关于如何跟踪代码相关的,谁知道相关内容请告诉一下。就是在调试代码时如果跟到了JDK的native代码,这时候一般怎么下手找到关注的内容呢?
谢谢。
simpleman7210 2013-12-30
我看到楼主跟踪到了os::listen(),其实这已经很近了。os应该是个C++类,jvm为了平台移植性,在os类里封装了与平台相关的实现。我没有打开OpenJDK工程观察这块代码,不过可以判断接下来就是调用平台所在的本地库函数了。比如在Windows上很可能是调用Windows的API函数listen()。listen()源代码一般是看不到的,因为在DLL(动态链接库)中。
RednaxelaFX 2013-12-31
楼主所说的“跟踪”有两种,静态与动态。

静态就像楼主那样只靠读源码来分析。这种方式在对源码越熟悉的情况下越有效。换句话说,如果对源码不熟就直接硬啃,效果通常不太好。

动态就是在程序运行过程中去发现信息。通常这会意味着要使用调试器。要跟C++代码的话可以用你熟悉的native debugger,例如Windows上Visual Studio自带的调试器、WinDBG或者如果没源码要做汇编级调试时用OllyDbg之类;Linux上GDB是王道。我还被迫用过一段时间dbx但始终不顺手

楼主多半需要native debugger的入门教程。如果是跟到Java的native方法而且你知道该native方法在C/C++那边的入口在哪里的话,只要用native debugger在那个入口处设断点然后慢慢跟就好了,不麻烦。

======================================

HotSpot里,os是一个封装操作系统特定行为的静态类。
http://hg.openjdk.java.net/jdk7u/jdk7u/hotspot/file/tip/src/share/vm/runtime/os.hpp
class os: AllStatic {
  // ...
};

它里面的函数的具体实现大多在hotspot/src/os里。

具体到os::listen(),具体实现取决于你在看什么版本和什么平台,
在JDK7的HotSpot里,
Linux: http://hg.openjdk.java.net/jdk7u/jdk7u/hotspot/file/tip/src/os/linux/vm/os_linux.inline.hpp
inline int os::listen(int fd, int count) {
  return ::listen(fd, count);
}

也就是调用了listen()

Windows: http://hg.openjdk.java.net/jdk7u/jdk7u/hotspot/file/tip/src/os/windows/vm/os_windows.cpp
int os::listen(int fd, int count) {
  return ::listen(fd, count);
}

也就是调用了listen()
chainhou 2013-12-31
多谢R大和simpleman的回复。其实我主要是为了解决工作中遇到的一些问题。因为项目中用了一些开源产品,当测试发现问题时,就需要跟踪其源码,来确认问题。大学的时候其实也学过C++,也用过Visual Studio,但一直没学好,后来也没再学。
对于R大提到的native debugger,是需要在VS里把jvm代码加断点后以debug方式启动,之后java代码如果用到就会跳转过去是吗?
真心不懂。哎。
RednaxelaFX 2014-01-02
chainhou 写道
对于R大提到的native debugger,是需要在VS里把jvm代码加断点后以debug方式启动,之后java代码如果用到就会跳转过去是吗?

假定你用Visual Studio的debugger,那应该在JVM的代码上设断点,然后“附加”(attach)到你要调试的进程上,然后等断点触发。

问题是怎么才能让Visual Studio知道你要调试的jvm.dll里的代码跟你打开的JVM的源码是有关系的呢?最简单的办法就是那个jvm.dll是你自己build出来的,这样VS就肯定知道;不然的话请看各种入门教程
chainhou 2014-01-06
元旦期间请假了,一下没上网。谢谢R大耐心细致的回答。,祝新年快乐
Global site tag (gtag.js) - Google Analytics