System calls aren't handled like regular function calls. It takes special code to make the transition from user space to kernel space, basically a bit of inline assembly code injected into your program at the call site. The kernel side code that "catches" the system call is also low-level stuff you probably don't need to understand deeply, at least at first.
In include/linux/syscalls.h
under your kernel source directory, you find this:
asmlinkage long sys_mkdir(const char __user *pathname, int mode);
Then in /usr/include/asm*/unistd.h
, you find this:
#define __NR_mkdir 83
__SYSCALL(__NR_mkdir, sys_mkdir)
This code is saying mkdir(2)
is system call #83. That is to say, system calls are called by number, not by address as with a normal function call within your own program or to a function in a library linked to your program. The inline assembly glue code I mentioned above uses this to make the transition from user to kernel space, taking your parameters along with it.
Another bit of evidence that things are a little weird here is that there isn't always a strict parameter list for system calls: open(2)
, for instance, can take either 2 or 3 parameters. That means open(2)
is overloaded, a feature of C++, not C, yet the syscall interface is C-compatible. (This is not the same thing as C's varargs feature, which allows a single function to take a variable number of arguments.)
To answer your first question, there is no single file where mkdir()
exists. Linux supports many different file systems and each one has its own implementation of the "mkdir" operation. The abstraction layer that lets the kernel hide all that behind a single system call is called the VFS. So, you probably want to start digging in fs/namei.c
, with vfs_mkdir()
. The actual implementations of the low-level file system modifying code are elsewhere. For instance, the ext4 implementation is called ext4_mkdir()
, defined in fs/ext4/namei.c
.
As for your second question, yes there are patterns to all this, but not a single rule. What you actually need is a fairly broad understanding of how the kernel works in order to figure out where you should look for any particular system call. Not all system calls involve the VFS, so their kernel-side call chains don't all start in fs/namei.c
. mmap(2)
, for instance, starts in mm/mmap.c
, because it's part of the memory management ("mm") subsystem of the kernel.
I recommend you get a copy of "Understanding the Linux Kernel" by Bovet and Cesati.