"Embedded Systems" is really a very imprecise term. It means often: "to deal with very hardware centric components, like MCU and board features, how to connect, initialize and use HW extensions (such as sensors) and Firmware (FW)".
An embedded system can run different types (or none) of Operating Systems:
- "bare metal": it does not have any OS, running as native code controlling the HW directly with all as "user code"
- "RTOS": using a RealTime OS (such as FreeRTOS or nowadays AzureRTOS, or others, e.g. VxWorks):
often not separating user and "kernel" code, more to have parallel threads ("cooperative" or "preemptive" multitasking) - "embedded Linux" is there are also some boards (and SW systems) implementing a Linux-like OS, often called "microC-Linux" (not full-blown Linux, e.g. no MMU, no virtualization)
- some boards (larger and more complex MCUs) can also run a full Linux (e.g. Raspberry Pi, PYNQ (FPGA) board)
What is important to know for you (when you want to start with "Embedded Systems"):
- you might know what a "BSP" is (Board Support Package, in order to "map" a specific HW into your OS)
- HAL_Drivers: Hardware Abstraction Layer drivers in order to deal with real HW (and abstract the use of HW)
- the architecture and system of your MCU (you want to use): how to initialize a device (peripheral), how to use it, how to deal with real HW interrupts, execution modes (e.g. TrustZone, secure vs. non-secure)
"Embedded Systems" require a pretty deep understanding of HW, of the MCU chip, the board you want to use. It can end up in tiny details like:
- how to enable a clocks, a device (peripheral)
- how to deal with an RTOS (creating threads)
- how to deal with real HW interrupts (on a low level basis), DMA (programming DMA engines, being aware of "memory layout" (buffers, regions))
- how to write a FW code which can boot the device (startup code), using LIBs, e.g. for USB stacks, network stacks (LwIP) etc. (these comes often with the decision which RTOS to use)
When you come from Linux system development: BSP might be a very helpful experience. But forget all high-level Linux application development (not applicable on "Embedded Systems"). The highest level you could expect is "MicroPython" - Python environments on MCUs (e.g. PYNQ), but not full blown Python (with all the LIBs possible, still hardware-centric).
On an embedded system you have to bring up all higher layer features "yourself", e.g. a mass storage (such as SD Cards, external SPI/QSPI flash memories, external SRAM), add stacks like a File System (fatFS), network support (lwIP)...
And all requires deep understanding of the MCU, the HW, the board (e.g. configuring clocks, power and pins) and the libraries to use (RTOS, fatFS, lwIP, USB stack, ...).
You might have a good understanding about kernel and user space (often not really available on embedded systems), BSP, HAL, processes (tasks and threads) but you have to learn everything again for a different, very HW centric environment (what is an RTOS task compared to a Linux user process - completely different "beasts" - in principle the same approach, in details just very different "how", often you have just a MPU (not a MMU)).
My suggestion:
- grab any MCU board (STM, NXP, Renesas, even Raspberry Pi: there is full-blown Linux with RPi 5, smaller as RPi Zero and very small as RPi Pico)
- be ready to learn a lot about HW, the MCU, the MCU architecture, the boards (schematics), the chips used as external components...
- decide also with which processor architecture you want to go with (e.g. X86 vs. ARM, ARM is very popular)
- also to decide which ARM architecture: often the Cortex-M is used, but there are also application processors (Cortex-A), multi-core systems (e.g. QualCom, STM MPUs, processors used in smartphones, with Android):
those processors cover a wide range: from very simple "bare-metal" MCUs to high-level Android/Linux systems
At the end it is "just" to study manuals, data sheets, application notes, reference manuals, programming manuals and to get familiar with the IDE you want to use. C/C++ knowledge is very helpful (you should have) but with a stronger focus on HW (e.g. why and when is a volatile needed, what is a LinkerScript, what is a memory image, how to flash code and data into memories (e.g. internal ROM flash, external SPI/QSPI flash), how to initialize devices (e.g. SDCard, external RAM controller).
Not magic, just "back to the roots" (and dealing with stuff on a very low level basis).
Start with a "simple" MCU, e.g. single core, as ARM CM0, CM4 or CM7. There are also dual-core MCUs out there up to multi-core application processors (MPUs, ARM A). The "obstacle" for you might be to get familiar "how to deal with HW and MCU features?".
Good luck!