Operating Systems 2017F Lecture 16: Difference between revisions
No edit summary |
|||
(2 intermediate revisions by 2 users not shown) | |||
Line 4: | Line 4: | ||
==Code== | ==Code== | ||
[http://homeostasis.scs.carleton.ca/~soma/os-2017f/code/lec16/newgetpid.zip Code for download] | |||
==newgetpid.c== | ==newgetpid.c== | ||
Line 161: | Line 163: | ||
$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules | $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules | ||
</source> | </source> | ||
=== Additional Notes === | |||
What determines what files you can and cannot create?<br> | |||
* Ssh privileges <br> | |||
<br> | |||
* Anything that you can do as an ssh user you can do in the vm, just doing file operations<br> | |||
* Sshfs means when you do read and write system calls for programs in a directory it also does it on the remote machine<br> | |||
* Ssh is a good tool to access remote files locally, we will use it to edit modules<br> | |||
<br> | |||
Ones/newgetpid program:<br> | |||
* We want to extend its functionality in a specific way<br> | |||
* We want to access info about the process that made the system call<br> | |||
* Let's get current processes id<br> | |||
** Normally we would use getpid<br> | |||
** But we can't make system calls in kernel space<br> | |||
** But we can call the function that the system call uses or just copy the functionality<br> | |||
* Code for this is in "kernel/sys.c"<br> | |||
<br> | |||
Syscall_define0:<br> | |||
* Macro that expands<br> | |||
* Defines system calls with "getpid" and takes no arguments<br> | |||
<br> | |||
* Can use the code inside the function but not the function itself in the kernel<br> | |||
* Getpid returns a pid_t<br> | |||
* Instead of get_ones returning all those ones we want it to return the pid<br> | |||
* Lets try to get it to output a basic string with the pid<br> | |||
<br> | |||
How does printk work?<br> | |||
* Printk sends its output to the kernel log<br> | |||
* We changed the name to "newgetpid"<br> | |||
* How do we convert int to string to print the pid?<br> | |||
** Make a buffer, let's call it "message"<br> | |||
<br> | |||
Why does the pid keep incrementing each time we call "cat /dev/newgetpid"?<br> | |||
* "Cat" spawns a process so every time we do a fork we get a new pid<br> | |||
<br> | |||
Why can we use snprintf but not printf?<br> | |||
* We include "linux/kernel.h" which defines snprintf but not printf<br> | |||
* Printf assumes we have a standard output to print to<br> | |||
* Snprintf only needs character arrays for it to work<br> | |||
<br> | |||
* Read functionality uses an API<br> | |||
* Adding new functionality like "write" is easy, just look at the standard API and original kernel source<br> | |||
* All device files have their own custom read and write functions<br> | |||
<br> | |||
Why do we use goto's?<br> | |||
* C has no exception handling functionality so we implement our own<br> | |||
* Jumps to error paths: failed_devreg, failed_classreg, failed_chrdevreg (very important in the kernel)<br> | |||
* Kernel needs to be able to handle it's own errors<br> | |||
* Needs to free up allocated resources, "undo" everything<br> | |||
<br> | |||
* "." is the current directory<br> | |||
* ".." is the parent directory<br> | |||
** Introduces another hardlink<br> | |||
<br> | |||
* In order to build kernel modules, you need to have the headers associated with the current kernel you're running | |||
* Modules are specified to a particular version of the kernel |
Latest revision as of 08:38, 24 November 2017
Video
The video from the lecture given on Nov. 7, 2017 is now available.
Code
newgetpid.c
/* Code derived from:
https://appusajeev.wordpress.com/2011/06/18/writing-a-linux-character-device-driver/
and
http://pete.akeo.ie/2011/08/writing-linux-device-driver-for-kernels.html
*/
#include <linux/module.h>
#include <linux/string.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <asm/uaccess.h>
#define dbg(format, arg...) do { if (debug) pr_info(CLASS_NAME ": %s: " format, __FUNCTION__, ## arg); } while (0)
#define err(format, arg...) pr_err(CLASS_NAME ": " format, ## arg)
#define info(format, arg...) pr_info(CLASS_NAME ": " format, ## arg)
#define warn(format, arg...) pr_warn(CLASS_NAME ": " format, ## arg)
#define DEVICE_NAME "newgetpid"
#define CLASS_NAME "comp3000"
static struct class* newgetpid_class = NULL;
static struct device* newgetpid_device = NULL;
static int newgetpid_major;
static int newgetpid_open(struct inode *the_inode, struct file *f)
{
return 0;
}
static ssize_t newgetpid_read(struct file *f, char *buf, size_t len, loff_t *offset)
{
size_t i, msglen;
pid_t thepid;
char message[100];
if (*offset > 0) {
return 0;
}
thepid = task_tgid_vnr(current);
snprintf(message, 100, "Your PID is %d!\n", thepid);
msglen = strlen(message);
if (len < msglen) {
msglen = len;
}
for (i = 0; i < msglen; i++) {
put_user(message[i], buf++);
}
*offset = i;
return i;
}
static int newgetpid_release(struct inode *the_inode, struct file *f)
{
printk(KERN_ALERT "Newgetpid device closed\n");
return 0;
}
static struct file_operations newgetpid_fops = {
.open = newgetpid_open,
.read = newgetpid_read,
.release = newgetpid_release,
};
static char *newgetpid_devnode(struct device *dev, umode_t *mode)
{
if (mode)
*mode = 0444;
return NULL;
}
static int __init newgetpid_init(void)
{
int retval;
newgetpid_major = register_chrdev(0, DEVICE_NAME, &newgetpid_fops);
if (newgetpid_major < 0) {
err("failed to register device: error %d\n", newgetpid_major);
retval = newgetpid_major;
goto failed_chrdevreg;
}
newgetpid_class = class_create(THIS_MODULE, CLASS_NAME);
if (IS_ERR(newgetpid_class)) {
err("failed to register device class '%s'\n", CLASS_NAME);
retval = PTR_ERR(newgetpid_class);
goto failed_classreg;
}
newgetpid_class->devnode = newgetpid_devnode;
newgetpid_device = device_create(newgetpid_class, NULL, MKDEV(newgetpid_major, 0),
NULL, DEVICE_NAME);
if (IS_ERR(newgetpid_device)) {
err("failed to create device '%s'\n", DEVICE_NAME);
retval = PTR_ERR(newgetpid_device);
goto failed_devreg;
}
info("Newgetpid device registered using major %d.\n", newgetpid_major);
return 0;
failed_devreg:
class_unregister(newgetpid_class);
class_destroy(newgetpid_class);
failed_classreg:
unregister_chrdev(newgetpid_major, DEVICE_NAME);
failed_chrdevreg:
return -1;
}
static void __exit newgetpid_exit(void)
{
device_destroy(newgetpid_class, MKDEV(newgetpid_major, 0));
class_unregister(newgetpid_class);
class_destroy(newgetpid_class);
unregister_chrdev(newgetpid_major, "newgetpid");
info("Unloading Newgetpid module.\n");
return;
}
module_init(newgetpid_init);
module_exit(newgetpid_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Anil Somayaji <soma@scs.carleton.ca>");
MODULE_DESCRIPTION("A write newgetpid character device module");
Makefile
obj-m := newgetpid.o
KDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
default:
$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules
Additional Notes
What determines what files you can and cannot create?
- Ssh privileges
- Anything that you can do as an ssh user you can do in the vm, just doing file operations
- Sshfs means when you do read and write system calls for programs in a directory it also does it on the remote machine
- Ssh is a good tool to access remote files locally, we will use it to edit modules
Ones/newgetpid program:
- We want to extend its functionality in a specific way
- We want to access info about the process that made the system call
- Let's get current processes id
- Normally we would use getpid
- But we can't make system calls in kernel space
- But we can call the function that the system call uses or just copy the functionality
- Normally we would use getpid
- Code for this is in "kernel/sys.c"
Syscall_define0:
- Macro that expands
- Defines system calls with "getpid" and takes no arguments
- Can use the code inside the function but not the function itself in the kernel
- Getpid returns a pid_t
- Instead of get_ones returning all those ones we want it to return the pid
- Lets try to get it to output a basic string with the pid
How does printk work?
- Printk sends its output to the kernel log
- We changed the name to "newgetpid"
- How do we convert int to string to print the pid?
- Make a buffer, let's call it "message"
- Make a buffer, let's call it "message"
Why does the pid keep incrementing each time we call "cat /dev/newgetpid"?
- "Cat" spawns a process so every time we do a fork we get a new pid
Why can we use snprintf but not printf?
- We include "linux/kernel.h" which defines snprintf but not printf
- Printf assumes we have a standard output to print to
- Snprintf only needs character arrays for it to work
- Read functionality uses an API
- Adding new functionality like "write" is easy, just look at the standard API and original kernel source
- All device files have their own custom read and write functions
Why do we use goto's?
- C has no exception handling functionality so we implement our own
- Jumps to error paths: failed_devreg, failed_classreg, failed_chrdevreg (very important in the kernel)
- Kernel needs to be able to handle it's own errors
- Needs to free up allocated resources, "undo" everything
- "." is the current directory
- ".." is the parent directory
- Introduces another hardlink
- Introduces another hardlink
- In order to build kernel modules, you need to have the headers associated with the current kernel you're running
- Modules are specified to a particular version of the kernel