每天推荐一个 GitHub 优质开源项目和一篇精选英文科技或编程文章原文,欢迎关注开源日报。交流QQ群:202790710;电报群 https://t.me/OpeningSourceOrg
今日推荐开源项目:《parcel——快速给你的网络应用打包》
GitHub地址:https://github.com/parcel-bundler/parcel
推荐理由:非常快,不需要配置的网络应用打包器。
特点:
非常快——打包时间快,多核编译,一个文件系统的存储器的重建甚至只需要花费一次重启的时间就能完成。创造性的支持JS, CSS, HTML, file assets,而且不需要安装插件。在使用Babel, PostCSS, PostHTML甚至no_module时可以在需要时自动转变模块。在使用动态import()声明时没有配置代码的分离。建立时支持热插拔。友好的记录错误的方式——语法会自动高亮代码框架帮助找出问题
使用parcel的优点:
在使用其他打包器时都会有配置文件和插件,配置文件会占用很多时间,它们不仅复杂而难以调配,而且在每个应用打包时都要再配置一次,而parcel的零配置可以让你只要给它一个入口文件,就可以让它做正确的事。
parcel运用多核处理器去平行编译你的代码,这会在开始打包时带来巨大的速度提升,而且它会用自己的文件系统存储编译后的代码,使得随后的运行更为迅速。
现存的打包器一般都会在运行时在一个文件内进行大量的代码转换与生成,这会导致效率的下降,相反parcel使用抽象语法树AST来进行转换,保证每个文件只需要一种语法解析,许多转换,一条代码生成。
parcel的工作原理:
许多打包器都是基于JS文件与一种其他格式,但是parcel不会知道文件的格式,它将不需要配置的对你期望的任何文件展开工作。有许多格式的文件会在parcel中有特别的定义——如何将他们转变成特殊的文件类型。
parcel会将一个资源树转化为一个打包树,在资源树完成时,资源将会被放入打包树中,一个包将会创建给入口文件,而子包会按照动态输入来创建,这导致了代码的分离,子包在文件以不同格式输入时也会被创建。如果一个资源需要更多的包,它将被运到打包树中最近的资源来源,保证一个文件只有一个包。
在包树生成后,每个包将被依照文件格式决定的打包器写入文件,这些打包器知道如何联合这些文件代码为最终在浏览器上读取的文件。
今日推荐英文原文:《Writing a Simple Linux Kernel Module》作者:Robert W. Oliver II
原文链接:https://blog.sourcerer.io/writing-a-simple-linux-kernel-module-d9dc3762c234
推荐理由:Linux 提供了强大的可扩展的 API ,但是有时候又是不够的,跟某些硬件交互或者一些系统操作可能需要一个 Linux 内核模块
Writing a Simple Linux Kernel Module
Linux provides a powerful and expansive API for applications, but sometimes that’s not enough. Interacting with a piece of hardware or conducting operations that require accessed to privileged information in the system require a kernel module.
A Linux kernel module is a piece of compiled binary code that is inserted directly into the Linux kernel, running at ring 0, the lowest and least protected ring of execution in the x86–64 processor. Code here runs completely unchecked but operates at incredible speed and has access to everything in the system.
Not for Mere Mortals
Writing a Linux kernel module is not for the faint of heart. By altering the kernel, you risk data loss and system corruption. Kernel code doesn’t have the usual safety net that regular Linux applications enjoy. If you have a fault, it will lock up the entire system.
To make matters worse, your issue may not become immediately apparent. Your module locking up immediately upon loading is probably the best-case scenario for failure. As you add more code to your module, you run the risk of introducing runaway loops and memory leaks. If you’re not careful, these can continue to grow as your machine continues to run. Eventually important memory structures and even buffers can be overwritten.
Traditional application development paradigms can be largely discarded. Other than loading and unloading of your module, you’ll be writing code that responds to system events rather than operates in a sequential pattern. With kernel development, you’re writing APIs, not applications themselves.
You also have no access to the standard library. While the kernel provides some functions like printk (which serves as a replacement for printf) and kmalloc (which operates in a similar fashion to malloc), you are largely left to your own devices. Additionally, when your module unloads, you are responsible for completely cleaning up after yourself. There is no garbage collection.
Prerequisites
Before we get started, we need to make sure we have the correct tools for the job. Most importantly, you’ll need a Linux machine. I know that comes as a complete surprise! While any Linux distribution will do, I am using Ubuntu 16.04 LTS in this example, so if you’re using a different distribution you may need to slightly adjust your installation commands.
Secondly, you’ll need either a separate physical machine or a virtual machine. I prefer to do my work in a virtual machine, but this is entirely up to you. I don’t suggest using your primary machine because data loss can occur when you make a mistake. I say when, not if, because you undoubtedly will lock up your machine at least a few times during the process. Your latest code changes may still be in the write buffer when the kernel panics, so it’s possible that your source files can become corrupted. Testing in a virtual machine eliminates this risk.
And finally, you’ll need to know at least some C. The C++ runtime is far too large for the kernel, so writing bare metal C is essential. For interaction with hardware, knowing some assembly might be helpful.
Installing the Development Environment
On Ubuntu, we need to run:
apt-get install build-essential linux-headers-`uname -r`
This will install the essential development tools and the kernel headers necessary for this example.
The examples below assume you are running as a regular user and not root, but that you have sudo privileges. Sudo is mandatory for loading kernel modules, but we want to work outside of root whenever possible.
Getting Started
Let’s start writing some code. Let’s prepare our environment:
mkdir ~/src/lkm_example cd ~/src/lkm_example
Fire up your favorite editor (in my case, this is vim) and create the file lkm_example.c with the following contents:
#include <linux/init.h> #include <linux/module.h> #include <linux/kernel.h>
MODULE_LICENSE(“GPL”); MODULE_AUTHOR(“Robert W. Oliver II”); MODULE_DESCRIPTION(“A simple example Linux module.”); MODULE_VERSION(“0.01”);
static int __init lkm_example_init(void) { printk(KERN_INFO “Hello, World!\n”); return 0; }
static void __exit lkm_example_exit(void) { printk(KERN_INFO “Goodbye, World!\n”); }
module_init(lkm_example_init); module_exit(lkm_example_exit);
Now that we’ve constructed the simplest possible module, let’s example the important parts in detail:
· The “includes” cover the required header files necessary for Linux kernel development.
· MODULE_LICENSE can be set to a variety of values depending on the license of the module. To see a full list, run:
grep “MODULE_LICENSE” -B 27 /usr/src/linux-headers-`uname -r`/include/linux/module.h
· We define both the init (loading) and exit (unloading) functions as static and returning an int.
· Note the use of printk instead of printf. Also, printk doesn’t share the same parameters as printf. For example, the KERN_INFO, which is a flag to declare what priority of logging should be set for this line, is defined without a comma. The kernel sorts this out inside the printk function to save stack memory.
· At the end of the file, we call module_init and module_exit to tell the kernel which functions are or loading and unloading functions. This gives us the freedom to name the functions whatever we like.
We can’t compile this file yet, though. We need a Makefile. This basic example will work for now. Note that make is very picky about spaces and tabs, so ensure you use tab instead of space where appropriate.
obj-m += lkm_example.o
all: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
If we run “make”, it should compile your module successfully. The resulting file is “lkm_example.ko”. If you receive any errors, check that your quotation marks in the example source file are correct and not pasted accidentally as UTF-8 characters.
Now we can insert the module to test it. To do this, run:
sudo insmod lkm_example.ko
If all goes well, you won’t see a thing. The printk function doesn’t output to the console but rather the kernel log. To see that, we’ll need to run:
sudo dmesg
You should see the “Hello, World!” line prefixed by a timestamp. This means our kernel module loaded and successfully printed to the kernel log. We can also check to see if the module is still loaded:
lsmod | grep “lkm_example”
To remove the module, run:
sudo rmmod lkm_example
If you run dmesg again, you’ll see “Goodbye, World!” in the logs. You can also use lsmod again to confirm it was unloaded.
As you can see, this testing workflow is a bit tedious, so to automate this we can add:
test: sudo dmesg -C sudo insmod lkm_example.ko sudo rmmod lkm_example.ko dmesg
at the end of our Makefile and now run:
make test
to test our module and see the output of the kernel log without having to run separate commands.
Now we have a fully functional, yet completely trivial, kernel module!
A Bit More Interesting
Let’s dive a bit deeper. While kernel modules can accomplish all sorts of tasks, interacting with applications is one of their most common uses.
Since applications are restricted from viewing the contents of kernel space memory, applications must use an API to communicate with them. While there are technically multiple ways to accomplish this, the most common is to create a device file.
You’ve likely interacted with device files before. Commands that use /dev/zero, /dev/null, or similar are interacting with a device named “zero” and “null” that return the expected values.
In our example, we’ll return “Hello, World”. While this isn’t a particularly useful function to provide applications, it will nevertheless show the process of responding to an application via a device file.
Here’s our complete listing:
#include <linux/init.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/fs.h> #include <asm/uaccess.h>
MODULE_LICENSE(“GPL”); MODULE_AUTHOR(“Robert W. Oliver II”); MODULE_DESCRIPTION(“A simple example Linux module.”); MODULE_VERSION(“0.01”);
#define DEVICE_NAME “lkm_example” #define EXAMPLE_MSG “Hello, World!\n” #define MSG_BUFFER_LEN 15
/* Prototypes for device functions */ static int device_open(struct inode *, struct file *); static int device_release(struct inode *, struct file *); static ssize_t device_read(struct file *, char *, size_t, loff_t *); static ssize_t device_write(struct file *, const char *, size_t, loff_t *);
static int major_num; static int device_open_count = 0; static char msg_buffer[MSG_BUFFER_LEN]; static char *msg_ptr;
/* This structure points to all of the device functions */ static struct file_operations file_ops = { .read = device_read, .write = device_write, .open = device_open, .release = device_release };
/* When a process reads from our device, this gets called. */ static ssize_t device_read(struct file *flip, char *buffer, size_t len, loff_t *offset) { int bytes_read = 0; /* If we’re at the end, loop back to the beginning */ if (*msg_ptr == 0) { msg_ptr = msg_buffer; } /* Put data in the buffer */ while (len && *msg_ptr) { /* Buffer is in user data, not kernel, so you can’t just reference * with a pointer. The function put_user handles this for us */ put_user(*(msg_ptr++), buffer++); len--; bytes_read++; } return bytes_read; }
/* Called when a process tries to write to our device */ static ssize_t device_write(struct file *flip, const char *buffer, size_t len, loff_t *offset) { /* This is a read-only device */ printk(KERN_ALERT “This operation is not supported.\n”); return -EINVAL; }
/* Called when a process opens our device */ static int device_open(struct inode *inode, struct file *file) { /* If device is open, return busy */ if (device_open_count) { return -EBUSY; } device_open_count++; try_module_get(THIS_MODULE); return 0; }
/* Called when a process closes our device */ static int device_release(struct inode *inode, struct file *file) { /* Decrement the open counter and usage count. Without this, the module would not unload. */ device_open_count--; module_put(THIS_MODULE); return 0; }
static int __init lkm_example_init(void) { /* Fill buffer with our message */ strncpy(msg_buffer, EXAMPLE_MSG, MSG_BUFFER_LEN); /* Set the msg_ptr to the buffer */ msg_ptr = msg_buffer; /* Try to register character device */ major_num = register_chrdev(0, “lkm_example”, &file_ops); if (major_num < 0) { printk(KERN_ALERT “Could not register device: %d\n”, major_num); return major_num; } else { printk(KERN_INFO “lkm_example module loaded with device major number %d\n”, major_num); return 0; } }
static void __exit lkm_example_exit(void) { /* Remember — we have to clean up after ourselves. Unregister the character device. */ unregister_chrdev(major_num, DEVICE_NAME); printk(KERN_INFO “Goodbye, World!\n”); }
/* Register module functions */ module_init(lkm_example_init); module_exit(lkm_example_exit);
Testing Our Enhanced Example
Now that our example does something more than simply print a message upon loading and unloading, we need a less restrictive test routine. Let’s modify our Makefile to only load the module and not unload it.
obj-m += lkm_example.o
all: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
test: # We put a — in front of the rmmod command to tell make to ignore # an error in case the module isn’t loaded. -sudo rmmod lkm_example # Clear the kernel log without echo sudo dmesg -C # Insert the module sudo insmod lkm_example.ko # Display the kernel log dmesg
Now when you run “make test”, you’ll see the output of the device’s major number. In our example, this is automatically assigned by the kernel. However, you’ll need this value to create the device.
Take the value you obtain from “make test” and use it to create a device file so that we can communicate with our kernel module from user space.
sudo mknod /dev/lkm_example c MAJOR 0
(in the above example, replace MAJOR with the value you obtain from “make test” or “dmesg”)
The “c” in the mknod command tells mknod that we need a character device file created.
Now we can grab content from the device:
cat /dev/lkm_example
or even via the “dd” command:
dd if=/dev/lkm_example of=test bs=14 count=100
You can also access this device via applications. They don’t have to be compiled applications — even Python, Ruby, and PHP scripts can access this data.
When we’re done with the device, delete it and unload the module:
sudo rm /dev/lkm_example sudo rmmod lkm_example
Conclusion
I hope you’ve enjoyed our romp through kernel land. Though the examples I’ve provided are basic, you can use this structure to construct your own module that does very complex tasks.
Just remember that you are completely on your own in kernel land. There are no backstops or second chances for your code. If you’re quoting a project for a client, be sure to double, if not triple, the anticipated debugging time. Kernel code has to be as perfect as possible to ensure the integrity and reliability of the systems that will run it.
每天推荐一个 GitHub 优质开源项目和一篇精选英文科技或编程文章原文,欢迎关注开源日报。交流QQ群:202790710;电报群 https://t.me/OpeningSourceOrg