4 ms·
I think interrupting I/O can be tricky for an application to handle. If write(2) didn't have this behavior, it's likely that a common outcome would be frozen pr
by asveikau 6y ago
I think interrupting I/O can be tricky for an application to handle. If write(2) didn't have this behavior, it's likely that a common outcome would be frozen processes that you can't quit with ^C.
There either needs to be a notion of partial writes (what the article describes) or a write that gets rolled back if interrupted (probably really hellish to implement ... Maybe impossible for something like a socket where the remote machine may have already seen some packets).
EINTR or SIGPIPE are famously criticized as poor mechanisms for this. However even without that, interrupting a blocked write would remain a tricky problem.
- nine_k 6y agoWriting to a file should be transactional. It either succeeds, or gets rolled back. Journaling filesystems implement it as a crash protection mechanism, but an API where you can commit changes without closing, and can roll back explicitly, would be nice. Writing to a socket is unlike writing to a local file, and cannot offer such guarantees.
- dajohnson89 6y agoare there measurable performance hits that you take with transactionality?
- nine_k 6y agoCertainly there should be. Not always large; I suppose creating and sequential writing into a new file should be about as expensive as non-transactional.
- asveikau 6y agoSeems a lot easier said than done, and would probably incur some costs for something most programs would be largely uninterested in. Case study: Windows has a feature called "transactional NTFS" which I think may have grown out of the WinFS experiment. They duplicated a lot of filesystem APIs with "transacted" versions. I think the ntdll APIs were a little cleaner than the win32 wrappers - maybe at that layer you can set a transaction on a handle and issue normal fs calls? Anecdotally I heard it was flakey. I believe it's in a sort of deprecated state today.
- tannhaeuser 6y agoJournalling protects metadata writes (file names, dates, sizes, permissions, root block allocations) not file content writes, such that a FS can be brought up quickly after a crash. If you want transactional/atomic file ops, you need to implement it yourself on top of existing mechanisms, like transactional databases have been doing for ages.
- teddyh 6y ago> Journalling protects metadata writes […] not file content writes As I understand it, the ext4 file system can have full-data journaling with the “data=journal” mount option (instead of the default, “data=ordered”, which is as you describe).
- deleted 6y ago[deleted]
- Thorrez 6y agoBut it sounds from the article that write(2) is more complicated than necessary. Why do some errors cause an error return while other errors cause a signal? I would think consistency in how errors are propagated would be better.
- Gibbon1 6y agoThe article I remember reading said that to do it right like most other operating systems of the era did requires a lot careful design and work to get it right, so Unix developers punted and left it to the application layer. You get bagged on for saying this but it's just inexcusably shitty. And that's that.
- saagarjha 6y agoA lot of it had to do with code that historically does not check for errors.
- gpderetta 6y agoYou are thinking of "Worse is Better". But the take away of that article, even if the authrs would have preferred otheewise, is that paractical considerations, time to market and ease of implementation beat purity and elegance every time.
- Joker_vD 6y ago> interrupting a blocked write would remain a tricky problem. Would it? I believe kill(pid, SIGTERM) interrupts it all right. Or do you want to do this without terminating the program? But then we are back to square one: the programs have to handle failed writes somehow. How? "Abort, Retry, Ignore"? And then again, most of this stuff happens deep inside libraries, and every library makes different choices in handling such errors, and when you combine them in a single program, you end up with a mess. As always.
- zaarn 6y agoIt doesn't... or rather, it doesn't unless the kernel can safely unroll the write from it's insides. Famously NFS mounts can get eternally stuck processes when they die because the other end of the file descriptor doesn't exist and there is nothing the kernel can reasonably do about it.
- Joker_vD 6y agoUh, return EIO? Or EINVAL?
- zaarn 6y agoEINVAL would mean the fd is invalid, which isn't true. The application already opened the file and possibly wrote to it, so EINVAL is not a valid error code. EIO sounds like it would make sense but can only be returned for errors relating to modifying inodes, the EIO that makes sense for NFS to be gone would be occuring when you call close(2) on the fd, or fsync(2) which has very unpredictable behaviour when it returns errors anyways. Either way, NFS doesn't handle inodes, so an EIO cannot be returned.
- Joker_vD 6y agoThat's an overly pedantic reading of the Linux manpage, IMHO. Even then, a) any of those error conditions may be extended to cover NFS failure; b) EINVAL's description already says "fd is attached to an object which is unsuitable for writing" — who says that such unsuitability is an immutable property? After all, writing into read-only fds is EBADF, and c) a new error code could be invented, if one is so inclined. But then again, POSIX merely states that while EIO shall be returned if "a physical I/O error has occurred", it "may also be returned under implementation-defined conditions". Heck, if you squint at the right angle, even ENXIO could be somewhat appropriate. I don't think that extending the meaning of EIO is worse than making unkillable zombie processes, to be fair.