4 ms·
I’ve never really understood why standard Unix functions rely on internal global state. For example, strtok(): https://en.cppreference.com/w/c/string/byte/strt
by Tyrubias 6y ago
I’ve never really understood why standard Unix functions rely on internal global state. For example, strtok():
https://en.cppreference.com/w/c/string/byte/strtok https://en.cppreference.com/w/c/string/byte/strtok
Was there some reason it was optimal to write these functions this way back in the early days of Unix?
- Zambyte 6y agoBecause they were written with the expectation that they would be used in composable programs that each do one thing well.
- tyingq 6y agoA fair amount of the timeline for Unix was when it supported multiple processes, but not multiple threads. SunOS/Solaris didn't have threads until 1992, for example, and it still didn't support SMP, so global state wasn't an issue initially. Strtok() predates 1992. They did add strtok_r() once Posix threads were a thing.
- jandrese 6y agoIt probably saved a few bytes of memory or disk. A PDP11 had 64k of memory and had to be shared by multiple simultaneous users. A lot of the Unix tradeoffs can be traced back to limitations of 1970s hardware. Plus you weren't going to need more than one anyway, your program wasn't going to be big enough.
- kragen 6y agoWell, if you wanted multiple dbm files or hash tables or whatever, it was easy enough to run multiple processes. Unix processes on the PDP-11 could only address 64 KiB of memory—PDP-11 addresses are 16-bit, and C strongly encourages a von Neumann model where your program and data are in the same address space, so you couldn't even use 64 KiB for your program and another 64 KiB for your data, though the PDP-11 hardware was capable of doing this. (On things like the AVR, this is handled by copying your data into RAM at startup unless you mark it PROGMEM, in which case you can't pass pointers to it to ordinary functions. PDP-11 Unix didn't have anything like this.) As long as you don't have multiple threads (and you shouldn't, and on Unix you couldn't) the static-variable interface in these cases was usually more convenient, though more bug-prone. There's also a micro-efficiency question. Global variables are at a known memory address. If you put them in a struct and pass around a pointer to it, they are at a dynamically computed memory address. Every time you want to access them, you have to add the field offset to the struct's base pointer. This is a small amount of extra computation, and how much it actually costs you depends on the hardware—on the 8080 it's a mess that probably requires you to spill a couple of registers onto the stack, and on modern implementations of amd64 like the ones from Intel, it probably takes literally zero extra time, because you probably aren't keeping all of your addition-capable functional units busy anyway. I don't know the PDP-11's instruction set well enough to say, but I suspect the cost of such indexed addressing was intermediate between these extremes: much like the 8086, it had an "index" addressing mode, mode 6, which added an immediate 16-bit offset to a base register https://pages.cpsc.ucalgary.ca/~dsb/PDP11/AddrModes.html https://pages.cpsc.ucalgary.ca/~dsb/PDP11/AddrModes.html, and it doesn't seem to have had a pure immediate addressing mode where the address of the variable is just an immediate word without any indexing. But if you didn't need a base register for your variables, you could use PC-relative addressing, thus saving a register and decreasing register pressure (the PDP-11 only had 8 general-purpose registers, and one of them was PC, so it really only had 7). I don't have any idea what actual PDP-11 compilers did, though, and that matters a lot here.