Modern wallets are hierarchical deterministic (HD) wallets.
A root key is used to derive an effectively infinite number of private keys using systems such as BIP32.
Each derivation results in a single public/private keypair, and hence a single address at that level.
Prior to the usage of HD wallets, things were a bit more straighforward - every time you requested a new address from the wallet, it would generate a new, random private key and store it in the wallet file, and give you the address created by the public key for the new private key.
The move to HD wallets was done for ergonomic and safety reasons - in older wallets, if you did not make a backup after every new address, you would lose any addresses generated after your most recent backup if the wallet file was lost. With HD wallets, only the root key (commonly a BIP39 mnemonic) must be backed up - all addresses and their keys can be recovered from it.