Operating Systems 2017F Lecture 16: Difference between revisions

From Soma-notes
No edit summary
 
(One intermediate revision by the same user not shown)
Line 163: 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

Code for download

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
  • 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"


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


  • 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