Matt: Conrad is using the SD code via a FATfs file system layer. As you said, SD cards are block devices, but file systems do clever tricks to make a block device look like a character device, with what you said in mind. Unfortunately, the fatfs library is a complied object, and I can't see the code on my system, but I'm guessing that it is clever, depending how much effort Espressif have put into it. There is only one way to find out:
Conrad: the SD library you are using lives here (well, on my system):
~/.arduino15/packages/esp32/hardware/esp32/1.0.4/libraries/SD
I recommend you backup this directory and then add some debug serial printf() statements into this library. Then write a small test program to write a byte of data with your open() write(1 byte) close() convention, and then see what it does. Then write another program that writes two bytes: open() write(1 byte) write(1 byte) close() etc.
I recommend putting some printfs in sdReadBytes(), sdWriteBytes(), sdReadSector(), sdReadSectors(), sWriteSector(), and sdWriteSectors(). These are located in sd_diskio.cpp in the directory (folder) above.
What you want to know is:
- if I write a byte, is the file system reading 512 bytes and then writing 512 bytes (a block write) ?
- What happens if I write 2 bytes in a row ?. Does file system do 1 read block and 2 writes blocks ?. Or does it do nothing ? (implying the file system is caching).
Once you gauge what the file system and SD library are doing, you'll be able to write some elegant code that gets your application singing

Matt is right. You should have ooddles of bandwidth.