Computer >> Hướng Dẫn Máy Tính >  >> Lập Trình >> Ruby

Giải mã ngoại lệ Errno của Ruby: Hướng dẫn thực tế để hiểu và xử lý

Nếu bạn từng xem xét hệ thống phân cấp ngoại lệ của Ruby, có thể đã nhận ra điều gì đó kỳ lạ. Ngoài các ngoại lệ thông thường như RuntimeError và NoMethodError, còn có một tham chiếu lạ đến Errno::*.

Exception StandardError ... SystemCallError Errno::* ... 

Nếu bạn từng bị lỗi khi ghi đĩa khi ổ cứng đầy, hoặc cố gọi API qua mạng không ổn định, bạn chắc chắn đã gặp loại lỗi này. Bạn có thể kích hoạt nó ngay bằng cách cố mở file không tồn tại.

irb> File.open("badfilename.txt") Errno::ENOENT: No such file or directory @ rb_sysopen - badfilename.txt from (irb):9:in `initialize' from (irb):9:in `open' from (irb):9 from /Users/snhorne/.rbenv/versions/2.1.0/bin/irb:11:in `<main>' 

Nhưng ngoại lệ Errno chính xác là gì? Và tại sao chúng được xử lý khác với các loại ngoại lệ khác?

Thích nghi Ruby với hệ điều hành

Các ngoại lệ Errno về cơ bản là một bộ chuyển đổi (adapter). Chúng kết nối lỗi của hệ điều hành với hệ thống ngoại lệ của Ruby. Hệ điều hành xử lý lỗi theo cách khác so với Ruby, nên bạn bắt buộc phải có một bộ chuyển đổi.

Trong Ruby, lỗi thường được báo cáo dưới dạng ngoại lệ. Nhưng lỗi hệ điều hành thường chỉ là các số nguyên. Vì vậy Ruby định nghĩa một class ngoại lệ cho từng lỗi OS có thể xảy ra. Sau đó đưa tất cả các ngoại lệ này vào một module gọi là Errno.

Chúng ta có thể dùng IRB để xem tất cả ngoại lệ trong module này. Và thật nhiều!

irb> Errno.constants => [:NOERROR, :EPERM, :ENOENT, :ESRCH, :EINTR, :EIO, :ENXIO, :E2BIG, :ENOEXEC, :EBADF, :ECHILD, :EAGAIN, :ENOMEM, :EACCES, :EFAULT, :ENOTBLK, :EBUSY, :EEXIST, :EXDEV, :ENODEV, :ENOTDIR, :EISDIR, :EINVAL, :ENFILE, :EMFILE, :ENOTTY, :ETXTBSY, :EFBIG, :ENOSPC, :ESPIPE, :EROFS, :EMLINK, :EPIPE, :EDOM, :ERANGE, :EDEADLK, :ENAMETOOLONG, :ENOLCK, :ENOSYS, :ENOTEMPTY, :ELOOP, :EWOULDBLOCK, :ENOMSG, :EIDRM, :ECHRNG, :EL2NSYNC, :EL3HLT, :EL3RST, :ELNRNG, :EUNATCH, :ENOCSI, :EL2HLT, :EBADE, :EBADR, :EXFULL, :ENOANO, :EBADRQC, :EBADSLT, :EDEADLOCK, :EBFONT, :ENOSTR, :ENODATA, :ETIME, :ENOSR, :ENONET, :ENOPKG, :EREMOTE, :ENOLINK, :EADV, :ESRMNT, :ECOMM, :EPROTO, :EMULTIHOP, :EDOTDOT, :EBADMSG, :EOVERFLOW, :ENOTUNIQ, :EBADFD, :EREMCHG, :ELIBACC, :ELIBBAD, :ELIBSCN, :ELIBMAX, :ELIBEXEC, :EILSEQ, :ERESTART, :ESTRPIPE, :EUSERS, :ENOTSOCK, :EDESTADDRREQ, :EMSGSIZE, :EPROTOTYPE, :ENOPROTOOPT, :EPROTONOSUPPORT, :ESOCKTNOSUPPORT, :EOPNOTSUPP, :EPFNOSUPPORT, :EAFNOSUPPORT, :EADDRINUSE, :EADDRNOTAVAIL, :ENETDOWN, :ENETUNREACH, :ENETRESET, :ECONNABORTED, :ECONNRESET, :ENOBUFS, :EISCONN, :ENOTCONN, :ESHUTDOWN, :ETOOMANYREFS, :ETIMEDOUT, :ECONNREFUSED, :EHOSTDOWN, :EHOSTUNREACH, :EALREADY, :EINPROGRESS, :ESTALE, :EUCLEAN, :ENOTNAM, :ENAVAIL, :EISNAM, :EREMOTEIO, :EDQUOT, :ECANCELED, :EKEYEXPIRED, :EKEYREJECTED, :EKEYREVOKED, :EMEDIUMTYPE, :ENOKEY, :ENOMEDIUM, :ENOTRECOVERABLE, :EOWNERDEAD, :ERFKILL, :EAUTH, :EBADRPC, :EDOOFUS, :EFTYPE, :ENEEDAUTH, :ENOATTR, :ENOTSUP, :EPROCLIM, :EPROCUNAVAIL, :EPROGMISMATCH, :EPROGUNAVAIL, :ERPCMISMATCH, :EIPSEC] 

