每天推薦一個 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