Showing posts with label Linux Kernel. Show all posts
Showing posts with label Linux Kernel. Show all posts

Saturday, July 29, 2006

Linux Kernel Programming Points to Remember

1 ) An 'inode' describes a file. A file can have several names (or no name at all), but it has a unique inode. A 'dentry' (directory entry) describes a name of a file: the inode plus the pathname. A 'vfsmount' describes the filesystem we are in.

2 ) INVOKING SYSTEM CALL FROM LINUX KERNEL - System calls defined in
can be used but the system call checks whether the provided buffer is a valid address or not. During normal operations, an address that lies in the user address range (0 - 3GB for standard kernel configuration) is considered valid, and an address that lies in the kernel address space (3GB - 4GB) is not.
If the system call is invoked from the kernel space, the system call will fail because the virtual address of our destination buffer will be in the kernel space.
The field 'addr_limit' in the 'task_struct' structure is used to define the highest virtual address that is considered valid, the macro get_fs() and set_fs() can be used to read & write this value.

Example:

# mm_segment_t fs;
# fs = get_fs(); /* save previous value */
# set_fs(KERNEL_DS); /* use kernel limit */

# /* Invoke system call */
# set_fs(fs);

It's important to restore original "fs" before returning to the user space. Otherwise, the user program that executed this code will retain permission to overwrite kernel memory

3 ) A 'struct nameidata' represents the result of a lookup.

4 ) A 'struct address_space' gives the mapping between the blocks in a file and blocks on disk.

5 ) Directories contain file names and the number of the corresponding inodes.

6 ) Name to inode is possible by doing the lookup but inode to name is not possible.

7 ) All hard links to a file live in the same filesystem but this is not true for synbolic links.

8 ) The filesystem keeps track of the number of hard links to a file, but not of the number of symbolic links.

9 ) There can be only one hardlink to a directory.

Linux Kernel 2.4 API's

get_unused_fd() - Attempts to find an empty slot in the current process's file descriptor table and marks it busy. The operation can fail, if the process has too many file descriptors open.

Prototype: int get_unused_fd(void)


fd_install() - Associates the file structure returned by filp_open() to the allocated slot in the file descriptor.

Prototype: void fd_install(unsigned int fd, struct file *file)

Example:

# int fd = get_unused_fd();
# struct file *f = filp_open(name, flags, mode)
# fd_install(fd, f);


filp_open() - Does the real open

Prototype: struct file *filp_open(const char *filename, int flags, int mode)


open_namei() - Fills in the nameidata structure containing the dentry and vfsmount structures. This function interacts with the dentry cache via path_walk(), which in turn calls real_lookup(), which invokes the filesystem specific inode_operations->lookup() method. The role of this function is to find the entry in the parent directory with the matching name and then do iget(sb, ino) to get the corresponding inode - which brings us to the inode cache. When the inode is read in, the dentry in instantiated by means of d_add(dentry, inode).

Prototype: open_namei(const char *pathname, int flag, int mode, struct nameidata *nd)

Example:
# struct file *filp_open (const char *name, int flags, int mode)
# {
# struct nameidata nd;
# open_namei (filename, namei_flags, mode, &nd);
# return dentry_open(nd.dentry, nd.mnt, flags);
# }


dentry_open() - Given a dentry and vfsmount, this function allocates a new 'struct file' and links them together; it also invokes the filesystem specific f_op->open() method which are set in inode->i_fop when inode was read in open_namei() (which provied inode via dentry->d_inode).

Prototype: struct file *dentry_open(struct dentry *dentry, struct vfsmount *mnt, int flags)

Example: Refer open_namei()


getname() - Copies the file name from user space to the kernel space.

Prototype: char *getname(const char *filename)


cached_lookup() - Tries to find the given dentry in a cache of recently used dentries. If it is not found, the real_lookup() goes to the filesystem, which goes to the disk and actually finds the thing.


register_filesystem() - Registers a file system with the kernel. This function returns error if the file system is slready registered else stores the 'file_system_type' in the kernels filesystem table.
Main information stored in 'struct file_system_type' is
struct file_system_type {
const char *name;
struct super_block * (*read_super) ...;
...
}
where 'name' - name of the file system.
'read_super' - will be responsible for initializing a 'struct super_block'. The superblock contains fundamental information, and also pointers to the block device operations needed to operate on this filesystem.

Prototype: int register_filesystem(struct file_system_type *fs)

lookup_create() - Looks up a dentry, creates it if it does not exist

Prototype: struct dentry *lookup_create(struct nameidata *nd, int is_dir)

get_fs() - Returns the current address limit (must always be in terms of a process)
Refer "Linux Kernel Programming - Points to remember.txt Point 2"

set_fs() - Sets the current address limit. Only options available are KERNEL_DS & USER_DS
Refer "Linux Kernel Programming - Points to remember.txt Point 2"

Example:

# old_fs = get_fs();
# set_fs(KERNEL_DS);
# fd = sys_open(filename, O_RDONLY, 0);
# if (fd >= 0) {
# /* read the file here */
# sys_close(fd);
# }
# set_fs(old_fs);


dget() - Gets a reference to a dentry. Given a dentry, increments the reference count. A dentry will never be destroyed in reference exist.

Prototype: struct dentry * dget (struct dentry * dentry)


atomic_read() - Reads atomic variable

Prototype: atomic_read(atomic_t *)

atomic_inc() -

path_lookup() -

path_release() -

d_path() - Returns ASCII parh name provided dentry & vfsmount
Prototype: char * d_path(struct dentry *dentry, struct vfsmount *vfsmnt, char *buf, int buflen)

mntget() - Increments mount count

Prototype: struct vfsmount *mntget(struct vfsmount *mnt)

Function Definition:

# struct vfsmount *mntget(struct vfsmount *mnt)
# {
# if (mnt)
# atomic_inc(&mnt->mnt_count);
# return mnt;
# }

mntput() - Decrements the mount count. It also decrements dentry associated with the mount point, if mount count is zero.

Prototype: void mntput(struct vfsmount *mnt)

Function Definition:

# void mntput(struct vfsmount *mnt)
# {
# if (mnt) {
# if (atomic_dec_and_test(&mnt->mnt_count))
# __mntput(mnt);
# }
# }



putname() - Free's kernel memory reserved by calling getname().

Prototype: putname(name) (macro)

dput() - Release dentry. This will drop the usage count and if appropriate call the dentry unlink method as well as removing it from the queues and releaseing its resources. If the parent dentries were scheduled for release they too many now get deleted.

Prototype: void dput(struct dentry *dentry)

schedule_timeout() - Sleep until timeout. Make the current task sleep until timeout jiffies have elapsed. The routine will return immediately unless the current task state has been set

Prototype: signed long schedule_timeout (signed long timeout);
where - timeout value in jiffies

path_walk()