I don't have any experience with OS development, although next year I'll most likely be taking a course on it. What I do know is assembler and working with microcontrollers, which if you want to work from the ground up, is an important first step to learning Operating Systems. The first thing is, you don't need to actually learn assembler, I learned Atmel AVR and a bit of ARM which were both pains to use, but you can do basically all assembler in C, so find an assembler language (there are thousands) that take your fancy (ARM is a good one, it's the language that is used on Smartphone processors, AVR is mainly for direct microcontrollers) and then take some tutorials on C.
The main thing you need to learn is the concepts and how to apply them, it's good that you know C because it is very low level, possibly the lowest level third generation programming language. Which means transition to assembler won't be as difficult as it would be for someone who say, mainly programmed in Java or Basic. The core concepts remain the same, although there are some differences. For one, in assembler you don't have "variables" per sue, you instead have "registers" which are small tid-bits of memory where you store your data. Registers are essentially your variables (but think as them more as temps, as a register may switch between many different variables), but you usually have a limit of the registers you use (usually 16 or 32) the other thing is that with registers there is no such thing as an int, a char, a string or a double or float, it's all just binary there isn't even a difference between signed and unsigned. Although there are several functions in most Instruction Sets that use different data types, there really isn't any difference between an 8 bit "char" and an 8 bit int, it's up to the programmer to remember which is supposed to be char or an int and program the program accordingly. This however will be different for different assemblers, which as I mentioned before, there are thousands.
You have to worry about very low level things (obviously) a lot more. You have to stop thinking of numbers in decimal but in binary and hex, you should be aware of how to translate a decimal, binary, hex or oct number to any other format quickly and efficiently. You should also be aware of how data types works, such as the binary representation of floating point numbers and doubles, as well as two's compliment. You also should read up on a bit of circuitry to start off, you're on such a low level that you need to worry about a lot of things, for an OS this mightn't be such a big deal, but for example input and outputs can be troubling because you have to sync the input to the clock of the CPU, you also have to worry about "debouncing" which is when a mechanical switch oscillates for a few milliseconds when clicked. One thing that always annoyed me is that the processor runs SO FAST, when I click a button even for a split second, it might think I clicked it 90,000 times. So I had to put lots of delays in to fix that issue xD.
For Operating System in particular, some things are going to be very important. Interrupts are essentially to learn for assembler and operating systems, any time a program runs that immediately needs to operating systems attention an interrupt occurs. It's essentially a way for a program to halt all other (or some other) programs instantly and execute a series of instructions. You need to learn about timers as well, especially if C is your primary language because as far as I'm aware you don't [really] worry about timers (you do in C++), initializing timers and so forth in some programming languages can be a huge pain. A very important thing is priority, I don't know much about it other than the concept, but it's a hugely important thing in an operating system and how it's implemented is of utmost importance.
Memory management is also an extremely important factor to make sure you know. Although C require memory management, there's even more to be done in assembler. For example, in some assemblers you can't necessarily use "malloc" to automatically allocate memory. You need to actively load different parts of memory and if you're using an assembler with Von Nuemann architecture like ARM, then your programs memory (IE: The memory that represents the actual code) could be in there too and you definitely do not want to overwrite that. Memory is also harder because it's the same situation as the variable issue, the memory is just one's and zero's, if you want to store a 64 bit double and a 32 bit integer in memory, you have to [remember] that the integer you stored is 4 bytes long and that the double you stored is 8 bytes, the computer won't do it for you. You also have to initialize your own stack, in case you don't know what the stack is it essentially keeps track of significant data when moving in and out of functions, it stores the return address for when the function is complete and any conflict registers (remember, no variables in assembler) on the stack so you don't overwrite previous data. Every time a function is called you add to the stack, and any time a function is completed you pop off the stack, this you need to do manually in assembler although most assemblers will have an instruction for this. Heaps are also not initialized and so you have to again manage it yourself.
The last thing is that instruction sets can have very few instructions, although if you code it in C it mightn't be so bad, it's still handy to know how to do certain elementary functions with very little to work with. For example, AVR doesn't have a "divide" function, so when we want to divide we have to only use the add, subtract and loop functions to work by. Everything works very differently in the low level and you'll run into a whole bunch of problems you wouldn't think about, think of someone who learned java trying to learn C, he'd probably be quite confused by the new found memory management he has to take into account! So it is from learning C to assembler. It is a huge pain to learn and it was probably my least favourite course so far, the main reason is because I was working with actual hardware and [nothing] worked the way it was supposed to. Either way, I got pretty much average results and am awaiting my final results which I think I did relatively good at, we'll see! But Assembler is VERY annoying and things won't work like they're supposed to, you'll also be bombarded with acronyms that have no explanation but sometimes you just have to accept that and move on.
Good luck on your adventure, in a year I would be able to help you with OS, but if you're really interested I do have some friends who did OS this semester who'd probably be willing to share some words of wisdom for you.