System calls are the rendezvous point between user space and kernel space. It's how normal, user-level code traps into kernel space when something a little more complex needs to be made - reading from a device, writing to a device, changing a hardware configuration, sending network packets, you name it.
So basically, user code interacts with the kernel via syscalls; invoking a syscall is a request to the kernel for a service. While doing so, an interrupt is generated that "wakes up" the kernel. This is called trapping into kernel space.
Signals, on the other hand, are an independent and different communication mechanism. Signals are used by the kernel to asynchronously notify user processes of various events (in some cases, I/O available, or an invalid memory access attempt, or an illegal instruction, etc.), but they are also used between processes: if you have the correct permissions, you can send a signal from a user-space process to another user-space process.
You can set up a custom handler for user-reserved signals such asSIGUSR1
and SIGUSR2
and do whatever pleases you with these. You can use signals to write a basic parent/child synchronization mechanism with the help of sigsuspend(2)
and sigaction(2)
(and a flag). You can kill an unresponsive process with SIGKILL
(although it's advisable that first you try SIGTERM
to give it a chance to terminate gracefully).
So, you see, the possibilities are endless. Syscalls are a request to the kernel for a service, adhere to a strictly defined API, and allow you to enter and leave kernel mode for administrative operations. Signals are more like a generic process communication mechanism that also happens to be used by the kernel to notify user processes, but there are other uses.