Building and Running Modules

#include <linux/init.h>
#include <linux/module.h>
MODULE_LICENSE("Dual BSD/GPL");
static int hello_init(void)
{
printk(KERN_ALERT "Hello, world\n");
return 0;
}
static void hello_exit(void)
{
printk(KERN_ALERT "Goodbye, cruel world\n");
}
module_init(hello_init);
module_exit(hello_exit);


insmod ./hello.ko

rmmod hello

The current ( <asm/current.h> )pointer refers to the process that is currently executing.

printk(KERN_INFO "The process is \"%s\" (pid %i)\n",
current->comm, current->pid);
Kernel code cannot do floating point arithmetic

makefile:

obj-m := hello.o

module-objs := file1.o file2.o


make -C ~/kernel-2.6 M=`pwd` modules

# If KERNELRELEASE is defined, we've been invoked from the
# kernel build system and can use its language.
ifneq ($(KERNELRELEASE),)
obj-m := hello.o
# Otherwise we were called directly from the command
# line; invoke the kernel build system.
else
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
default:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
endif

The lsmod program produces a list of the modules currently loaded in the kernel.

linux/version.h. This header file defines the following macros:

UTS_RELEASE
This macro expands to a string describing the version of this kernel tree. For
example, "2.6.10".
LINUX_VERSION_CODE
This macro expands to the binary representation of the kernel version, one byte
for each part of the version release number. For example, the code for 2.6.10 is
132618 (i.e., 0x02060a).* With this information, you can (almost) easily determine
what version of the kernel you are dealing with.
KERNEL_VERSION(major,minor,release)
This is the macro used to build an integer version code from the individual numbers
that build up a version number. For example, KERNEL_VERSION(2,6,10)
expands to 132618. This macro is very useful when you need to compare the
current version and a known checkpoint.

If your module needs to export symbols for other modules to
use, the following macros should be used.

EXPORT_SYMBOL(name);
EXPORT_SYMBOL_GPL(name);

#include <linux/module.h>
#include <linux/init.h>

#include <moduleparam.h>

The actual definition of
the initialization function always looks like:
static int __init initialization_function(void)
{
/* Initialization code here */
}
module_init(initialization_function);

Initialization functions should be declared static

static void __exit cleanup_function(void)
{
/* Cleanup code here */
}
module_exit(cleanup_function);

The __exit modifier
marks the code as being for module unload only (by causing the compiler to
place it in a special ELF section).

int __init my_init_function(void)
{
int err;
/* registration takes a pointer and a name */
err = register_this(ptr1, "skull");
if (err) goto fail_this;
err = register_that(ptr2, "skull");
if (err) goto fail_that;
err = register_those(ptr3, "skull");
if (err) goto fail_those;
return 0; /* success */
fail_those: unregister_that(ptr2, "skull");
fail_that: unregister_this(ptr1, "skull");
fail_this: return err; /* propagate the error */
}

insmod hellop howmany=10 whom="Mom"

static char *whom = "world";
static int howmany = 1;
module_param(howmany, int, S_IRUGO);
module_param(whom, charp, S_IRUGO);

Numerous types are supported for module parameters:
bool
invbool
A boolean (true or false) value (the associated variable should be of type int).
The invbool type inverts the value, so that true values become false and vice
versa.
charp
A char pointer value. Memory is allocated for user-provided strings, and the
pointer is set accordingly.

int
long
short
uint
ulong
ushort
Basic integer values of various lengths. The versions starting with u are for
unsigned values.

To declare an array parameter, use:
module_param_array(name,type,nump,perm);

All module parameters should be given a default value;

The final module_param field is a permission value; you should use the definitions
found in <linux/stat.h>. This value controls who can access the representation of the
module parameter in sysfs. If perm is set to 0, there is no sysfs entry at all; otherwise,
it appears under /sys/module* with the given set of permissions. Use S_IRUGO for a
parameter that can be read by the world but cannot be changed; S_IRUGO|S_IWUSR
allows root to change the parameter. Note that if a parameter is changed by sysfs, the
value of that parameter as seen by your module changes, but your module is not
notified in any other way. You should probably not make module parameters writable,
unless you are prepared to detect the change and react accordingly.

Major and Minor Numbers

