NodeJS First Dive
原项目视频地址
https://www.udemy.com/course/complete-nodejs-developer-zero-to-mastery/
相关技术栈
NodeJS
File I/O
Web Servers
Express.js
Perfermance and Scale
Authentication
Databases
Deployment and CI/CD
RESTful APIs
Production + the Cloud
Sockets
TS
Deno
主项目Master Project
- NASA行星
记得开启字幕~
P1-17
Node.js is born
How do you run JavaScript?
我们的计算机可以运行js吗?实际上是浏览器可以运行js。因为浏览器有JS引擎。
JS引擎
JavaScript引擎 - 维基百科,自由的百科全书 (kfd.me)
V8引擎的发布,JavaScript性能得到释放,Google Chrome因此流行
时间线
-
1995 Brenden创造JS
-
1996 Netscape浏览器可以执行js
-
2008 Chrome V8新引擎发布
-
2009 Ryan Dahl创造NodeJS
- 后来创建了deno
建议观看视频:Ryan Dahl: Original Node.js presentation
Node.js (nodejs.org)
Node.js is a JavaScript runtime built on Chrome's V8 JavaScript engine.
Node接收js文件,交给V8引擎去编译执行;js引擎遇到不属于js的部分(openfiles),交给libuv
libuv | Cross-platform asynchronous I/O
libuv is a multi-platform support library with a focus on asynchronous I/O.
how work
Node.js 安装
Long Term Support (LTS) schedule是稳定版本。关于版本的详细规划可以看Releases | Node.js (nodejs.org),据此选择自己接下来的生产版本所应该使用的,最近的话使用V16。
- Current 会有比较大的改动
- Active 可用,小的改动
- Maintenance 可用,仅修改Bug
First App
// hello.js
const mission = process.argv[2];
if (mission === "learn") {
console.log("Time to write some node code!")
}
else {
console.log(`Is ${mission} really more fun?`)
}
process 进程 | Node.js API 文档 (nodejs.cn)
process
对象提供有关当前 Node.js 进程的信息并对其进行控制.
process.argv | Node.js API 文档 (nodejs.cn)
peocess.argv(arg0,arg1,args)
- agr0 Node.js安装路径;
- arg[1] 当前文件js路径;
- args命令行参数
命令行
> node hello.js learn
Time to write some node code!
Node VS JavaScript
浏览器js
- window 全局对象,表示浏览器中打开的窗口
- alert
- setTimeout
- ···
- document HTML专用的属性和方法
- activeElement
- URL
- ···
- history
- location
- navigator
Node.js
- global
- process
- module
- __filename 当前模块的文件名:CommonJS 模块 | Node.js API 文档 (nodejs.cn)
- require()
What does Node.js do?
- V8引擎(c++) 这是node能够运行js的原因
- APIs(js/c++)
- fs 文件读取
- http 网络请求
- path 查找计算机文件路径
- crypto 密码功能
- Node.js bindings 将js与C/C++的部分绑定
- libuv(c) 与计算机对话
Deep dive
nodejs/node: Node.js JavaScript runtime (github.com)
- lib node.js apis
- console
- fs
- http
- ···
- src c++部分
- node_file.cc
- ···
打开文件事件
-
在node/lib下找到fs模块,搜索
open(
function open(path, flags, mode, callback) { path = getValidatedPath(path); if (arguments.length < 3) { callback = flags; flags = 'r'; mode = 0o666; } else if (typeof mode === 'function') { callback = mode; mode = 0o666; } else { mode = parseFileMode(mode, 'mode', 0o666); } const flagsNumber = stringToFlags(flags); callback = makeCallback(callback); const req = new FSReqCallback(); req.oncomplete = callback; binding.open(pathModule.toNamespacedPath(path), flagsNumber, mode, req); }
其中
binding.open()
是绑定部分 -
在node/src中node_file.cc
- 在页面最下方稍微上一点,可以看到方法绑定
env->SetMethod(target, "access", Access); env->SetMethod(target, "close", Close); env->SetMethod(target, "open", Open); env->SetMethod(target, "openFileHandle", OpenFileHandle); env->SetMethod(target, "read", Read);
- 因此我们需要寻找Open函数,搜索
Open(
static void Open(const FunctionCallbackInfo<Value>& args) { Environment* env = Environment::GetCurrent(args); const int argc = args.Length(); CHECK_GE(argc, 3); BufferValue path(env->isolate(), args[0]); CHECK_NOT_NULL(*path); CHECK(args[1]->IsInt32()); const int flags = args[1].As<Int32>()->Value(); CHECK(args[2]->IsInt32()); const int mode = args[2].As<Int32>()->Value(); FSReqBase* req_wrap_async = GetReqWrap(args, 3); if (req_wrap_async != nullptr) { // open(path, flags, mode, req) req_wrap_async->set_is_plain_open(true); AsyncCall(env, req_wrap_async, args, "open", UTF8, AfterInteger, uv_fs_open, *path, flags, mode); } else { // open(path, flags, mode, undefined, ctx) CHECK_EQ(argc, 5); FSReqWrapSync req_wrap_sync; FS_SYNC_TRACE_BEGIN(open); int result = SyncCall(env, args[4], &req_wrap_sync, "open", uv_fs_open, *path, flags, mode); FS_SYNC_TRACE_END(open); if (result >= 0) env->AddUnmanagedFd(result); args.GetReturnValue().Set(result); } }
- 可以看到异步调用AsyncCall、同步调用SyncCall,定位到了
uv_fs_open
函数。我们需要去libuv的代码中寻找.
-
在libuv/src/unix中,可以看到unix系统类的相关代码实现
-
我们可以看到darwin-proctitle.c,darwin是苹果系统的核心组件代号,因此我们来对地方了
-
进入fs.c中搜索
uv_fs_open
int uv_fs_open(uv_loop_t* loop, uv_fs_t* req, const char* path, int flags, int mode, uv_fs_cb cb) { INIT(OPEN); PATH; req->flags = flags; req->mode = mode; POST; }
-
可以看到实际调用
OPEN
,在代码最下面稍微上一点的地方,可以看到X(MKDIR, mkdir(req->path, req->mode)); X(MKDTEMP, uv__fs_mkdtemp(req)); X(MKSTEMP, uv__fs_mkstemp(req)); X(OPEN, uv__fs_open(req)); X(READ, uv__fs_read(req));
-
因此实际调用
uv__fs_open
,可以搜到static ssize_t uv__fs_open(uv_fs_t* req) { #ifdef O_CLOEXEC return open(req->path, req->flags | O_CLOEXEC, req->mode); // unix实际打开文件 #else /* O_CLOEXEC */ int r; if (req->cb != NULL) uv_rwlock_rdlock(&req->loop->cloexec_lock); r = open(req->path, req->flags, req->mode); /* In case of failure `uv__cloexec` will leave error in `errno`, * so it is enough to just set `r` to `-1`. */ if (r >= 0 && uv__cloexec(r, 1) != 0) { r = uv__close(r); if (r != 0) abort(); r = -1; } if (req->cb != NULL) uv_rwlock_rdunlock(&req->loop->cloexec_lock); return r; // 返回到js中 #endif /* O_CLOEXEC */ }
-
-
在libuv/src/win中,可以看到win系统类的相关代码实现
-
打开fs.c,搜索
uv_fs_open
int uv_fs_open(uv_loop_t* loop, uv_fs_t* req, const char* path, int flags, int mode, uv_fs_cb cb) { int err; INIT(UV_FS_OPEN); err = fs__capture_path(req, path, NULL, cb != NULL); if (err) { SET_REQ_WIN32_ERROR(req, err); return req->result; } req->fs.info.file_flags = flags; req->fs.info.mode = mode; POST; }
-
实际调用
UV_FS_OPEN
,像unix一样,在代码底部稍微上一点的地方可以看到#define XX(uc, lc) case UV_FS_##uc: fs__##lc(req); break; switch (req->fs_type) { XX(OPEN, open) XX(CLOSE, close) XX(READ, read) XX(WRITE, write) XX(COPYFILE, copyfile)
-
实际调用
fs__open
,搜索得到void fs__open(uv_fs_t* req) { DWORD access; DWORD share; DWORD disposition; DWORD attributes = 0; HANDLE file; int fd, current_umask; int flags = req->fs.info.file_flags; struct uv__fd_info_s fd_info; /* Adjust flags to be compatible with the memory file mapping. Save the * original flags to emulate the correct behavior. */ if (flags & UV_FS_O_FILEMAP) { fd_info.flags = flags; fd_info.current_pos.QuadPart = 0; if ((flags & (UV_FS_O_RDONLY | UV_FS_O_WRONLY | UV_FS_O_RDWR)) == UV_FS_O_WRONLY) { /* CreateFileMapping always needs read access */ flags = (flags & ~UV_FS_O_WRONLY) | UV_FS_O_RDWR; } if (flags & UV_FS_O_APPEND) { /* Clear the append flag and ensure RDRW mode */ flags &= ~UV_FS_O_APPEND; flags &= ~(UV_FS_O_RDONLY | UV_FS_O_WRONLY | UV_FS_O_RDWR); flags |= UV_FS_O_RDWR; } } /* Obtain the active umask. umask() never fails and returns the previous * umask. */ current_umask = umask(0); umask(current_umask); /* convert flags and mode to CreateFile parameters */ switch (flags & (UV_FS_O_RDONLY | UV_FS_O_WRONLY | UV_FS_O_RDWR)) { case UV_FS_O_RDONLY: access = FILE_GENERIC_READ; break; case UV_FS_O_WRONLY: access = FILE_GENERIC_WRITE; break; case UV_FS_O_RDWR: access = FILE_GENERIC_READ | FILE_GENERIC_WRITE; break; default: goto einval; } if (flags & UV_FS_O_APPEND) { access &= ~FILE_WRITE_DATA; access |= FILE_APPEND_DATA; } /* * Here is where we deviate significantly from what CRT's _open() * does. We indiscriminately use all the sharing modes, to match * UNIX semantics. In particular, this ensures that the file can * be deleted even whilst it's open, fixing issue * https://github.com/nodejs/node-v0.x-archive/issues/1449. * We still support exclusive sharing mode, since it is necessary * for opening raw block devices, otherwise Windows will prevent * any attempt to write past the master boot record. */ if (flags & UV_FS_O_EXLOCK) { share = 0; } else { share = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE; } switch (flags & (UV_FS_O_CREAT | UV_FS_O_EXCL | UV_FS_O_TRUNC)) { case 0: case UV_FS_O_EXCL: disposition = OPEN_EXISTING; break; case UV_FS_O_CREAT: disposition = OPEN_ALWAYS; break; case UV_FS_O_CREAT | UV_FS_O_EXCL: case UV_FS_O_CREAT | UV_FS_O_TRUNC | UV_FS_O_EXCL: disposition = CREATE_NEW; break; case UV_FS_O_TRUNC: case UV_FS_O_TRUNC | UV_FS_O_EXCL: disposition = TRUNCATE_EXISTING; break; case UV_FS_O_CREAT | UV_FS_O_TRUNC: disposition = CREATE_ALWAYS; break; default: goto einval; } attributes |= FILE_ATTRIBUTE_NORMAL; if (flags & UV_FS_O_CREAT) { if (!((req->fs.info.mode & ~current_umask) & _S_IWRITE)) { attributes |= FILE_ATTRIBUTE_READONLY; } } if (flags & UV_FS_O_TEMPORARY ) { attributes |= FILE_FLAG_DELETE_ON_CLOSE | FILE_ATTRIBUTE_TEMPORARY; access |= DELETE; } if (flags & UV_FS_O_SHORT_LIVED) { attributes |= FILE_ATTRIBUTE_TEMPORARY; } switch (flags & (UV_FS_O_SEQUENTIAL | UV_FS_O_RANDOM)) { case 0: break; case UV_FS_O_SEQUENTIAL: attributes |= FILE_FLAG_SEQUENTIAL_SCAN; break; case UV_FS_O_RANDOM: attributes |= FILE_FLAG_RANDOM_ACCESS; break; default: goto einval; } if (flags & UV_FS_O_DIRECT) { /* * FILE_APPEND_DATA and FILE_FLAG_NO_BUFFERING are mutually exclusive. * Windows returns 87, ERROR_INVALID_PARAMETER if these are combined. * * FILE_APPEND_DATA is included in FILE_GENERIC_WRITE: * * FILE_GENERIC_WRITE = STANDARD_RIGHTS_WRITE | * FILE_WRITE_DATA | * FILE_WRITE_ATTRIBUTES | * FILE_WRITE_EA | * FILE_APPEND_DATA | * SYNCHRONIZE * * Note: Appends are also permitted by FILE_WRITE_DATA. * * In order for direct writes and direct appends to succeed, we therefore * exclude FILE_APPEND_DATA if FILE_WRITE_DATA is specified, and otherwise * fail if the user's sole permission is a direct append, since this * particular combination is invalid. */ if (access & FILE_APPEND_DATA) { if (access & FILE_WRITE_DATA) { access &= ~FILE_APPEND_DATA; } else { goto einval; } } attributes |= FILE_FLAG_NO_BUFFERING; } switch (flags & (UV_FS_O_DSYNC | UV_FS_O_SYNC)) { case 0: break; case UV_FS_O_DSYNC: case UV_FS_O_SYNC: attributes |= FILE_FLAG_WRITE_THROUGH; break; default: goto einval; } /* Setting this flag makes it possible to open a directory. */ attributes |= FILE_FLAG_BACKUP_SEMANTICS; file = CreateFileW(req->file.pathw, // 实际创建文件 access, share, NULL, disposition, attributes, NULL); if (file == INVALID_HANDLE_VALUE) { DWORD error = GetLastError(); if (error == ERROR_FILE_EXISTS && (flags & UV_FS_O_CREAT) && !(flags & UV_FS_O_EXCL)) { /* Special case: when ERROR_FILE_EXISTS happens and UV_FS_O_CREAT was * specified, it means the path referred to a directory. */ SET_REQ_UV_ERROR(req, UV_EISDIR, error); } else { SET_REQ_WIN32_ERROR(req, GetLastError()); } return; } fd = _open_osfhandle((intptr_t) file, flags); // 把句柄赋给了fd变量 if (fd < 0) { /* The only known failure mode for _open_osfhandle() is EMFILE, in which * case GetLastError() will return zero. However we'll try to handle other * errors as well, should they ever occur. */ if (errno == EMFILE) SET_REQ_UV_ERROR(req, UV_EMFILE, ERROR_TOO_MANY_OPEN_FILES); else if (GetLastError() != ERROR_SUCCESS) SET_REQ_WIN32_ERROR(req, GetLastError()); else SET_REQ_WIN32_ERROR(req, (DWORD) UV_UNKNOWN); CloseHandle(file); return; } if (flags & UV_FS_O_FILEMAP) { FILE_STANDARD_INFO file_info; if (!GetFileInformationByHandleEx(file, FileStandardInfo, &file_info, sizeof file_info)) { SET_REQ_WIN32_ERROR(req, GetLastError()); CloseHandle(file); return; } fd_info.is_directory = file_info.Directory; if (fd_info.is_directory) { fd_info.size.QuadPart = 0; fd_info.mapping = INVALID_HANDLE_VALUE; } else { if (!GetFileSizeEx(file, &fd_info.size)) { SET_REQ_WIN32_ERROR(req, GetLastError()); CloseHandle(file); return; } if (fd_info.size.QuadPart == 0) { fd_info.mapping = INVALID_HANDLE_VALUE; } else { DWORD flProtect = (fd_info.flags & (UV_FS_O_RDONLY | UV_FS_O_WRONLY | UV_FS_O_RDWR)) == UV_FS_O_RDONLY ? PAGE_READONLY : PAGE_READWRITE; fd_info.mapping = CreateFileMapping(file, NULL, flProtect, fd_info.size.HighPart, fd_info.size.LowPart, NULL); if (fd_info.mapping == NULL) { SET_REQ_WIN32_ERROR(req, GetLastError()); CloseHandle(file); return; } } } uv__fd_hash_add(fd, &fd_info); } SET_REQ_RESULT(req, fd); // 设置结果返回给js return; einval: SET_REQ_UV_ERROR(req, UV_EINVAL, ERROR_INVALID_PARAMETER); }
-
可以看到win的代码有254行,比unix的27行多了一个fr11。这是因为需要处理跨平台的一些逻辑,保证结果在unix上兼容。
-
本文来自博客园,作者:沧浪浊兮,转载请注明原文链接:https://www.cnblogs.com/shixiu/p/16262067.html