diff --git a/3.16/LKMPG-3.16.html b/3.16/LKMPG-3.16.html index de17eec..02d1968 100644 --- a/3.16/LKMPG-3.16.html +++ b/3.16/LKMPG-3.16.html @@ -3,11 +3,11 @@ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
- +-The Linux Kernel Module Programming Guide is a free book; you may reproduce and/or modify it under the terms of the Open Software License, version 1.1. You can obtain a copy of this license at http://opensource.org/licenses/osl.php. +The Linux Kernel Module Programming Guide is a free book; you may reproduce and/or modify it under the terms of the Open Software License, version 1.1. You can obtain a copy of this license at http://opensource.org/licenses/osl.php.
@@ -486,12 +486,12 @@ The source code and discussions should apply to most architectures, but I can't The following people have contributed corrections or good suggestions: Ignacio Martin, David Porter, Daniele Paolo Scarpazza, Dimo Velev, Francois Audeon, Horst Schirmeier and Bob Mottram.
-So, you want to write a kernel module. You know C, you've written a few normal programs to run as processes, and now you want to get to where the real action is, to where a single wild pointer can wipe out your file system and a core dump means a reboot.
@@ -502,9 +502,9 @@ What exactly is a kernel module? Modules are pieces of code that can be loaded aYou can see what modules are already loaded into the kernel by running lsmod, which gets its information by reading the file /proc/modules.
@@ -573,9 +573,9 @@ Now you know how modules get into the kernel. There's a bit more to the story ifBefore we delve into code, there are a few issues we need to cover. Everyone's system is different and everyone has their own groove. Getting @@ -585,7 +585,7 @@ for the first time, it will be smooth sailing thereafter.
A module compiled for one kernel won't load if you boot a different kernel unless you enable CONFIG_MODVERSIONS in the kernel. We won't go into module @@ -597,7 +597,7 @@ versioning errors, compile a kernel with modversioning turned off.
It is highly recommended that you type in, compile and load all the examples this guide discusses. It's also highly recommended you do this from a @@ -614,7 +614,7 @@ information, do all your work from the console.
Very often, Linux distros will distribute kernel source that has been patched in various non-standard ways, which may cause trouble. @@ -644,12 +644,12 @@ by using gcc's -I switch.
When the first caveman programmer chiseled the first program on the walls of the first cave computer, it was a program to paint the string `Hello, world' in Antelope pictures. Roman programming textbooks began with the `Salut, Mundi' program. I don't know what happens to people who break with this tradition, but I think it's safer not to find out. We'll start with a series of hello world programs that demonstrate the different aspects of the basics of writing a kernel module.
@@ -659,9 +659,9 @@ Here's the simplest module possible. Don't compile it yet; we'll cover module co#+BEGIN_SRC: c /* @@ -670,7 +670,7 @@ Here's the simplest module possible. Don't compile it yet; we'll cover module co
-/ + / #include <linux/module.h> / Needed by all modules / #include <linux/kernel.h> / Needed for KERN_INFO */
@@ -688,8 +688,8 @@ int init_module(void)- */ -return 0; + */ + return 0; }
@@ -717,9 +717,9 @@ Lastly, every kernel module needs to include linux/module.h. We needed to includDespite what you might think, printk() was not meant to communicate information to the user, even though we used it for exactly this purpose in hello-1! It happens to be a logging mechanism for the kernel, and is used to log information or give warnings. Therefore, each printk() statement comes with a priority, which is the <1> and KERN_ALERT you see. There are 8 priorities and the kernel has macros for them, so you don't have to use cryptic numbers, and you can view them (and their meanings) in linux/kernel.h. If you don't specify a priority level, the default priority, DEFAULT_MESSAGE_LOGLEVEL, will be used. @@ -735,9 +735,9 @@ If the priority is less than int console_loglevel, the message is printed on you
Kernel modules need to be compiled a bit differently from regular userspace apps. Former kernel versions required us to care much about these settings, which are usually stored in Makefiles. Although hierarchically organized, many redundant settings accumulated in sublevel Makefiles and made them large and rather difficult to maintain. Fortunately, there is a new way of doing these things, called kbuild, and the build process for external loadable modules is now fully integrated into the standard kernel build mechanism. To learn more on how to compile modules which are not part of the official kernel (such as all the examples you'll find in this guide), see file linux/ Documentation/kbuild/modules.txt.
@@ -747,9 +747,9 @@ So, let's look at a simple Makefile for compiling a module named hello-1.c:obj-m += hello-1.o
@@ -846,17 +846,17 @@ Here's another exercise for the reader. See that comment above the return statem
As of Linux 2.4, you can rename the init and cleanup functions of your modules; they no longer have to be called init_module() and cleanup_module() respectively. This is done with the module_init() and module_exit() macros. These macros are defined in linux/init.h. The only caveat is that your init and cleanup functions must be defined before calling the macros, otherwise you'll get compilation errors. Here's an example of this technique:
#+BEGIN_SRC: c /* @@ -866,7 +866,7 @@ As of Linux 2.4, you can rename the init and cleanup functions of your modules;
-/ + / #include <linux/module.h> / Needed by all modules / #include <linux/kernel.h> / Needed for KERN_INFO / #include <linux/init.h> / Needed for the macros */ @@ -899,9 +899,9 @@ So now we have two real kernel modules under our belt. Adding another module is
obj-m += hello-1.o obj-m += hello-2.o @@ -922,9 +922,9 @@ you can see, some things get hardwired into the kernel (obj-y) but where are all
This demonstrates a feature of kernel 2.2 and later. Notice the change in the definitions of the init and cleanup functions. The __init macro causes the init function to be discarded and its memory freed once the init function finishes for built-in drivers, but not loadable modules. If you think about when the init function is invoked, this makes perfect sense.
@@ -942,9 +942,9 @@ These macros are defined in linux/init.h and serve to free up kernel memory. Whe#+BEGIN_SRC: c /* @@ -953,7 +953,7 @@ These macros are defined in linux/init.h and serve to free up kernel memory. Whe
-/ + / #include <linux/module.h> / Needed by all modules / #include <linux/kernel.h> / Needed for KERN_INFO / #include <linux/init.h> / Needed for the macros */ @@ -987,9 +987,9 @@ module_exit(hello_3_exit);
If you're running kernel 2.4 or later, you might have noticed something like this when you loaded proprietary modules:
@@ -1047,7 +1047,7 @@ In kernel 2.4 and later, a mechanism was devised to identify code licensed under-*/ + */ #+END_SRC
@@ -1069,9 +1069,9 @@ Users of traditional Unix editors, like emacs or vi will also find tag files use#+BEGIN_SRC: c /* @@ -1080,7 +1080,7 @@ Users of traditional Unix editors, like emacs or vi will also find tag files use
-/
+ /
#include <linux/module.h> / Needed by all modules /
#include <linux/kernel.h> / Needed for KERN_INFO /
#include <linux/init.h> / Needed for the macros */
@@ -1125,7 +1125,7 @@ module_exit(cleanup_hello_4);
-*/ + */ MODULE_LICENSE("GPL");
@@ -1136,7 +1136,7 @@ MODULE_LICENSE("GPL");-/ + / MODULE_AUTHOR(DRIVER_AUTHOR); / Who wrote this module? / MODULE_DESCRIPTION(DRIVER_DESC); / What does this module do */
@@ -1150,7 +1150,7 @@ MODULE_DESCRIPTION(DRIVER_DESC); / What does this module do */-*/ + */ MODULE_SUPPORTED_DEVICE("testdevice"); #+END_SRC
@@ -1158,9 +1158,9 @@ MODULE_SUPPORTED_DEVICE("testdevice");Modules can take command line arguments, but not with the argc/argv you might be used to.
@@ -1207,9 +1207,9 @@ Lastly, there's a macro function, MODULE_PARM_DESC(), that is used to document a#+BEGIN_SRC: c /* @@ -1218,7 +1218,7 @@ Lastly, there's a macro function, MODULE_PARM_DESC(), that is used to document a
-*/ + */ #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/kernel.h> @@ -1277,7 +1277,7 @@ MODULE_PARM_DESC(mystring, "A character string");
-*/ + */ module_param_array(myintArray, int, &arr_argc, 0000); MODULE_PARM_DESC(myintArray, "An array of integers");
@@ -1358,9 +1358,9 @@ hello-5.o: invalid argument syntax for mylong: 'h'Sometimes it makes sense to divide a kernel module between several source files.
@@ -1370,9 +1370,9 @@ Here's an example of such a kernel module.#+BEGIN_SRC: c /* @@ -1405,9 +1405,9 @@ The next file:
#+BEGIN_SRC: c /* @@ -1439,9 +1439,9 @@ And finally, the makefile:
#+BEGIN_SRC: makefile obj-m += hello-1.o @@ -1474,9 +1474,9 @@ make what object files are part of that module.
Obviously, we strongly suggest you to recompile your kernel, so that you can enable a number of useful debugging features, such as forced module unloading (MODULE_FORCE_UNLOAD): when this option is enabled, you can force the kernel to unload a module even when it believes it is unsafe, via a rmmod -f module command. This option can save you a lot of time and a number of reboots during the development of a module.
@@ -1573,12 +1573,12 @@ If you do not desire to actually compile the kernel, you can interrupt the buildA program usually begins with a main() function, executes a bunch of instructions and terminates upon completion of those instructions. Kernel modules work a bit differently. A module always begin with either the init_module or the function you specify with module_init call. This is the entry function for modules; it tells the kernel what functionality the module provides and sets up the kernel to run the module's functions when they're needed. Once it does this, entry function returns and the module does nothing until the kernel wants to do something with the code that the module provides.
@@ -1593,9 +1593,9 @@ Every module must have an entry function and an exit function. Since there's morProgrammers use functions they don't define all the time. A prime example of this is printf(). You use these library functions which are provided by the standard C library, libc. The definitions for these functions don't actually enter your program until the linking stage, which insures that the code (for printf() for example) is available, and fixes the call instruction to point to that code.
@@ -1630,9 +1630,9 @@ You can even write modules to replace the kernel's system calls, which we'll doA kernel is all about access to resources, whether the resource in question happens to be a video card, a hard drive or even memory. Programs often compete for the same resource. As I just saved this document, updatedb started updating the locate database. My vim session and updatedb are both using the hard drive concurrently. The kernel needs to keep things orderly, and not give users access to resources whenever they feel like it. To this end, a CPU can run in different modes. Each mode gives a different level of freedom to do what you want on the system. The Intel 80386 architecture has 4 of these modes, which are called rings. Unix uses only two rings; the highest ring (ring 0, also known as `supervisor mode' where everything is allowed to happen) and the lowest ring, which is called `user mode'.
@@ -1643,9 +1643,9 @@ Recall the discussion about library functions vs system calls. Typically, you usWhen you write a small C program, you use variables which are convenient and make sense to the reader. If, on the other hand, you're writing routines which will be part of a bigger problem, any global variables you have are part of a community of other peoples' global variables; some of the variable names can clash. When a program has lots of global variables which aren't meaningful enough to be distinguished, you get namespace pollution. In large projects, effort must be made to remember reserved names, and to find ways to develop a scheme for naming unique variable names and symbols.
@@ -1660,9 +1660,9 @@ The file /proc/kallsyms holds all the symbols that the kernel knows aboutMemory management is a very complicated subject—the majority of O'Reilly's `Understanding The Linux Kernel' is just on memory management! We're not setting out to be experts on memory managements, but we do need to know a couple of facts to even begin worrying about writing real modules.
@@ -1681,15 +1681,15 @@ By the way, I would like to point out that the above discussion is true for anyOne class of module is the device driver, which provides functionality for hardware like a TV card or a serial port. On unix, each piece of hardware is represented by a file located in /dev named a device file which provides the means to communicate with the hardware. The device driver provides the communication on behalf of a user program. So the es1370.o sound card device driver might connect the /dev/sound device file to the Ensoniq IS1370 sound card. A userspace program like mp3blaster can use /dev/sound without ever knowing what kind of sound card is installed.
The file_operations structure is defined in linux/fs.h, and holds pointers to functions defined by the driver that perform various operations on the device. Each field of the structure corresponds to the address of some function defined by the driver to handle a requested operation.
@@ -1850,9 +1850,9 @@ An instance of struct file_operations containing pointers to functions that areEach device is represented in the kernel by a file structure, which is defined in linux/fs.h. Be aware that a file is a kernel level structure and never appears in a user space program. It's not the same thing as a FILE, which is defined by glibc and would never appear in a kernel space function. Also, its name is a bit misleading; it represents an abstract open `file', not a file on a disk, which is represented by a structure named inode.
@@ -1867,9 +1867,9 @@ Go ahead and look at the definition of file. Most of the entries you see, like sAs discussed earlier, char devices are accessed through device files, usually located in /dev[7]. The major number tells you which driver handles which device file. The minor number is used only by the driver itself to differentiate which device it's operating on, just in case the driver handles more than one device.
@@ -1898,9 +1898,9 @@ If you pass a major number of 0 to register_chrdev, the return value will be theWe can't allow the kernel module to be rmmod'ed whenever root feels like it. If the device file is opened by a process and then we remove the kernel module, using the file would cause a call to the memory location where the appropriate function (read/write) used to be. If we're lucky, no other code was loaded there, and we'll get an ugly error message. If we're unlucky, another kernel module was loaded into the same location, which means a jump into the middle of another function within the kernel. The results of this would be impossible to predict, but they can't be very positive.
@@ -1921,9 +1921,9 @@ It's important to keep the counter accurate; if you ever do lose track of the coThe next code sample creates a char driver named chardev. You can cat its device file.
@@ -1939,9 +1939,9 @@ cat /proc/devices#+BEGIN_SRC: c /* @@ -1968,7 +1968,7 @@ cat /proc/devices
-*/ + */ int init_module(void); void cleanup_module(void); static int device_open(struct inode *, struct file *); @@ -2021,10 +2021,10 @@ static struct file_operations fops = {
-*/ + */ int init_module(void) { - Major = register_chrdev(0, DEVICE_NAME, &fops); + Major = register_chrdev(0, DEVICE_NAME, &fops);
@@ -2044,7 +2044,7 @@ printk(KERN_INFO "Remove the device file and module when done.\n");
-return SUCCESS; + return SUCCESS; }
@@ -2055,17 +2055,17 @@ return SUCCESS;-*/ + */ void cleanup_module(void) { - /* + /*
- */ -unregister_chrdev(Major, DEVICE_NAME); + */ + unregister_chrdev(Major, DEVICE_NAME); }
@@ -2087,10 +2087,10 @@ unregister_chrdev(Major, DEVICE_NAME);-*/ + */ static int device_open(struct inode *inode, struct file *file) { - static int counter = 0; + static int counter = 0;
@@ -2106,7 +2106,7 @@ try_module_get(THIS_MODULE);
-return SUCCESS; + return SUCCESS; }
@@ -2117,10 +2117,10 @@ return SUCCESS;-*/ + */ static int device_release(struct inode *inode, struct file *file) { - Device_Open–; * We're now ready for our next caller * + Device_Open–; * We're now ready for our next caller *
@@ -2136,7 +2136,7 @@ module_put(THIS_MODULE);
-return 0; + return 0; }
@@ -2148,13 +2148,13 @@ return 0;-/ + / static ssize_t device_read(struct file *filp, / see include/linux/fs.h / - char *buffer, / buffer to fill with data / - size_t length, / length of the buffer */ - loff_t * offset) + char *buffer, / buffer to fill with data / + size_t length, / length of the buffer */ + loff_t * offset) { - /* + /*
- */ -return bytes_read; + */ + return bytes_read; }
@@ -2227,12 +2227,12 @@ return bytes_read;-*/ + */ static ssize_t device_write(struct file *filp, const char *buff, size_t len, loff_t * off) { - printk(KERN_ALERT "Sorry, this operation isn't supported.\n"); - return -EINVAL; + printk(KERN_ALERT "Sorry, this operation isn't supported.\n"); + return -EINVAL; } #+END_SRC
@@ -2240,9 +2240,9 @@ device_write(struct file *filp, const char *buff, size_t len, loff_t * off)The system calls, which are the major interface the kernel shows to the processes, generally stay the same across versions. A new system call may be added, but usually the old ones will behave exactly like they used to. This is necessary for backward compatibility – a new kernel version is not supposed to break regular processes. In most cases, the device files will also remain the same. On the other hand, the internal interfaces within the kernel can and do change between versions.
@@ -2266,9 +2266,9 @@ Update: What we've said above was true for kernels up to and including 2.6.10. YIn Linux, there is an additional mechanism for the kernel and kernel modules to send information to processes — the /proc file system. Originally designed to allow easy access to information about processes (hence the name), it is now used by every bit of the kernel which has something interesting to report, such as /proc/modules which provides the list of modules and /proc/meminfo which stats memory usage statistics.
@@ -2305,9 +2305,9 @@ HelloWorld!#+BEGIN_SRC: c /* @@ -2338,7 +2338,7 @@ HelloWorld!
-*/ + */ struct proc_dir_entry *Our_Proc_File;
@@ -2386,13 +2386,13 @@ struct proc_dir_entry *Our_Proc_File;-*/ + */ int procfile_read(char *buffer, - char **buffer_location, - off_t offset, int buffer_length, int *eof, void *data) + char **buffer_location, + off_t offset, int buffer_length, int *eof, void *data) { - int ret; + int ret;
@@ -2426,7 +2426,7 @@ if (offset > 0) {
-return ret; + return ret; }
@@ -2455,8 +2455,8 @@ Our_Proc_File->size = 37;-printk(KERN_INFO "proc/%s created\n", procfs_name); -return 0; /* everything is ok * + printk(KERN_INFO "proc/%s created\n", procfs_name); + return 0; /* everything is ok * }
@@ -2471,9 +2471,9 @@ void cleanup_module()We have seen a very simple example for a /proc file where we only read the file /proc/helloworld. It's also possible to write in a /proc file. It works the same way as read, a function is called when the /proc file is written. But there is a little difference with read, data comes from user, so you have to import data from user space to kernel space (with copy_from_user or get_user)
@@ -2487,9 +2487,9 @@ The only memory segment accessible to a process is its own, so when writing regu#+BEGIN_SRC: c /** @@ -2522,7 +2522,7 @@ The only memory segment accessible to a process is its own, so when writing regu
-*/ + */ static struct proc_dir_entry *Our_Proc_File;
@@ -2534,7 +2534,7 @@ static struct proc_dir_entry *Our_Proc_File;-*/ + */ static char procfs_buffer[PROCFS_MAX_SIZE];
@@ -2546,7 +2546,7 @@ static char procfs_buffer[PROCFS_MAX_SIZE];-*/ + */ static unsigned long procfs_buffer_size = 0;
@@ -2558,13 +2558,13 @@ static unsigned long procfs_buffer_size = 0;-*/ + */ int procfile_read(char *buffer, - char **buffer_location, - off_t offset, int buffer_length, int *eof, void *data) + char **buffer_location, + off_t offset, int buffer_length, int *eof, void *data) { - int ret; + int ret;
@@ -2583,7 +2583,7 @@ if (offset > 0) {
-return ret; + return ret; }
@@ -2595,15 +2595,15 @@ return ret;-*/ + */ int procfile_write(struct file *file, const char *buffer, unsigned long count, - void *data) + void *data) { - * get buffer size * - procfs_buffer_size = count; - if (procfs_buffer_size > PROCFS_MAX_SIZE ) { - procfs_buffer_size = PROCFS_MAX_SIZE; - } + * get buffer size * + procfs_buffer_size = count; + if (procfs_buffer_size > PROCFS_MAX_SIZE ) { + procfs_buffer_size = PROCFS_MAX_SIZE; + }
@@ -2614,7 +2614,7 @@ if ( copy_from_user(procfs_buffer, buffer, procfs_buffer_size) ) {
-return procfs_buffer_size; + return procfs_buffer_size; }
@@ -2626,11 +2626,11 @@ return procfs_buffer_size;-*/ + */ int init_module() { - * create the /proc file * - Our_Proc_File = proc_create(PROCFS_NAME, 0, NULL, NULL); + * create the /proc file * + Our_Proc_File = proc_create(PROCFS_NAME, 0, NULL, NULL);
@@ -2652,8 +2652,8 @@ Our_Proc_File->size = 37;
-printk(KERN_INFO "proc/%s created\n", PROCFS_NAME); -return 0; /* everything is ok * + printk(KERN_INFO "proc/%s created\n", PROCFS_NAME); + return 0; /* everything is ok * }
@@ -2665,11 +2665,11 @@ return 0; /* everything is ok *-*/ + */ void cleanup_module() { - remove_proc_entry(PROCFS_NAME, NULL); - printk(KERN_INFO "/proc/%s removed\n", PROCFS_NAME); + remove_proc_entry(PROCFS_NAME, NULL); + printk(KERN_INFO "/proc/%s removed\n", PROCFS_NAME); } #+END_SRC
@@ -2677,9 +2677,9 @@ void cleanup_module()We have seen how to read and write a /proc file with the /proc interface. But it's also possible to manage /proc file with inodes. The main interest is to use advanced function, like permissions.
@@ -2697,9 +2697,9 @@ It's important to note that the standard roles of read and write are reversed in#+BEGIN_SRC: c /* @@ -2733,7 +2733,7 @@ It's important to note that the standard roles of read and write are reversed in
-*/ + */ static char procfs_buffer[PROCFS_MAX_SIZE];
@@ -2745,7 +2745,7 @@ static char procfs_buffer[PROCFS_MAX_SIZE];-*/ + */ static unsigned long procfs_buffer_size = 0;
@@ -2757,7 +2757,7 @@ static unsigned long procfs_buffer_size = 0;-*/ + */ static struct proc_dir_entry *Our_Proc_File;
@@ -2769,13 +2769,13 @@ static struct proc_dir_entry *Our_Proc_File;-/ + / static ssize_t procfs_read(struct file *filp, / see include/linux/fs.h / - char *buffer, / buffer to fill with data / - size_t length, / length of the buffer */ - loff_t * offset) + char *buffer, / buffer to fill with data / + size_t length, / length of the buffer */ + loff_t * offset) { - static int finished = 0; + static int finished = 0;
@@ -2820,7 +2820,7 @@ printk(KERN_INFO "procfs_read: read %lu bytes\n", procfs_buffer_size);
-return procfs_buffer_size; * Return the number of bytes "read" * + return procfs_buffer_size; * Return the number of bytes "read" * }
@@ -2831,16 +2831,16 @@ return procfs_buffer_size; * Return the number of bytes "read" *-*/ + */ static ssize_t procfs_write(struct file *file, const char *buffer, size_t len, loff_t * off) { - if ( len > PROCFS_MAX_SIZE ) { - procfs_buffer_size = PROCFS_MAX_SIZE; - } - else { - procfs_buffer_size = len; - } + if ( len > PROCFS_MAX_SIZE ) { + procfs_buffer_size = PROCFS_MAX_SIZE; + } + else { + procfs_buffer_size = len; + }
@@ -2854,7 +2854,7 @@ printk(KERN_INFO "procfs_write: write %lu bytes\n", procfs_buffer_size);
-return procfs_buffer_size; + return procfs_buffer_size; }
@@ -2878,10 +2878,10 @@ return procfs_buffer_size;-*/ + */ static int module_permission(struct inode *inode, int op) { - /* + /*
= 4 || (op = 2 && current_euid() == 0))
- */ -return -EACCES; + */ + return -EACCES; }
@@ -2914,11 +2914,11 @@ return -EACCES;-*/ + */ int procfs_open(struct inode *inode, struct file *file) { - try_module_get(THIS_MODULE); - return 0; + try_module_get(THIS_MODULE); + return 0; }
@@ -2930,11 +2930,11 @@ int procfs_open(struct inode *inode, struct file *file)-*/ + */ int procfs_close(struct inode *inode, struct file *file) { - module_put(THIS_MODULE); - return 0; * success * + module_put(THIS_MODULE); + return 0; * success * }
@@ -2976,11 +2976,11 @@ static struct inode_operations Inode_Ops_4_Our_Proc_File = {-*/ + */ int init_module() { - * create the /proc file * - Our_Proc_File = proc_create(PROC_ENTRY_FILENAME, 0644, NULL, NULL); + * create the /proc file * + Our_Proc_File = proc_create(PROC_ENTRY_FILENAME, 0644, NULL, NULL);
@@ -3006,7 +3006,7 @@ printk(KERN_INFO "/proc/%s created\n", PROC_ENTRY_FILENAME);
-return 0; * success * + return 0; * success * }
@@ -3026,9 +3026,9 @@ Still hungry for procfs examples? Well, first of all keep in mind, there are rumAs we have seen, writing a /proc file may be quite "complex". So to help people writting /proc file, there is an API named seq_file that helps @@ -3046,9 +3046,9 @@ BE CARREFUL: when a sequence is finished, another one starts. That means that at