Char devices are accessed through names in the filesystem.Special files for char drivers are identified by a “c”
in the first column of the output of ls –l. Block devices appear in /dev as well, but
they are identified by a “b.” Traditionally, the major number identifies the driver associated with the device. For
example, /dev/null and /dev/zero are both managed by driver 1, Modern Linux kernels allow multiple drivers to
share major numbers, but most devices that you will see are still organized on the
one-major-one-driver principle.

The minor number is used by the kernel to determine exactly which device is being
referred to.Either way, the kernel itself
knows almost nothing about minor numbers beyond the fact that they refer to
devices implemented by your driver.

dev_t type (defined in <linux/types.h>) is used to hold device
numbers—both the major and minor parts.

<linux/kdev_t.h>. To obtain the major or minor parts of a dev_t, use:
MAJOR(dev_t dev);
MINOR(dev_t dev);

MKDEV(int major, int minor);

One of the first things your driver will need to do when setting up a char device is to
obtain one or more device numbers to work with. The necessary function for this
task is register_chrdev_region, which is declared in <linux/fs.h>:
int register_chrdev_region(dev_t first, unsigned int count,
char *name);

Here, first is the beginning device number of the range you would like to allocate.
The minor number portion of first is often 0, but there is no requirement to that
effect. count is the total number of contiguous device numbers you are requesting.

Often, however, you will not know which major numbers your
device will use;

int alloc_chrdev_region(dev_t *dev, unsigned int firstminor,
unsigned int count, char *name);

dev is an output-only parameter that will, on successful completion,
hold the first number in your allocated range.

void unregister_chrdev_region(dev_t first, unsigned int count);

#!/bin/sh
module="scull"
device="scull"
mode="664"
# invoke insmod with all arguments we got
# and use a pathname, as newer modutils don't look in . by default
/sbin/insmod ./$module.ko $* || exit 1
# remove stale nodes
rm -f /dev/${device}[0-3]
major=$(awk "\$2= =\"$module\" {print \$1}" /proc/devices)
mknod /dev/${device}0 c $major 0
mknod /dev/${device}1 c $major 1
mknod /dev/${device}2 c $major 2
mknod /dev/${device}3 c $major 3
# give appropriate group/permissions, and change the group.
# Not all distributions have staff, some have "wheel" instead.
group="staff"
grep -q '^staff:' /etc/group || group="wheel"
chgrp $group /dev/${device}[0-3]
chmod $mode /dev/${device}[0-3]

if (scull_major) {
dev = MKDEV(scull_major, scull_minor);
result = register_chrdev_region(dev, scull_nr_devs, "scull");
} else {
result = alloc_chrdev_region(&dev, scull_minor, scull_nr_devs,

"scull");
scull_major = MAJOR(dev);
}
if (result < 0) {
printk(KERN_WARNING "scull: can't get major %d\n", scull_major);
return result;
}

So far, we have reserved some device numbers for our use, but we have not yet connected
any of our driver’s operations to those numbers. The file_operations structure
is how a char driver sets up this connection.

For normal compilation, __user has no effect, but it can be used by
external checking software to find misuse of user-space addresses.

struct module *owner
The first file_operations field is not an operation at all; it is a pointer to the
module that “owns” the structure. This field is used to prevent the module from
being unloaded while its operations are in use. Almost all the time, it is simply
initialized to THIS_MODULE, a macro defined in <linux/module.h>.

file_operations structure is initialized as follows:
struct file_operations scull_fops = {
.owner = THIS_MODULE,
.llseek = scull_llseek,
.read = scull_read,
.write = scull_write,
.ioctl = scull_ioctl,
.open = scull_open,
.release = scull_release,
};

The file structure represents an open file.It is created by
the kernel on open and is passed to any function that operates on the file, until
the last close. After all instances of the file are closed, the kernel releases the data
structure.

The inode structure is used by the kernel internally to represent files.Therefore, it is
different from the file structure that represents an open file descriptor. There can be
numerous file structures representing multiple open descriptors on a single file, but
they all point to a single inode structure.

dev_t i_rdev;
For inodes that represent device files, this field contains the actual device number.
struct cdev *i_cdev;
struct cdev is the kernel’s internal structure that represents char devices; this
field contains a pointer to that structure when the inode refers to a char device
file.

added two macros that can be used to obtain the major and minor number
from an inode:
unsigned int iminor(struct inode *inode);
unsigned int imajor(struct inode *inode);