Nhưng tại sao chúng được đặt tên bí ẩn như vậy? Tôi phải làm sao đoán được ENOENT có nghĩa là "File not found"?

...Thực ra có một câu trả lời rất đơn giản.

Sao chép trực tiếp từ libc

Người đầu tiên xây dựng module Errno chỉ sao chép tên lỗi trực tiếp từ libc. Vì vậy ENOENT, trong C, là tên của một macro chứa mã lỗi số nguyên mà OS trả về khi không tìm thấy file.

Vì vậy, để thực sự hiểu từng lỗi làm gì, thủ thuật là xem tài liệu thư viện C chuẩn. Bạn có thể tìm danh sách lớn ở đây. Tôi trích xuất một vài lỗi phổ biến hơn dưới đây:

Mã lỗiÝ nghĩa
EPERMOperation not permitted; bạn không thể truy cập file trừ khi có quyền.
ENOENTFile or directory not found.
EIOInput/output error; thường dùng cho lỗi đọc/ghi vật lý.
EBADFBad file descriptor. Bạn sẽ gặp lỗi này nếu cố ghi vào file chỉ mở để đọc.
ECHILDBạn cố thao tác tiến trình con, nhưng không có tiến trình con nào.
ENOMEMHết RAM, không thể cấp phát thêm bộ nhớ ảo.
EACCESPermission denied; quyền file không cho phép thao tác.
ENOTBLKBạn cố mount file thường như thiết bị, ví dụ HDD.
EBUSYResource busy; tài nguyên hệ thống không thể chia sẻ đang được dùng.
EEXISTFile exists; file đã tồn tại được chỉ định ở ngữ cảnh chỉ hợp lý với file mới.
ENOTDIRFile không phải thư mục được chỉ định khi cần thư mục.
EISDIRFile là thư mục; không thể mở thư mục để ghi, hoặc tạo/xóa hard link tới nó.
EINVALInvalid argument. Dùng để chỉ ra các vấn đề truyền sai đối số cho hàm thư viện.
EMFILEQuá nhiều file mở trong tiến trình hiện tại, không thể mở thêm.
EFBIGFile too big; kích thước file vượt quá giới hạn cho phép.
ENOSPCNo space left on device; ghi file thất bại vì ổ đĩa đầy.
ESPIPEInvalid seek operation (như trên pipe).
EROFSCố sửa đổi thứ gì đó trên hệ thống file read-only.
EPIPEBroken pipe; không có tiến trình nào đọc từ đầu kia của pipe.
ENOTSOCKFile không phải socket được chỉ định khi cần socket.
ENETUNREACHThao tác socket thất bại vì subnet chứa host từ xa không thể truy cập.
ENETRESETKết nối mạng bị reset vì host từ xa crash.
ECONNABORTEDKết nối mạng bị hủy cục bộ.
ECONNRESETKết nối mạng bị đóng vì lý do ngoài tầm kiểm soát (remote reboot, vi phạm giao thức).
ENOBUFSBuffer kernel cho I/O đang đầy. Trong GNU, luôn đồng nghĩa với ENOMEM.
EISCONNCố kết nối socket đã kết nối.
ENOTCONNSocket chưa kết nối. Xảy ra khi truyền dữ liệu mà chưa chỉ định đích.
EDESTADDRREQKhông có địa chỉ đích mặc định cho socket.
ESHUTDOWNSocket đã bị shutdown.
ETIMEDOUTThao tác socket với timeout không nhận phản hồi trong thời gian chờ.
ECONNREFUSEDHost từ xa từ chối kết nối (thường do không chạy dịch vụ yêu cầu).
EHOSTDOWNHost từ xa cho kết nối mạng đang down.
EHOSTUNREACHHost từ xa không thể truy cập.
ENOTEMPTYDirectory not empty, nơi mong đợi thư mục rỗng. Thường khi xóa thư mục.
EPROCLIMGiới hạn tiến trình mới per-user sẽ bị vượt qua bởi fork.