1、序言 javascript
IO操做,才程序中比較廣泛,JAVA 中提出了IO/NIO 的概念,也一直在說NIO 比IO快,一直不知道緣由,就想memcache 和ehcache 比較優劣同樣,這些東西得本身看看如何實現的,才 知道區別,從而才知道優劣以及試用範圍,而不單單是「據說」!這裏我能夠先了解下JAVA 如何操做IO的。 java
2、代碼示例 linux
咱們先看看簡單文件操做: windows
- // 這是將文件轉換成輸入流的的一種方式,得到了流咱們就能幹不少事
- FileInputStream in = new FileInputStream(new File("...file"));
再看看FileInputStream 的源碼: 數組
- public FileInputStream(File file) throws FileNotFoundException {
- String name = (file != null ? file.getPath() : null);
- // 安全管理器,這裏暫時不說
- SecurityManager security = System.getSecurityManager();
- if (security != null) {
- // 檢查是否能夠按這名字讀取
- security.checkRead(name);
- }
- if (name == null) {
- throw new NullPointerException();
- }
- // 得到文件描述,這個JDK 未公佈
- fd = new FileDescriptor();
- // 打開文件,這是個native 方法,咱們能夠用openjdk 看看裏面
- open(name);
- }
在FileInputStream.c下能夠找到方法 安全
- // open 方法
- JNIEXPORT void JNICALL
- Java_java_io_FileInputStream_open(JNIEnv *env, jobject this, jstring path) {
- fileOpen(env, this, path, fis_fd, O_RDONLY);
- }
OK。咱們從該文件上面的引用io_util_md.c找到實現: app
- void
- fileOpen(JNIEnv *env, jobject this, jstring path, jfieldID fid, int flags)
- {
- jlong h = winFileHandleOpen(env, path, flags);
- if (h >= 0) {
- // 設置fd 的值
- SET_FD(this, h, fid);
- }
- }
在上面文件裏面能夠看到:winFileHandleOpen, jvm
- jlong
- winFileHandleOpen(JNIEnv *env, jstring path, int flags)
- {
- const DWORD access =
- (flags & O_RDWR) ? (GENERIC_WRITE | GENERIC_READ) :
- (flags & O_WRONLY) ? GENERIC_WRITE :
- GENERIC_READ;
- const DWORD sharing =
- FILE_SHARE_READ | FILE_SHARE_WRITE;
- const DWORD disposition =
- /* Note: O_TRUNC overrides O_CREAT */
- (flags & O_TRUNC) ? CREATE_ALWAYS :
- (flags & O_CREAT) ? OPEN_ALWAYS :
- OPEN_EXISTING;
- const DWORD maybeWriteThrough =
- (flags & (O_SYNC | O_DSYNC)) ?
- FILE_FLAG_WRITE_THROUGH :
- FILE_ATTRIBUTE_NORMAL;
- const DWORD maybeDeleteOnClose =
- (flags & O_TEMPORARY) ?
- FILE_FLAG_DELETE_ON_CLOSE :
- FILE_ATTRIBUTE_NORMAL;
- const DWORD flagsAndAttributes = maybeWriteThrough | maybeDeleteOnClose;
- HANDLE h = NULL;
-
- if (onNT) {
- WCHAR *pathbuf = pathToNTPath(env, path, JNI_TRUE);
- if (pathbuf == NULL) {
- /* Exception already pending */
- return -1;
- }
- h = CreateFileW(
- pathbuf, /* Wide char path name */
- access, /* Read and/or write permission */
- sharing, /* File sharing flags */
- NULL, /* Security attributes */
- disposition, /* creation disposition */
- flagsAndAttributes, /* flags and attributes */
- NULL);
- free(pathbuf);
- } else {
- WITH_PLATFORM_STRING(env, path, _ps) {
- h = CreateFile(_ps, access, sharing, NULL, disposition,
- flagsAndAttributes, NULL);
- } END_PLATFORM_STRING(env, _ps);
- }
- if (h == INVALID_HANDLE_VALUE) {
- int error = GetLastError();
- if (error == ERROR_TOO_MANY_OPEN_FILES) {
- JNU_ThrowByName(env, JNU_JAVAIOPKG "IOException",
- "Too many open files");
- return -1;
- }
- throwFileNotFoundException(env, path);
- return -1;
- }
- return (jlong) h;
- }
好吧,上面代碼我也搞不明白- -,可是這幾句代碼: ide
- h = CreateFileW(
- pathbuf, /* Wide char path name */
- access, /* Read and/or write permission */
- sharing, /* File sharing flags */
- NULL, /* Security attributes */
- disposition, /* creation disposition */
- flagsAndAttributes, /* flags and attributes */
- NULL);
-
- 以及
- WITH_PLATFORM_STRING(env, path, _ps) {
- h = CreateFile(_ps, access, sharing, NULL, disposition,
- flagsAndAttributes, NULL);
個人理解是,這裏經過CreateFileW方法,建立了一個相似文件描述的結構,而後經過CreateFile調windows 下面的底層方法,填充這個結構的數據,那麼這個文件對象就能被咱們上層對象識別了,就能是獲取裏面的資源。 學習
OK,咱們如今建立了對文件之間的鏈接,拿到文件流對象以後,來看看咱們經常使用的read 方法。
- // 對字節的操做
- public native int read() throws IOException;
- private native int readBytes(byte b[], int off, int len) throws IOException;
在FileInputStream.c 裏面一樣能夠找到
- JNIEXPORT jint JNICALL
- Java_java_io_FileInputStream_read(JNIEnv *env, jobject this) {
- return readSingle(env, this, fis_fd);
- }
-
- JNIEXPORT jint JNICALL
- Java_java_io_FileInputStream_readBytes(JNIEnv *env, jobject this,
- jbyteArray bytes, jint off, jint len) {
- return readBytes(env, this, bytes, off, len, fis_fd);
- }
繼續看io_util.c 裏面:
- jint
- readSingle(JNIEnv *env, jobject this, jfieldID fid) {
- jint nread;
- char ret;
- FD fd = GET_FD(this, fid);
- if (fd == -1) {
- JNU_ThrowIOException(env, "Stream Closed");
- return -1;
- }
- // 看出是一個一個的讀取,fd 表示剛纔文件描述的一種結構
- nread = IO_Read(fd, &ret, 1);
- if (nread == 0) { /* EOF */
- return -1;
- } else if (nread == JVM_IO_ERR) { /* error */
- JNU_ThrowIOExceptionWithLastError(env, "Read error");
- } else if (nread == JVM_IO_INTR) {
- JNU_ThrowByName(env, "java/io/InterruptedIOException", NULL);
- }
- return ret & 0xFF;
- }
關於IO_Read 的東西,在io_util_md.h 有定義:
- /*
- * HPI是一個與主機通訊的並行接口
- * Route the routines through HPI
- */
- #define IO_Write JVM_Write
- #define IO_Sync JVM_Sync
- <strong>#define IO_Read JVM_Read</strong>
- #define IO_Lseek JVM_Lseek
- #define IO_Available JVM_Available
- #define IO_SetLength JVM_SetLength
關於JVM_Read 我在jvm.h 裏面看到
- /*
- // 從文件裏面讀取 a char array
- * Read data from a file decriptor into a char array.
- * // 文件的來源
- * fd the file descriptor to read from.
- // 讀出來的存放位置
- * buf the buffer where to put the read data.
- // 讀的字節數
- * nbytes the number of bytes to read.
- *
- * This function returns -1 on error, and 0 on success.
- */
- JNIEXPORT jint JNICALL
- JVM_Read(jint fd, char *buf, jint nbytes);
而後在jvm.cpp 裏面找到
關於這段代碼,大神告訴我是宏定義,關於C和C++ 的東西,我已經無力迴天啦。
固然咱們知道了介紹,能夠理解這裏會讓調系統的read 方法。
這能夠參考:「操做系統read 的原理實現」,google 一下不少,這裏就不解釋了
jvm.cpp 裏面有不少關於IO這塊的,能夠去看看,可是都是 宏定義。。
關於linux 下的這些代碼,還有涉及一下阻塞等東西,之後在去研究一下操做系統的東西吧。
- JVM_LEAF(jint, JVM_Read(jint fd, char *buf, jint nbytes))
- JVMWrapper2("JVM_Read (0x%x)", fd);
-
- //%note jvm_r6
- return (jint)os::restartable_read(fd, buf, nbytes);
- JVM_END
小結:
1.本想了解下底層怎麼操做的,可是大概瞭解下了,最終到read 或者write 的時候,一些基礎知識不夠,仍是不能透徹,可是也足夠咱們大體瞭解了。
2.按上面的思路能夠看出大概的思路,雖然沒寫write 的過程,可是最終都是經過流,或者說字符數組在內存裏面的一些操做進行,包括咱們用裝飾器模式搞了不少其餘流,基本原理同樣,僅僅爲了方便加了額外的功能。
3. 有不對的地方還請指出,僅僅是我的學習,分享做用