the kernel uses structures of type struct cdev to represent char
devices internally. Before the kernel invokes your device’s operations, you must allocate
and register one or more of these structures

<linux/cdev.h>

struct cdev *my_cdev = cdev_alloc( );
my_cdev->ops = &my_fops;

void cdev_init(struct cdev *cdev, struct file_operations *fops);

int cdev_add(struct cdev *dev, dev_t num, unsigned int count);

Here, dev is the cdev structure, num is the first device number to which this device
responds, and count is the number of device numbers that should be associated with
the device.

as soon as cdev_add returns, your device is “live” and its operations
can be called by the kernel. You should not call cdev_add until your driver is completely
ready to handle operations on the device.

void cdev_del(struct cdev *dev);

struct scull_dev {
struct scull_qset *data; /* Pointer to first quantum set */
int quantum; /* the current quantum size */
int qset; /* the current array size */
unsigned long size; /* amount of data stored here */
unsigned int access_key; /* used by sculluid and scullpriv */
struct semaphore sem; /* mutual exclusion semaphore */
struct cdev cdev; /* Char device structure */
};

static void scull_setup_cdev(struct scull_dev *dev, int index)
{
int err, devno = MKDEV(scull_major, scull_minor + index);
cdev_init(&dev->cdev, &scull_fops);
dev->cdev.owner = THIS_MODULE;
dev->cdev.ops = &scull_fops;
err = cdev_add (&dev->cdev, devno, 1);
/* Fail gracefully if need be */
if (err)
printk(KERN_NOTICE "Error %d adding scull%d", err, index);
}

int (*open)(struct inode *inode, struct file *filp);
The inode argument has the information we need in the form of its i_cdev field,
which contains the cdev structure we set up before.

<linux/kernel.h>:
container_of(pointer, container_type, container_field);

This macro takes a pointer to a field named container_field, within a structure of
type container_type, and returns a pointer to the containing structure.

struct scull_dev *dev; /* device information */
dev = container_of(inode->i_cdev, struct scull_dev, cdev);
filp->private_data = dev; /* for other methods */

The other way to identify the device being opened is to look at the minor number
stored in the inode structure. If you register your device with register_chrdev, you
must use this technique. Be sure to use iminor to obtain the minor number from the
inode structure, and make sure that it corresponds to a device that your driver is
actually prepared to handle.

int scull_open(struct inode *inode, struct file *filp)
{
struct scull_dev *dev; /* device information */
dev = container_of(inode->i_cdev, struct scull_dev, cdev);
filp->private_data = dev; /* for other methods */
/* now trim to 0 the length of the device if open was write-only */
if ( (filp->f_flags & O_ACCMODE) = = O_WRONLY) {
scull_trim(dev); /* ignore errors */
}
return 0; /* success */
}

int scull_release(struct inode *inode, struct file *filp)
{
return 0;
}

Note that the flush method is called every time an application calls close. However,
very few drivers implement flush, because usually there’s nothing to perform at close
time unless release is involved.

The read and write methods both perform a similar task, that is, copying data from
and to application code.

ssize_t read(struct file *filp, char __user *buff,
size_t count, loff_t *offp);
ssize_t write(struct file *filp, const char __user *buff,
size_t count, loff_t *offp);

<asm/uaccess.h>

unsigned long copy_to_user(void __user *to,
const void *from,
unsigned long count);
unsigned long copy_from_user(void *to,
const void __user *from,
unsigned long count);

The pread and pwrite system calls have different
semantics, however; they operate from a given file offset and do not change the
file position as seen by any other system calls.

ssize_t (*readv) (struct file *filp, const struct iovec *iov,
unsigned long count, loff_t *ppos);
ssize_t (*writev) (struct file *filp, const struct iovec *iov,
unsigned long count, loff_t *ppos);

<linux/uio.h>

struct iovec
{

void _ _user *iov_base;
__kernel_size_t iov_len;
};

printk(KERN_DEBUG "Here I am: %s:%i\n", __FILE__, __LINE__);
printk(KERN_CRIT "I'm trashed; giving up on %p\n", ptr);

KERN_EMERG
Used for emergency messages, usually those that precede a crash.
KERN_ALERT
A situation requiring immediate action.
KERN_CRIT
Critical conditions, often related to serious hardware or software failures.
KERN_ERR
Used to report error conditions; device drivers often use KERN_ERR to report hardware
difficulties.
KERN_WARNING
Warnings about problematic situations that do not, in themselves, create serious
problems with the system.
KERN_NOTICE
Situations that are normal, but still worthy of note. A number of security-related
conditions are reported at this level.

KERN_INFO
Informational messages. Many drivers print information about the hardware
they find at startup time at this level.
KERN_DEBUG
Used for debugging messages.

<linux/proc_fs.h>

When a process reads from your /proc file, the kernel allocates a page of memory (i.e.,
PAGE_SIZE bytes) where the driver can write data to be returned to user space. That
buffer is passed to your function, which is a method called read_proc:
int (*read_proc)(char *page, char **start, off_t offset, int count,
int *eof, void *data);

The page pointer is the buffer where you’ll write your data; start is used by the function
to say where the interesting data has been written in page (more on this later);
offset and count have the same meaning as for the read method. The eof argument
points to an integer that must be set by the driver to signal that it has no more
data to return, while data is a driver-specific data pointer you can use for internal
bookkeeping.

Once you have a read_proc function defined, you need to connect it to an entry in
the /proc hierarchy. This is done with a call to create_proc_read_entry:
struct proc_dir_entry *create_proc_read_entry(const char *name,
mode_t mode, struct proc_dir_entry *base,
read_proc_t *read_proc, void *data);

Here, name is the name of the file to create, mode is the protection mask for the file (it
can be passed as 0 for a system-wide default), base indicates the directory in which the
file should be created (if base is NULL, the file is created in the /proc root), read_proc is
the read_proc function that implements the file, and data is ignored by the kernel (but
passed to read_proc). Here is the call used by scull to make its /proc function available
as /proc/scullmem:

create_proc_read_entry("scullmem", 0 /* default mode */,
NULL /* parent dir */, scull_read_procmem,
NULL /* client data */);

remove_proc_entry("scullmem", NULL /* parent dir */);

<linux/seq_file.h>

void *start(struct seq_file *sfile, loff_t *pos);

The sfile argument can almost always be ignored. pos is an integer position indicating
where the reading should start.

void *next(struct seq_file *sfile, void *v, loff_t *pos);

Here, v is the iterator as returned from the previous call to start or next, and pos is
the current position in the file.

void stop(struct seq_file *sfile, void *v);

int show(struct seq_file *sfile, void *v);

int seq_printf(struct seq_file *sfile, const char *fmt, ...);
This is the printf equivalent for seq_file implementations; it takes the usual format
string and additional value arguments. You must also pass it the seq_file
structure given to the show function, however. If seq_printf returns a nonzero
value, it means that the buffer has filled, and output is being discarded. Most
implementations ignore the return value, however.
int seq_putc(struct seq_file *sfile, char c);
int seq_puts(struct seq_file *sfile, const char *s);
These are the equivalents of the user-space putc and puts functions.
int seq_escape(struct seq_file *m, const char *s, const char *esc);
This function is equivalent to seq_puts with the exception that any character in
s that is also found in esc is printed in octal format. A common value for esc is
" \t\n\\", which keeps embedded white space from messing up the output and
possibly confusing shell scripts.

int seq_path(struct seq_file *sfile, struct vfsmount *m, struct dentry
*dentry, char *esc);
This function can be used for outputting the file name associated with a given
directory entry. It is unlikely to be useful in device drivers; we have included it
here for completeness.

static struct seq_operations scull_seq_ops = {
.start = scull_seq_start,
.next = scull_seq_next,
.stop = scull_seq_stop,
.show = scull_seq_show
};

static int scull_proc_open(struct inode *inode, struct file *file)
{
return seq_open(file, &scull_seq_ops);
}

static struct file_operations scull_proc_ops = {
.owner = THIS_MODULE,
.open = scull_proc_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release
};

The final step is to create the actual file in /proc:
entry = create_proc_entry("scullseq", 0, NULL);
if (entry)
entry->proc_fops = &scull_proc_ops;

struct proc_dir_entry *create_proc_entry(const char *name,
mode_t mode,
struct proc_dir_entry *parent);

The strace command is a powerful tool that shows all the system calls issued by a
user-space program. Not only does it show the calls, but it can also show the arguments
to the calls and their return values in symbolic form.


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值