免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 981 | 回复: 0
打印 上一主题 下一主题

java native [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2007-05-10 10:53 |只看该作者 |倒序浏览

一. 什么是Native Method
   简单地讲,一个Native Method就是一个java调用非java代码的接口。一个Native Method是这样一个java的方法:该方法的实现由非java语言实现,比如C。这个特征并非java所特有,很多其它的编程语言都有这一机制,比如在C++中,你可以用extern "C"告知C++编译器去调用一个C的函数。
   "A native method is a Java method whose implementation is provided by non-java code."
   在定义一个native method时,并不提供实现体(有些像定义一个java interface),因为其实现体是由非java语言在外面实现的。,下面给了一个示例:   
    public class IHaveNatives
    {
      native public void Native1( int x ) ;
      native static public long Native2() ;
      native synchronized private float Native3( Object o ) ;
      native void Native4( int[] ary ) throws Exception ;
    }
    这些方法的声明描述了一些非java代码在这些java代码里看起来像什么样子(view).
    标识符native可以与所有其它的java标识符连用,但是abstract除外。这是合理的,因为native暗示这些方法是有实现体的,只不过这些实现体是非java的,但是abstract却显然的指明这些方法无实现体。native与其它java标识符连用时,其意义同非Native Method并无差别,比如native static表明这个方法可以在不产生类的实例时直接调用,这非常方便,比如当你想用一个native method去调用一个C的类库时。上面的第三个方法用到了native synchronized,JVM在进入这个方法的实现体之前会执行同步锁机制(就像java的多线程。)
    一个native method方法可以返回任何java类型,包括非基本类型,而且同样可以进行异常控制。这些方法的实现体可以制一个异常并且将其抛出,这一点与java的方法非常相似。当一个native method接收到一些非基本类型时如Object或一个整型数组时,这个方法可以访问这非些基本型的内部,但是这将使这个native方法依赖于你所访问的java类的实现。有一点要牢牢记住:我们可以在一个native method的本地实现中访问所有的java特性,但是这要依赖于你所访问的java特性的实现,而且这样做远远不如在java语言中使用那些特性方便和容易。
    native method的存在并不会对其他类调用这些本地方法产生任何影响,实际上调用这些方法的其他类甚至不知道它所调用的是一个本地方法。JVM将控制调用本地方法的所有细节。需要注意当我们将一个本地方法声明为final的情况。用java实现的方法体在被编译时可能会因为内联而产生效率上的提升。但是一个native final方法是否也能获得这样的好处却是值得怀疑的,但是这只是一个代码优化方面的问题,对功能实现没有影响。
    如果一个含有本地方法的类被继承,子类会继承这个本地方法并且可以用java语言重写这个方法(这个似乎看起来有些奇怪),同样的如果一个本地方法被fianl标识,它被继承后不能被重写。
   本地方法非常有用,因为它有效地扩充了jvm.事实上,我们所写的java代码已经用到了本地方法,在sun的java的并发(多线程)的机制实现中,许多与操作系统的接触点都用到了本地方法,这使得java程序能够超越java运行时的界限。有了本地方法,java程序可以做任何应用层次的任务。
二.为什么要使用Native Method
   java使用起来非常方便,然而有些层次的任务用java实现起来不容易,或者我们对程序的效率很在意时,问题就来了。
   与java环境外交互:
   有时java应用需要与java外面的环境交互。这是本地方法存在的主要原因,你可以想想java需要与一些底层系统如操作系统或某些硬件交换信息时的情况。本地方法正是这样一种交流机制:它为我们提供了一个非常简洁的接口,而且我们无需去了解java应用之外的繁琐的细节。
   与操作系统交互:
   JVM支持着java语言本身和运行时库,它是java程序赖以生存的平台,它由一个解释器(解释字节码)和一些连接到本地代码的库组成。然而不管怎 样,它毕竟不是一个完整的系统,它经常依赖于一些底层(underneath在下面的)系统的支持。这些底层系统常常是强大的操作系统。通过使用本地方法,我们得以用java实现了jre的与底层系统的交互,甚至JVM的一些部分就是用C写的,还有,如果我们要使用一些java语言本身没有提供封装的操作系统的特性时,我们也需要使用本地方法。
    Sun's Java
    Sun的解释器是用C实现的,这使得它能像一些普通的C一样与外部交互。jre大部分是用java实现的,它也通过一些本地方法与外界交互。例如:类java.lang.Thread 的 setPriority()方法是用java实现的,但是它实现调用的是该类里的本地方法setPriority0()。这个本地方法是用C实现的,并被植入JVM内部,在Windows 95的平台上,这个本地方法最终将调用Win32 SetPriority() API。这是一个本地方法的具体实现由JVM直接提供,更多的情况是本地方法由外部的动态链接库(external dynamic link library)提供,然后被JVM调用。
三.JVM怎样使Native Method跑起来:
    我们知道,当一个类第一次被使用到时,这个类的字节码会被加载到内存,并且只会回载一次。在这个被加载的字节码的入口维持着一个该类所有方法描述符的list,这些方法描述符包含这样一些信息:方法代码存于何处,它有哪些参数,方法的描述符(public之类)等等。
    如果一个方法描述符内有native,这个描述符块将有一个指向该方法的实现的指针。这些实现在一些DLL文件内,但是它们会被操作系统加载到java程序的地址空间。当一个带有本地方法的类被加载时,其相关的DLL并未被加载,因此指向方法实现的指针并不会被设置。当本地方法被调用之前,这些DLL才会被加载,这是通过调用java.system.loadLibrary()实现的。
   
   最后需要提示的是,使用本地方法是有开销的,它丧失了java的很多好处。如果别无选择,我们可以选择使用本地方法。



CONTENTS

The goal for this chapter is to introduce you to Java's native methods. If you are new to Java, you may not know what native methods are, and even if you are an experienced Java developer, you may not have had a reason to learn more about native methods. At the conclusion of this chapter you should have a better understanding of what native methods are, when and why you may want to use them, and the consequences of using them. You should also have a basic understanding of how native methods work. You will then be more than ready to tackle the next three chapters, which dive into the nitty-gritty details of Java's Native Methods.
What Is a Native Method?
Simply put, a native method is the Java interface to non-Java code. It is Java's link to the "outside world." More specifically, a native method is a Java method whose implementation is provided by non-Java code, most likely C (see Figure 30.1). This feature is not special to Java. Most languages provide some mechanism to call routines written in another language. In C++, you must use the extern "C" stmt to signal that the C++ compiler is making a call to C functions. It is common to see the qualifier pascal in many C compilers to signal that the calling convention should be done in a Pascal convention, rather than a C convention. FORTRAN and Pascal have similar facilities, as do most other languages.
Figure 30.1 : A native method is a Java method whose implementation is provided by non-java code.

In Java, this is done via native methods. In your Java class, you mark the methods you wish to implement outside of Java with the native method modifier-much like you would use the public or static modifiers. Then, rather than supplying the method's body, you simply place a semicolon in its place. As an example, the following class defines a variety of native methods:
public class IHaveNatives
{
  native public void Native1( int x ) ;
  native static public long Native2() ;
  native synchronized private float Native3( Object o ) ;
  native void Native4( int[] ary ) throws Exception ;
}
This sample class shows a number of possible native methods. As you may have noticed, native methods look much like any other Java method, except a single semicolon is in the place of the method body. Naturally, the body of the method is implemented outside of Java. What you basically define is the interface into this external method. This method declaration describes the Java view of some foreign code.
The only thing special about this declaration is that the keyword native is used as a modifier. Every other Java method modifier can be used along with native, except abstract. This is logical, because the native modifier implies that an implementation exists, and the abstract modifier insists that there is no implementation. Your native methods can be static methods, thus not requiring the creation of an object (or instance of a class). This is often convenient when using native methods to access an existing C-based library. Naturally, native methods can limit their visibility with the public, private, private protected, protected, or unspecified default access. Native methods can also be synchronized (
see Chapter 7
, "Concurrency and Synchronization"). In the case of a synchronized native method, the Java VM will perform the monitor locking prior to entering the native method implementation code. So, as in Java, the developer is not burdened with doing the actual monitor locking and unlocking.
The example uses a variety (although not all) of types. This is because a native method can be passed any Java type. There is no special procedure within the Java code to pass data to the native method. However, the developer of native methods must be careful that his native methods behave properly when manipulating Java datatypes. Native methods do not undergo the same kinds of checking as a Java method, and they can easily corrupt a Java datatype if care is not taken (
see Chapter 31
, "The Native Method Interface").
A native method can accept and return any of the Java types-including class types. Of course, the power of exception handling is also available to native methods. The implementation of the native method can create and throw exceptions similar to a Java method. When a native method receives complex types, such as class types (such as Object in the example) or array types (such as the int[] in the example), it has access to the contents of those types. However, the method used to access the contents may vary depending on the Java implementation being used. The major point to remember is that you can access all the Java features from your native implementation code, but it may be implementation-dependent and will surely not be as convenient or easy as it can be done from Java.
The presence of native methods does not affect how other classes call those methods. The caller does not even realize it is calling a native method, so no special code is generated, and the calling convention is the same as for any other method-the calling depends on the method being virtual or static. The Java virtual machine will handle all the details to make the call in the native method implementation. One minor exception may be with the methods marked with the final modifier. The Java implementation may take advantage of a final method and choose to inline its code. It would be doubtful that this could be achieved with a native final method, but this is an optimization issue, not one of functionality. When a class containing native methods is subclassed, the subclass will inherit the native method and also will have the capability of overriding the native method-even with a Java method (that is, the overridden method can be implemented in Java). If a native method is also marked with the final modifier, a subclass is still prevented from overriding it.
Native methods are very powerful, because they effectively extend the Java virtual machine. In fact, your Java code already uses native methods. In the current implementation from Sun, native methods are used in many places to interface to the underlying operating system. This enables a Java program to go beyond the confines of the Java Runtime. With native methods, a Java program can virtually do any application level task.
Uses for Native Methods
Java is a wonderful language to use. However, there are times when you either must interface with existing code, can't express the task in Java, or need the absolute best performance.
Accessing Outside the Java Environment
There are times where a Java application (or applet) must communicate with the environment outside of Java. This is, perhaps, the main reason for the existence of native methods. For starters, the Java implementation will need to communicate with the underlying system. That underlying system may be an operating system such as Solaris or Win32, or it may be a Web browser, or it may be custom hardware, such as a PDA, Set-top-device, and so forth. Regardless of what is under Java, there must be a mechanism to communicate with that system. At some point in a Java program, there will be that point where Java meets the outside world, an interface between Java and non-Java worlds. Native methods provide a simple clean approach to providing this interface without burdening the rest of the Java application with special knowledge.
Accessing the Operating System
The Java virtual machine describes a system that the Java program can rely on to be there. This virtual machine supports the Java Language and its runtime library. It may be composed of an interpreter or can be libraries linked to native code. Regardless of its form, it is not a complete system and often relies on an existing system underneath to provide a lot of support. More than likely, a full-fledged operating system, such as Solaris or Win32, resides beneath it. The use of native methods enables the Java Runtime to be written in Java yet have access to the underlying operating system, or even the parts of the Java virtual machine that may be written in a language such as C. Further, if a Java feature does not encapsulate an operating system feature needed by an application, native methods can be used to access this feature.
Embedded Java
It is conceivable to see a Java virtual machine embedded inside another program. Several WWW browsers come to mind. Perhaps this enclosing program is not implemented in Java. The Java Runtime may need to access the enclosing program for services to support the Java environment. Once again, native methods provide a clean interface for this access to the surrounding program. Furthermore, the vendor of the program may wish to expose some features of the program to a Java applet. The vendor would simply need to create a set of Java classes containing native methods, which provide the interface for the Java application into the program. The native method implementation would then be the "glue" between the Java applet and the internals of the enclosing program.
Custom Hardware
Another important possible application of native methods being used to access a non-Java world is providing Java programs access to custom hardware. Perhaps a Java virtual machine is running within a PDA or Set-Top-Device. A lot of what would normally be in an operating system may exist in hardware or software embedded in ROM, or other custom chip sets. Another possibility is that a computer may be equipped with a dedicated graphics card. It would be ideal to have Java make use of the graphics hardware. A set of Java classes with native methods defined would provide the Java program access to these features.
Sun's Java
In the current implementation from Sun, the Java interpreter is written in C and can thus talk to the outside environment as any normal C program can. A majority of the Java Runtime is written in Java and may make calls into the interpreter or directly to the outside environment, all via native methods. The application deals mostly with the Java Runtime, but it may also talk to the outside environment via native methods. For example in the class java.lang.Thread the setPriority() method is implemented in Java but calls the method setPriority0(), which is a native method in the Thread class. This native method is implemented in C and resides within the Java virtual machine itself. On the Windows 95 platform this native method will then call (eventually) the Win32 SetPriority() API. This is an example where the native method implementation was provided by the Java virtual machine directly. In most cases the native method implementation resides in an external dynamic link library (discussed in a following section), but the call still goes through the Java virtual machine.
Performance
Another major reason for native methods is performance. The Java language trades some performance for features like its dynamic nature, garbage collecting, and safety. Some Java implementations, like the current crop, may be interpreters, which also add extra overhead. The lost performance can be small as the implementation technology for Java systems improve, but until then and even after there may always be a small performance overhead for certain functionality a Java program may need. This functionality can be pushed down into a native method. That native method can then be implemented efficiently at the native lower level of the system on which the Java virtual machine is running. Once at the native implementation level, the developer can use the best-suited language, such as C or even assembler. In this way, maximum performance can be achieved in those specific areas while the bulk of the application is done within the safe and robust Java virtual machine. One area where you may choose to implement some parts of an application in native methods is time-intensive computations, such as graphics rendering, simulation models, and so forth.
Accessing Existing Libraries
The fact that Java is targeted at the production of platform-neutral code means that the current implementations may not access system features that you may need. An example is a database engine. If you need to, you can use the native method facility to provide your own interface to such libraries. Further, you may want to use Java to write applications that use existing in-house libraries. Again, the use of native methods enables you to make such an interface. This enables you to leverage off your existing code base as well as gradually introduce Java-based applications among your other applications coded in an older language.
Benefits and Trade-Offs
The presense of native methods offers many benefits, the biggest being the extension of Java power. However, there is always a downside to all good things, and native methods definitely have their downsides. Depending on what the goals of your application are, the downsides may not be that terrible. Foremost is the fact that, by definition, the use of native methods defeats several of Java's main goals: platform neutrality, system safety, and security.
Some of Java's attractive features help minimize the downsides, however. The best feature of all is that Java is such a nice language to develop in you won't want to use native methods unless you have to.
Platform Neutrality
Because a native method is implemented in a foreign language, the platform neutrality is limited to the language being used. Most likely, native methods are implemented in C or C++. Although those languages have standards, these standards leave a lot of room for implementation-defined attributes (even compilers on the same system may differ), so your mileage may vary. If the native method accesses the underlying system, you are tying your implementation to that system. For example, the file systems of UNIX and Win32 have some differences. There may even be differences between flavors of UNIX and Win32 (Win95 and WinNT are not identical). Once again, you may sacrifice your platform neutrality with your native method. This may cause you to have to support a limited number of platforms (rather than all Java platforms). Further, for each platform you choose to support, you may (probably will) have to implement several flavors of the native method.
The Java language and runtime provide a number of features that make applications more robust and safe. Java's memory management, synchronization features, and lack of address manipulation help prevent common programming mistakes from slipping through the development and testing phases of your product. However, once you drop out of Java into a native method, you are, once again, at the mercy of the language and system in which you are implementing the native method. If your native method implemented in C chooses to manipulate an address directly, you risk corrupting some part of memory, perhaps even the Java virtual machine itself.
Security Concerns
Additionally, the Java Language provides features to aid in the writing of secure applications. A Java virtual machine is much more capable of detecting an "evil" Java program than an application in other languages. Once you drop into a native method, the Java virtual machine can no longer verify, catch, or prevent the program from violating the security of the environment in which the Java virtual machine is running. This is the reason a Java-enabled Web browser typically does not allow a nontrusted native method to be called. In today's browsers, a trusted native method must be present on the local system in a certain location to be executed from an arriving applet (in other words, one loaded from a remote site). For more information on security, in general, see Part 6, "Security." For more information on how security applies to native methods, see
Chapter 33
, "Securing Your Native Method Libraries."
System Safety
Another potential hazard is the fact that a native method is not isolated. When a native method is entered, it not only accesses the environment outside the Java virtual machine, it also freely accesses the Java virtual machine directly. This is a necessary evil. It gives the native method quite a bit of power and flexibility, because it may need access to information kept within the virtual machine to do its job. This flexibility, however, exposes the internals of the Java virtual machine to the native method.
Dependence on the Java Implementation
It should be obvious that the implementation of native methods is also dependent on the Java implementation itself. This means that the native methods you write today for use with the Sun implementation of Java may not work with a Java implementation from another vendor.
The interface used for the Java virtual machine to call out to native methods and the interface that native methods use to access the internal functions and data structures of the Java virtual machine are not, currently, defined by either the Java Language Specification or the Java Virtual Machine Specification. A lot of native methods call back into the Java virtual machine for instantiating new objects, calling Java methods, throwing Java Exceptions, and so forth. Further, the method used to lay out Java types is also not defined. So, although your native method of today knows how to access the fields of an object, this could be different on the Java virtual machine of tomorrow. This oversight can be greatly helped if a standard API is defined for both how a Java program interacts with a native method and how a native method accesses data within the Java virtual machine. Even after such an API, implementation-defined behavior will likely still be present.
Java to the Rescue!
Recall that Java helps to minimize the damage of native methods. When you find yourself in the position that you must use native methods, you can take advantage of Java's features to help isolate the usage and perhaps maintain a fair amount of Java's advantages.
The Java Class System
Because Java narrows the use of native facilities to within the confines of a method, it does not affect the design of the program. A program is still a collection of classes and all classes still communicate with each other via their defined interface-that is, the classes' methods. Thus the callers of native methods do not know they are calling native methods. Because methods are discrete operations on the data of a specific object, they tend to be small chunks of code. This implies that native methods tend to be conceptually small, easily managed, pieces of code.
Java Still Works for You
Java will still perform a variety of duties-such as parameter checking, stack checking, synchronization, and so forth-before entering the actual native code. It greatly aids the developer in writing correct native methods. A native method is capable of creating new objects and calling Java methods, and it can even cause exceptions to be thrown. In the current implementation from Sun, an exception can be created by a native method and registered for throwing. When Java virtual machine gains control back from the native method, usually because of that method returning, the exception will then be thrown.
It's a good idea to make your native methods as small as possible and have them do a specific task. Do the work that needs to be done and pass the information back into the Java method. It's also wise to have your Java classes make the native methods private, then provide a public Java method that will call the private native method. This enables the Java method to perform error checks and other data manipulations, freeing your actual native method implementation to focus on its simple task.
How Does This Magic Work?
Much of the magic of making native methods work is provided in the next three chapters. This section provides an introduction, which neglects many of the details but should give you a good frame of reference for understanding the following chapters. If you don't really want to use native methods, but just want a basic understanding this discussion should satisfy your needs.
Sun's Implementation
Due to the lack of a well-defined interface between a Java implementation and its surrounding environment, the details of writing native methods will most likely be specific to the implementation of the Java system you are using. The next sections are based on the implementation provided by Sun on the Solaris-Sparc and Win32-Intel platforms.
Using Dynamic Linking
Sun's Java implementation interfaces to native methods by using the dynamic linking capabilities of the underlying operation system. The Java virtual machine is a complete program, which is already compiled for its respective platform. The nature of Java enables it easily to absorb a Java class and execute its behavior. However, for a compiled native method, things are not so simple. Somehow, the Java virtual machine must be taught how to call this native method. This is done by relying on the implementation of native methods to reside in a dynamic link library, which the operating system magically loads and links into the process that is running the Java virtual machine. On the Solaris platform, such a library is often called shared objects, or shared libraries, or simply dot-so's (.so's). On Win32 platforms, they are called dynamic link libraries (DLLs). This chapter uses DLL to refer to both.
Both Solaris and Win32 provide the necessary capabilities to achieve this dynamic linking. The dynamic linking facilities of both Solaris and Win32 are similar in concept, but differ in their details. This chapter does not attempt to describe the two in detail; however, if you wish to do native method programming, you should understand the mechanism used by your platform. On Solaris, you can begin by viewing the manual page on the dlopen() system call, and its relatives. On Win32, start with the help file on LoadLibrary() and its relatives. Further, you should understand the calling conventions and linking convention used by your compiler.
Sometime before a native method is invoked, the Java virtual machine must be told to find, load, and link the necessary DLLs, which contain the native method implementations. This is conveniently achieved by using the static method java.lang.System.loadLibrary( "mystuff" ). It is worth noting here that the name passed is not the actual filename of the DLL. Java maps the passed name into an expected filename, appropriate for the underlying system, of the DLL. In the call described previously, the string "mystuff" is mapped to a DLL named libmystuff.so on Solaris and mystuff.dll on Win32. If you run the Java program under a debugger, Java conveniently maps the same name "mystuff" to libmystuff_g.so and mystuff_g.dll. This enables you to supply two versions of the DLL-one with debug symbols, one without. Java magically finds the right one, dependent on whether you run under a debugger or not.
Defining the Calling Convention
In, essence, Sun defines the method its Java virtual machine will use to call external functions. In order to dynamically link and call the implementation of a native method successfully, the Java virtual machine must know several details. It must know the name of the function within the DLL (the implementation of the native method) to locate the symbol and its entry point. It also must know how to call that function (its return type, number of parameters, and types of parameters). The Java virtual machine expects the functions to be coded in C using the calling conventions appropriate for the underlying architecture (and compiler).
In simple terms, this means the actual function calls Java makes into the DLL must be known names; if you are trying to get Java to call into your existing library, unless your functions magically match the names Java expects (unlikely), you will usually have glue code, which sits between Java and your real functions. Java will call the glue functions, which in turn call in your functions. Alternatively, you can modify your functions to use the names and parameters Java expects, thus eliminating this extra call; however, in practice this is not always feasible, especially when calling existing libraries. Figure 30.2 shows the most likely scenarios of how your code will be segmented.
Figure 30.2 : Java's use of Dynamic Link Libraries.

The Sun JDK provides a tool, named javah, to help you create your native method implementation functions. The developer of native methods runs javah, passing it the name of a class. javah emits both a header file (.h) and a code file (.c) containing information about each native method and relevant type declarations. The .h file will contain the prototypes of the functions Java will call, and thus expect to find in the DLL. The .c file will contain stubs for each function. Thus, the developer needs to fill in only the details of the functions in the c file and build the DLL appropriately.
How the Virtual Machine Makes It Work
When a class is first used by Java, its class descriptor is loaded into memory. The class descriptor can be thought of as a directory for all services provided by the class-there is only one class descriptor loaded, regardless of how many instances of that class exist. Among its entries is a list of method descriptors, which contain information specific to methods, including where the code is, what parameters they take, and method modifiers.
If a method descriptor has its native modifier set, the block will include a pointer to the function that implements that native method. This function resides in some DLL but will be loaded into the Java processes address space by the operating system. At the time the class descriptor with native methods is loaded, the associated DLL does not have to be loaded, and thus the function pointer will not be set. Sometime prior to a native method being called, the associated DLL should be loaded. This is done via a call to java.system.loadLibrary(). When this call is made, Java will find and load the DLL but will still not resolve symbols; the resolution phase is delayed until the point of use. At the time of a call to a native method, Java will first check to see whether the native method implementation function has already been resolved-that is, its pointer is not null. If it has been previously resolved, the call is performed; otherwise, the resolution of the symbols is attempted. The resolution is performed by making an operating system call to see whether the symbol exists in the caller's address space. This includes the Java process and any DLLs loaded on its behalf. On Win32, this is done via a GetProcAddress() and on Solaris via a dlsym() call.
If the symbols are correctly resolved, the call is performed as if the Java virtual machine was making a standard C call to its own internal functions. If the resolution fails, the exception java.lang.UnsatisfiedLinkError will be thrown at the point of the native method call.
Summary
You should now have a basic understanding of how native methods enable a Java program to access the outside environment. Whether that consists of an operating system, a browser, or your own existing libraries, your Java code can reach them. It should now be clear that native methods do not come without some cost. You lose a lot of the benefits of the Java language. When there is no choice, however, native methods are there to be used. With the basic understanding of how native methods work you should be ready to tackle the next chapters, which provide more in-depth examples of native methods in action, as well as more tips and tricks to help you.


本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u/25176/showart_296965.html
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP