xdp作为一个新生事物,目前好多东西都在探索。
以这个https://github.com/xdp-project/xdp-tutorial/tree/master/basic01-xdp-pass为例,基本上eBPF的程序由两部分组成,一个位于内核,一个位于用户空间。
xdp_pass_kern.c:
#include <linux/bpf.h>
#include "bpf_helpers.h"
SEC("xdp")
int xdp_prog_simple(struct xdp_md *ctx)
{
return XDP_PASS;
}
char _license[] SEC("license") = "GPL";
xdp_pass_user.c :
/* SPDX-License-Identifier: GPL-2.0 */
static const char *__doc__ = "Simple XDP prog doing XDP_PASS\n";
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <getopt.h>
#include <bpf/bpf.h>
#include <bpf/libbpf.h>
#include <net/if.h>
#include <linux/if_link.h> /* depend on kernel-headers installed */
#include "../common/common_params.h"
static const struct option_wrapper long_options[] = {
{
{"help", no_argument, NULL, 'h' },
"Show help", false},
{
{"dev", required_argument, NULL, 'd' },
"Operate on device <ifname>", "<ifname>", true},
{
{"skb-mode", no_argument, NULL, 'S' },
"Install XDP program in SKB (AKA generic) mode"},
{
{"native-mode", no_argument, NULL, 'N' },
"Install XDP program in native mode"},
{
{"auto-mode", no_argument, NULL, 'A' },
"Auto-detect SKB or native mode"},
{
{"force", no_argument, NULL, 'F' },
"Force install, replacing existing program on interface"},
{
{"unload", no_argument, NULL, 'U' },
"Unload XDP program instead of loading"},
{
{0, 0, NULL, 0 }, NULL, false}
};
int load_bpf_object_file__simple(const char *filename)
{
int first_prog_fd = -1;
struct bpf_object *obj;
int err;
/* Use libbpf for extracting BPF byte-code from BPF-ELF object, and
* loading this into the kernel via bpf-syscall
*/
err = bpf_prog_load(filename, BPF_PROG_TYPE_XDP, &obj, &first_prog_fd);
if (err) {
fprintf(stderr, "ERR: loading BPF-OBJ file(%s) (%d): %s\n",
filename, err, strerror(-err));
return -1;
}
/* Simply return the first program file descriptor.
* (Hint: This will get more advanced later)
*/
return first_prog_fd;
}
static int xdp_link_detach(int ifindex, __u32 xdp_flags)
{
/* Next assignment this will move into ../common/
* (in more generic version)
*/
int err;
if ((err = bpf_set_link_xdp_fd(ifindex, -1, xdp_flags)) < 0) {
fprintf(stderr, "ERR: link set xdp unload failed (err=%d):%s\n",
err, strerror(-err));
return EXIT_FAIL_XDP;
}
return EXIT_OK;
}
int xdp_link_attach(int ifindex, __u32 xdp_flags, int prog_fd)
{
/* Next assignment this will move into ../common/ */
int err;
/* libbpf provide the XDP net_device link-level hook attach helper */
err = bpf_set_link_xdp_fd(ifindex, prog_fd, xdp_flags);
if (err < 0) {
fprintf(stderr, "ERR: "
"ifindex(%d) link set xdp fd failed (%d): %s\n",
ifindex, -err, strerror(-err));
switch (-err) {
case EBUSY:
fprintf(stderr, "Hint: XDP already loaded on device"
" use --force to swap/replace\n");
break;
case EOPNOTSUPP:
fprintf(stderr, "Hint: Native-XDP not supported"
" use --skb-mode or --auto-mode\n");
break;
default:
break;
}
return EXIT_FAIL_XDP;
}
return EXIT_OK;
}
int main(int argc, char **argv)
{
struct bpf_prog_info info = {};
__u32 info_len = sizeof(info);
char filename[256] = "xdp_pass_kern.o";
int prog_fd, err;
struct config cfg = {
.xdp_flags = XDP_FLAGS_UPDATE_IF_NOEXIST | XDP_FLAGS_DRV_MODE,
.ifindex = -1,
.do_unload = false,
};
parse_cmdline_args(argc, argv, long_options, &cfg, __doc__);
/* Required option */
if (cfg.ifindex == -1) {
fprintf(stderr, "ERR: required option --dev missing\n");
usage(argv[0], __doc__, long_options, (argc == 1));
return EXIT_FAIL_OPTION;
}
if (cfg.do_unload)
return xdp_link_detach(cfg.ifindex, cfg.xdp_flags);
/* Load the BPF-ELF object file and get back first BPF_prog FD */
prog_fd = load_bpf_object_file__simple(filename);
if (prog_fd <= 0) {
fprintf(stderr, "ERR: loading file: %s\n", filename);
return EXIT_FAIL_BPF;
}
/* At this point: BPF-prog is (only) loaded by the kernel, and prog_fd
* is our file-descriptor handle. Next step is attaching this FD to a
* kernel hook point, in this case XDP net_device link-level hook.
* Fortunately libbpf have a helper for this:
*/
err = xdp_link_attach(cfg.ifindex, cfg.xdp_flags, prog_fd);
if (err)
return err;
/* This step is not really needed , BPF-info via bpf-syscall */
err = bpf_obj_get_info_by_fd(prog_fd, &info, &info_len);
if (err) {
fprintf(stderr, "ERR: can't get prog info - %s\n",
strerror(errno));
return err;
}
printf("Success: Loading "
"XDP prog name:%s(id:%d) on device:%s(ifindex:%d)\n",
info.name, info.id, cfg.ifname, cfg.ifindex);
return EXIT_OK;
}
Makefile:
XDP_TARGETS := xdp_pass_kern
USER_TARGETS := xdp_pass_user
LLC ?= llc
CLANG ?= clang
CC := gcc
LIBBPF_DIR = ../libbpf/src/
COMMON_DIR = ../common/
include $(COMMON_DIR)/common.mk
COMMON_OBJS := $(COMMON_DIR)/common_params.o
Makefile文件因为使用了libbpf库文件(或者说这个git仓库),思路都差不多的。
首先使用llvm编译xdp_pass_kern.o,这个文件是要注入到内核中去的,内核怎么保证这个代码的安全咱们这里无法展开详细说,怎么使用这个东西是咱们的重点。
加载。这里就是两种方式,第一种就是使用用户程序:xdp_pass_user.c,去生成可执行文件,具体来讲,The libbpf library provides both an ELF loader and several XDP helper functions.就是借助 libelf-dev库文件的某些机制去加载到内核。第二种方式就是使用iproute2命令.
Run: llvm-objdump -S xdp_pass_kern.o
xdp_pass_kern.o: file format ELF64-BPF
Disassembly of section xdp:
xdp_prog_simple:
; {
0: b7 00 00 00 02 00 00 00 r0 = 2
; return XDP_PASS;
1: 95 00 00 00 00 00 00 00 exit
## 用户空间加载 在libbpf/include/uapi/linux/bpf.h中,有以下定义。
This is a mirror of bpf-next linux tree
(https://kernel.googlesource.com/pub/scm/linux/kernel/git/bpf/bpf-next)
bpf-next/tools/lib/bpf directory plus its supporting header files.
The following files will by sync'ed with bpf-next repo:
src/ <-> bpf-next/tools/lib/bpf/
include/uapi/linux/bpf_common.h <-> bpf-next/tools/include/uapi/linux/bpf_common.h
include/uapi/linux/bpf.h <-> bpf-next/tools/include/uapi/linux/bpf.h
include/uapi/linux/btf.h <-> bpf-next/tools/include/uapi/linux/btf.h
include/uapi/linux/if_link.h <-> bpf-next/tools/include/uapi/linux/if_link.h
include/uapi/linux/if_xdp.h <-> bpf-next/tools/include/uapi/linux/if_xdp.h
include/uapi/linux/netlink.h <-> bpf-next/tools/include/uapi/linux/netlink.h
include/tools/libc_compat.h <-> bpf-next/tools/include/tools/libc_compat.h
Other header files at this repo (include/linux/*.h) are reduced versions of
their counterpart files at bpf-next/tools/include/linux/*.h to make compilation
successful.
bpf_prog_info来自bpf.h
struct bpf_prog_info {
__u32 type;
__u32 id;
__u8 tag[BPF_TAG_SIZE];
__u32 jited_prog_len;
__u32 xlated_prog_len;
__aligned_u64 jited_prog_insns;
__aligned_u64 xlated_prog_insns;
__u64 load_time; /* ns since boottime */
__u32 created_by_uid;
__u32 nr_map_ids;
__aligned_u64 map_ids;
char name[BPF_OBJ_NAME_LEN];
__u32 ifindex;
__u32 gpl_compatible:1;
__u64 netns_dev;
__u64 netns_ino;
__u32 nr_jited_ksyms;
__u32 nr_jited_func_lens;
__aligned_u64 jited_ksyms;
__aligned_u64 jited_func_lens;
__u32 btf_id;
__u32 func_info_rec_size;
__aligned_u64 func_info;
__u32 nr_func_info;
__u32 nr_line_info;
__aligned_u64 line_info;
__aligned_u64 jited_line_info;
__u32 nr_jited_line_info;
__u32 line_info_rec_size;
__u32 jited_line_info_rec_size;
__u32 nr_prog_tags;
__aligned_u64 prog_tags;
__u64 run_time_ns;
__u64 run_cnt;
} __attribute__((aligned(8)));
还有一个config来自哪里?来自common/common_defines.h
struct config {
__u32 xdp_flags;
int ifindex;
char *ifname;
char ifname_buf[IF_NAMESIZE];
bool do_unload;
char filename[512];
char progsec[32];
};
parse_cmdline_args(argc, argv, long_options, &cfg, __doc__);
很明显来自一个库文件。
prog_fd = load_bpf_object_file__simple(filename);
/* At this point: BPF-prog is (only) loaded by the kernel, and prog_fd
* is our file-descriptor handle. Next step is attaching this FD to a
* kernel hook point, in this case XDP net_device link-level hook.
* Fortunately libbpf have a helper for this:
*/
也许xdp可以指定在哪个设备上。
The C structs corresponding to the libbpf objects are:
struct bpf_object
struct bpf_program
struct bpf_map
xdp_prog_kern.c:
#include <linux/bpf.h>
#include "bpf_helpers.h"
/* Notice how this XDP/BPF-program contains several programs in the same source
* file. These will each get their own section in the ELF file, and via libbpf
* they can be selected individually, and via their file-descriptor attached to
* a given kernel BPF-hook.
*
* The libbpf bpf_object__find_program_by_title() refers to SEC names below.
* The iproute2 utility also use section name.
*
* Slightly confusing, the names that gets listed by "bpftool prog" are the
* C-function names (below the SEC define).
*/
SEC("xdp_pass")
int xdp_pass_func(struct xdp_md *ctx)
{
return XDP_PASS;
}
SEC("xdp_drop")
int xdp_drop_func(struct xdp_md *ctx)
{
return XDP_DROP;
}
/* Assignment#2: Add new XDP program section that use XDP_ABORTED */
char _license[] SEC("license") = "GPL";
/* Hint the avail XDP action return codes are:
enum xdp_action {
XDP_ABORTED = 0,
XDP_DROP,
XDP_PASS,
XDP_TX,
XDP_REDIRECT,
};
*/
xdp_loader.c:
static const char *default_filename = "xdp_prog_kern.o";
static const char *default_progsec = "xdp_pass";
static const struct option_wrapper long_options[] = {
{
{"help", no_argument, NULL, 'h' },
"Show help", false},
{
{"dev", required_argument, NULL, 'd' },
"Operate on device <ifname>", "<ifname>", true},
{
{"skb-mode", no_argument, NULL, 'S' },
"Install XDP program in SKB (AKA generic) mode"},
{
{"native-mode", no_argument, NULL, 'N' },
"Install XDP program in native mode"},
{
{"auto-mode", no_argument, NULL, 'A' },
"Auto-detect SKB or native mode"},
{
{"offload-mode",no_argument, NULL, 3 },
"Hardware offload XDP program to NIC"},
{
{"force", no_argument, NULL, 'F' },
"Force install, replacing existing program on interface"},
{
{"unload", no_argument, NULL, 'U' },
"Unload XDP program instead of loading"},
{
{"quiet", no_argument, NULL, 'q' },
"Quiet mode (no output)"},
{
{"filename", required_argument, NULL, 1 },
"Load program from <file>", "<file>"},
{
{"progsec", required_argument, NULL, 2 },
"Load program in <section> of the ELF file", "<section>"},
{
{0, 0, NULL, 0 }, NULL, false}
};
/* Lesson#1: More advanced load_bpf_object_file and bpf_object */
struct bpf_object *__load_bpf_object_file(const char *filename, int ifindex)
{
/* In next assignment this will be moved into ../common/ */
int first_prog_fd = -1;
struct bpf_object *obj;
int err;
/* Lesson#3: This struct allow us to set ifindex, this features is used
* for hardware offloading XDP programs.
*/
struct bpf_prog_load_attr prog_load_attr = {
.prog_type = BPF_PROG_TYPE_XDP,
.ifindex = ifindex,
};
prog_load_attr.file = filename;
/* Use libbpf for extracting BPF byte-code from BPF-ELF object, and
* loading this into the kernel via bpf-syscall
*/
err = bpf_prog_load_xattr(&prog_load_attr, &obj, &first_prog_fd);
if (err) {
fprintf(stderr, "ERR: loading BPF-OBJ file(%s) (%d): %s\n",
filename, err, strerror(-err));
return NULL;
}
/* Notice how a pointer to a libbpf bpf_object is returned */
return obj;
}
/* Lesson#2: This is a central piece of this lesson:
* - Notice how BPF-ELF obj can have several programs
* - Find by sec name via: bpf_object__find_program_by_title()
*/
struct bpf_object *__load_bpf_and_xdp_attach(struct config *cfg)
{
/* In next assignment this will be moved into ../common/ */
struct bpf_program *bpf_prog;
struct bpf_object *bpf_obj;
int offload_ifindex = 0;
int prog_fd = -1;
int err;
/* If flags indicate hardware offload, supply ifindex */
if (cfg->xdp_flags & XDP_FLAGS_HW_MODE)
offload_ifindex = cfg->ifindex;
/* Load the BPF-ELF object file and get back libbpf bpf_object */
bpf_obj = __load_bpf_object_file(cfg->filename, offload_ifindex);
if (!bpf_obj) {
fprintf(stderr, "ERR: loading file: %s\n", cfg->filename);
exit(EXIT_FAIL_BPF);
}
/* At this point: All XDP/BPF programs from the cfg->filename have been
* loaded into the kernel, and evaluated by the verifier. Only one of
* these gets attached to XDP hook, the others will get freed once this
* process exit.
*/
/* Find a matching BPF prog section name */
bpf_prog = bpf_object__find_program_by_title(bpf_obj, cfg->progsec);
if (!bpf_prog) {
fprintf(stderr, "ERR: finding progsec: %s\n", cfg->progsec);
exit(EXIT_FAIL_BPF);
}
prog_fd = bpf_program__fd(bpf_prog);
if (prog_fd <= 0) {
fprintf(stderr, "ERR: bpf_program__fd failed\n");
exit(EXIT_FAIL_BPF);
}
/* At this point: BPF-progs are (only) loaded by the kernel, and prog_fd
* is our select file-descriptor handle. Next step is attaching this FD
* to a kernel hook point, in this case XDP net_device link-level hook.
*/
err = xdp_link_attach(cfg->ifindex, cfg->xdp_flags, prog_fd);
if (err)
exit(err);
return bpf_obj;
}
static void list_avail_progs(struct bpf_object *obj)
{
struct bpf_program *pos;
printf("BPF object (%s) listing avail --progsec names\n",
bpf_object__name(obj));
bpf_object__for_each_program(pos, obj) {
if (bpf_program__is_xdp(pos))
printf(" %s\n", bpf_program__title(pos, false));
}
}
int main(int argc, char **argv)
{
struct bpf_object *bpf_obj;
struct config cfg = {
.xdp_flags = XDP_FLAGS_UPDATE_IF_NOEXIST | XDP_FLAGS_DRV_MODE,
.ifindex = -1,
.do_unload = false,
};
/* Set default BPF-ELF object file and BPF program name */
strncpy(cfg.filename, default_filename, sizeof(cfg.filename));
strncpy(cfg.progsec, default_progsec, sizeof(cfg.progsec));
/* Cmdline options can change these */
parse_cmdline_args(argc, argv, long_options, &cfg, __doc__);
/* Required option */
if (cfg.ifindex == -1) {
fprintf(stderr, "ERR: required option --dev missing\n");
usage(argv[0], __doc__, long_options, (argc == 1));
return EXIT_FAIL_OPTION;
}
if (cfg.do_unload)
return xdp_link_detach(cfg.ifindex, cfg.xdp_flags, 0);
bpf_obj = __load_bpf_and_xdp_attach(&cfg);
if (!bpf_obj)
return EXIT_FAIL_BPF;
if (verbose)
list_avail_progs(bpf_obj);
if (verbose) {
printf("Success: Loaded BPF-object(%s) and used section(%s)\n",
cfg.filename, cfg.progsec);
printf(" - XDP prog attached on device:%s(ifindex:%d)\n",
cfg.ifname, cfg.ifindex);
}
/* Other BPF section programs will get freed on exit */
return EXIT_OK;
}
In xdp_loader.c : Basic bpf_object usage In xdp_loader.c the function __load_bpf_object_file() now returns a libbpf struct bpf_object pointer (while in the basic01 lesson we just returned the file descriptor to the first BPF prog).
The struct bpf_object represents an ELF object, which can be of various types.
the function __load_bpf_and_xdp_attach() uses bpf_object__find_program_by_title() on the bpf_object, which finds a program by the “SEC” name (not the C-function name). The return type is a struct bpf_program object, and we can use the function bpf_program__fd() for getting the file descriptor that we want to attach to the XDP hook.
In this lesson we will only cover two simple maps types:
BPF_MAP_TYPE_ARRAY and BPF_MAP_TYPE_PERCPU_ARRAY.
xdp_prog_kern.c:
#include "common_kern_user.h" /* defines: struct datarec; */
/* Lesson#1: See how a map is defined.
* - Here an array with XDP_ACTION_MAX (max_)entries are created.
* - The idea is to keep stats per (enum) xdp_action
*/
struct bpf_map_def SEC("maps") xdp_stats_map = {
.type = BPF_MAP_TYPE_ARRAY,
.key_size = sizeof(__u32),
.value_size = sizeof(struct datarec),
.max_entries = XDP_ACTION_MAX,
};
/* LLVM maps __sync_fetch_and_add() as a built-in function to the BPF atomic add
* instruction (that is BPF_STX | BPF_XADD | BPF_W for word sizes)
*/
#ifndef lock_xadd
#define lock_xadd(ptr, val) ((void) __sync_fetch_and_add(ptr, val))
#endif
SEC("xdp_stats1")
int xdp_stats1_func(struct xdp_md *ctx)
{
// void *data_end = (void *)(long)ctx->data_end;
// void *data = (void *)(long)ctx->data;
struct datarec *rec;
__u32 key = XDP_PASS; /* XDP_PASS = 2 */
/* Lookup in kernel BPF-side return pointer to actual data record */
rec = bpf_map_lookup_elem(&xdp_stats_map, &key);
/* BPF kernel-side verifier will reject program if the NULL pointer
* check isn't performed here. Even-though this is a static array where
* we know key lookup XDP_PASS always will succeed.
*/
if (!rec)
return XDP_ABORTED;
/* Multiple CPUs can access data record. Thus, the accounting needs to
* use an atomic operation.
*/
lock_xadd(&rec->rx_packets, 1);
/* Assignment#1: Add byte counters
* - Hint look at struct xdp_md *ctx (copied below)
*
* Assignment#3: Avoid the atomic operation
* - Hint there is a map type named BPF_MAP_TYPE_PERCPU_ARRAY
*/
return XDP_PASS;
}
char _license[] SEC("license") = "GPL";
/* Copied from: $KERNEL/include/uapi/linux/bpf.h
*
* User return codes for XDP prog type.
* A valid XDP program must return one of these defined values. All other
* return codes are reserved for future use. Unknown return codes will
* result in packet drops and a warning via bpf_warn_invalid_xdp_action().
*
enum xdp_action {
XDP_ABORTED = 0,
XDP_DROP,
XDP_PASS,
XDP_TX,
XDP_REDIRECT,
};
* user accessible metadata for XDP packet hook
* new fields must be added to the end of this structure
*
struct xdp_md {
// (Note: type __u32 is NOT the real-type)
__u32 data;
__u32 data_end;
__u32 data_meta;
// Below access go through struct xdp_rxq_info
__u32 ingress_ifindex; // rxq->dev->ifindex
__u32 rx_queue_index; // rxq->queue_index
};
*/
在这个教程中,bpf_map_dev定义一个SEC(“map”).
struct bpf_map_def SEC("maps") xdp_stats_map = {
.type = BPF_MAP_TYPE_ARRAY,
.key_size = sizeof(__u32),
.value_size = sizeof(struct datarec),
.max_entries = XDP_ACTION_MAX,
};
有关map的核心就在这里了
这里以教科书版的视角去解读。
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 应用层(Application) | http Telent Ftp SMTP SMTP NFS
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
^
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 表示层(Presentation layer)| JPEG ASCII GIF DES MPEG
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
^
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 会话层(session) | SQL RPC NFS
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
^
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 传输层(transport) | TCP UDP SPX
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
^
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 网络层(network) | IP IPX
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
^
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 数据链路层(link) | MAC (媒介访问控制) LLC(逻辑链路控制)
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ PPP ATM IEEE802.3/2
^
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 物理层(physical) | RS-232 RJ-45 FDDI
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
物理层:提供物理通路,二进制数据传输、 定义机械 电气特性和接口 比特流
链路层: 数据链路的释放、流量控制、 构成链路数据单元、差错的检测与恢复、 帧界定与同步、传送以帧(frame)为单位的信息 数据帧
网络层:路由的选择与中继 网络链接的激活与终止 网络链接的多路复用、差错的检测与恢复 、排序与流量的控制、服务选择 数据包
传输层:映射传输地址到网络地址、传输链接的建立与释放、多路复用与分割、差错控制与恢复、分段与重组、组片与分片、序号及流量控制 信息报文
会话层:: 会话链接到传输链接、会话链接的恢复与释放、会话参数的协商、服务选择、活动管理与令牌管理、数据传输 同上
表示层:数据语法的转换、数据加密与数据压缩、语法表示与链接管理 同上
应用层: 用户应用程序执行任务所需的协议与功能 同上
原因还是非常的简单,对指针的理解不透彻。 是这样的,在一个字符串中,有大小写的字符,请将其中的大写字母转化为小写字母,让你们见笑了。
char* toLowerCase(char* str) {
int len = strlen(str);
int i;
/* 第一种情况: */
for(i = 0; i < len; i++){
if(str[i] >= 'A' && str[i] <= 'Z')
str[i] = str[i] + 32;
}
return str;
使用指针,这里有个问题:
char *p = str;
while(*p){
if(*p >= 'A' && *p <= 'Z')
*p = *p + 32;
p++;
}
p = p - len;
return p;
你如果想要返回字符指针也就是p,必须将指针调整回原头步,即减去移动的长度,也可以直接返回str.
char* toLowerCase(char* str) {
int len = strlen(str);
while(*str){
if(*str >= 'A' && *str <= 'Z')
*str += 32;
str++;
}
str = str - len;
return str;
}
其中,way three比way two 快太多了
目前,leetcode的解题思路都是运用使用python、java、c++等语言,为啥呢? 因为简单啊,一些现用的库函数拿起来就是用很爽的,但是,我的思路先用pure c 啃完大部分题目,即便是自己造轮子,这个时候就是学习积累,工作中当然使用现成的 标准库了。
也许leetcode从测试的角度来考虑在线提交的代码,对于c来说有点变态,也许是自己没有 考虑全面。但是,强如内核都没见这样的使用案例,下面是错误汇总。
下面是引自leetcode的官方手册:
/*
The input [1,null,2,3] represents the serialized format of a binary tree using level order traversal, where null signifies a path terminator where no node exists below.
We provide a Tree Visualizer tool to help you visualize the binary tree. By opening the console panel, you should see a Tree Visualizer toggle switch under the TestCase tab. Click on it and it will show the test case's binary tree representation.
5
/ \
1 4
/ \
3 6
代表:[5,1,4,null,null,3,6]
也就是树的遍历为层次遍历,如果节点为空,则使用null.而且,还注意:
[]
Empty tree.
The root is a reference to NULL (C/C++), null (Java/C#/Javascript), None (Python), or nil (Ruby).
这个错误太突然了,貌似是在某个时间点才引入这个检查。bug 重现:
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
};
作为一个定义很常见的代码。 但是,以convert-sorted-array-to-binary-search-tree为例:
struct TreeNode *sortedArrayToBST(int *nums, int numsSize)
{
struct TreeNode *root = (struct TreeNode *)malloc(sizeof(struct TreeNode));
// root->left = NULL;
// root->right = NULL;
// root->val = NULL;
int head = 0;
int tail;
if (numsSize >= 1) {
tail = numsSize - 1;
toBST(root, nums, head, tail);
}
else
return root = NULL;
printf("trace\n");
//show(root);
return root;
}
也就是说,上面的代码在提交的时候,就会报上面的错误。当你申请一个带指针成员的结构体时,一定要在 使用前初始化他,貌似这个规定内核中也不常见啊。 改正的方法就是注释掉的语句,将里面的指针成员初始化为NULL;
如果像上面的代码中,怎么处理一个空的树,如果直接
return root;
那么结果很有可能不正确,在我的代码中,提交后会明显看到(输入[]),输出[-122541545465] 明显是个垃圾值,正确的处理方式是
return root = NULL;
我一开始还把输入和输出的结果考虑全面了,其实是不用的,比如,结果要求
[“1->2->3”, “1->5”] 实际你不用管开始和结尾的”[“或者’]’符号,只需把字符串存入一个地方,并能读取出来就行。
这也是我不经意间发现的,就是你的输出结果不必和给出的案例一致,我估计这是为什么使用”[]”的原因。
之前一直在寻找快速开发linux kernel的方法,有兴趣的同学们可以参考kernel系列文章。众所周知,kernel的开发不同于用户空间的程序,一个是本身系统的复杂性,众多子系统需要一名合格的内核开发者去掌握;二是编译内核程序的复杂性,除了编写module程序。但是我相信大多数同学经历过自己编译内核的经历,以满足自身的需要。以前我在想,怎么才能让编译内核快一些,无论是时间还是空间,其中,基于libvirt的qemu是最符合我这个要求的,请参看本文。这个方法的思路是安装一个无图形界面的debian系统,然后使用make deb-pkg,生成*.deb包,这样就可以测试你自己的kernel程序,这种方法可以,但是重新编译了整个内核,这还不是最痛苦的,最痛苦的地方在于它会生成5个包,其中包含一个gdb调戏信息的内核镜像,眼见的消耗时间是单个vmlinux*4的数量可以,但是笨重。
还有这篇here
其中,这个链接下面还会再次提到,因为这个涉及到了qemu,本篇会继续使用这个工具。
直到今日,本人终于找到了一个理论上最完美的一个方案,之所以这么说,是因为:一,它就是我梦中最理想的方案,几乎与我想象的一模一样,当然,我也只能想象了,行动并没有实施;二,基本再没有提高的空间,对,就是这样,没有了。
我把我的构思给大家提前分享下:
你只需重新编译很少的选项,嗯,如果只是测试某些程序,交互就用ssh就可以了,并不需要一些硬件资源,比如,键盘、屏幕、鼠标、无线…也就是最小量的编译选项,仅仅这一点,就大大节省了时间和空间。
用户空间的程序可以重用,也就是安装内核后可以接着使用原来的工具,按道理说这个要求绝大多数的编译内核都会满足。但是,我希望不借助重新启动进入新的内核去使用这些,这又节省了时间。
好了,仅凭上面的两点,我相信此方案绝对就可以独领风骚了。
这个是什么请自己google,这篇文章的第一个链接就是这个项目的介绍。
这里涉及到了kbuild的一些细节,其实也可以不使用这个,但是,使用没有坏处。
前期编译好qemu,一定对应自己的平台,可以自己私自修改。
export KBUILD_OUTPUT=$HOME/src/kernel-build
这个(可能需要提前创建需要的目录),这个目录下就把源代码进行了一份拷贝并生成了可执行文件,最后组成了一个镜像文件,简而言之就是和主目录的内容一样,只不过在另外一个目录下也有同样的内容。
minimal-configuration就可以生成最小量的.config.sudo make modules_install install {INSTALL_MOD_PATH,INSTALL_PATH}=$HOME/src/kernel-dev
也就是说,此时你在$HONE/src下:
yubo@debian:~$ ls src/
kernel-build kernel-dev
其中,kernel-dev下是
yubo@debian:~$ ls src/kernel-dev/
config-5.0.0-rc7+ lib System.map-5.0.0-rc7+ vmlinuz-5.0.0-rc7+
其实,这也是一份来自/boot的拷贝。至此,前期的准备工作已经完成的差不多了,其中,我可能完全不会KBUILD的使用方案,请朋友们指正。
eudyptula-boot脚本去启动自己编译好的内核:yubo@debian:~/git/configure_file/kernel-fast-dev$ ./eudyptula-boot --kernel ~/src/kernel-dev/vmlinuz-5.0.0-rc7+
[✔] All dependencies are met.
[✔] Found kernel 5.0.0-rc7+.
[✔] Kernel configuration checked. overlayfs present.
[✔] Modules are in /home/yubo/src/kernel-dev/lib/modules/5.0.0-rc7+.
[∗] TMP is /tmp/tmp.0NkeE3lzn9.
[✔] initrd built in /tmp/tmp.0NkeE3lzn9/initrd.gz.
[∗] Start VM eudyptula-5-0-0-rc7+.
[∗] GDB server listening on /tmp/tmp.0NkeE3lzn9/vm-eudyptula-5-0-0-rc7+-gdb.pipe.
[∗] monitor listening on /tmp/tmp.0NkeE3lzn9/vm-eudyptula-5-0-0-rc7+-console.pipe.
[∗] ttyS1 listening on /tmp/tmp.0NkeE3lzn9/vm-eudyptula-5-0-0-rc7+-serial.pipe.
[ 0.810771] mce: Unable to init MCE device (rc: -5)
[∗] initrd started.
[∗] Loading modules.
[✔] Configuration loaded.
[✔] Root file system setup.
[✔] /tmp, /run and others are clean.
[∗] Change root.
[✔] /proc and /sys setup.
[✔] /root mounted.
[✔] /lib/modules mounted.
[…] Starting udev... starting version 232
[✔] udev started.
[✔] Network configured.
[∗] Setup terminal.
[✔] Terminal size is 80×23.
[∗] QEMU PID is 28311.
[∗] Spawning a shell.
其中,请注意
[∗] GDB server listening on /tmp/tmp.0NkeE3lzn9/vm-eudyptula-5-0-0-rc7+-gdb.pipe.
这个语句可以用来调试内核的。 看到没有,已经启动了一个VM,可以看一下内核版本:
root@eudyptula-5-0-0-rc7+:~/git/configure_file/kernel-fast-dev# uname -r
5.0.0-rc7+
大家发现了没有,我们在主操作系统中开了一个镜像进程,而且这个内核居然可以使用原来用户空间的程序。
经过上面的准备,我们要对修改的代码进行验证才可以。 教材 这个文章就是根据这篇改编过来。
这个部分是我后来补写的,那么,gdb server的信息为
[∗] GDB server listening on /tmp/tmp.Aec21mkPjO/vm-eudyptula-5-0-0-next-20190306-00003-g801cbc0c69c2-dirty-gdb.pipe.
vmlinux可以是你内核代码源目录下的那个,也可以是~/src/kernel-dev/lib/build/vmlinux文件。gdb vmlinux
GNU gdb (Debian 7.12-6) 7.12.0.20161007-git
Copyright (C) 2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from vmlinux...done.
warning: File "/home/yubo/git/linux/scripts/gdb/vmlinux-gdb.py" auto-loading has been declined by your `auto-load safe-path' set to "$debugdir:$datadir/auto-load".
To enable execution of this file add
add-auto-load-safe-path /home/yubo/git/linux/scripts/gdb/vmlinux-gdb.py
line to your configuration file "/home/yubo/.gdbinit".
To completely disable this security protection add
set auto-load safe-path /
line to your configuration file "/home/yubo/.gdbinit".
For more information about this security protection see the
---Type <return> to continue, or q <return> to quit---
"Auto-loading safe path" section in the GDB manual. E.g., run from the shell:
info "(gdb)Auto-loading safe path"
(gdb)
(gdb) target remote | socat UNIX:/tmp/tmp.6pDwCfUhbz/vm-eudyptula-5-0-0-next-20190306-00003-g801cbc0c69c2-dirty-gdb.pipe -
Remote debugging using | socat UNIX:/tmp/tmp.6pDwCfUhbz/vm-eudyptula-5-0-0-next-20190306-00003-g801cbc0c69c2-dirty-gdb.pipe -
__read_once_size (size=<optimized out>, res=<optimized out>, p=<optimized out>)
at ./include/linux/compiler.h:197
197 __READ_ONCE_SIZE;
(gdb)
注意,我的监听地址变化了好几次,请注意。注意上面的地址。 有什么问题大家可以问我,谢谢大家
在github上产生pull-request请求后,还需要你能够及时的sync原来的仓库,要不然 你克隆的仓库就失去了价值,无法继续进行相应的开发,下面讲讲我的心得.
对,当你fork一个仓库后,就会在你的github主页上产生一个你fork的备份。以这个为例,单击一下fork后,回到你的主页就会显示相应的仓库,这也是你产生pull request的第一步。
#1 fork https://github.com/netoptimizer/prototype-kernel on the origin page
git clone https://github.com/yuzibo/prototype-kernel.git
#2 这儿就是你克隆自己fork的仓库
所谓的上游仓库就是你刚才fork的原来的仓库,添加一个upstream的标签,这个就是为了同步用的。
git remote add upstream https://github.com/netoptimizer/prototype-kernel.git
接着使用命令:
yubo-2@debian:~/git/yubo-prototype$ git remote -v
origin https://github.com/yuzibo/prototype-kernel.git (fetch)
origin https://github.com/yuzibo/prototype-kernel.git (push)
upstream https://github.com/netoptimizer/prototype-kernel.git (fetch)
upstream https://github.com/netoptimizer/prototype-kernel.git (push)
更新上游的最新修改,保存在本地的upstream/master分支。
yubo-2@debian:~/git/yubo-prototype$ git fetch upstream
remote: Enumerating objects: 1, done.
remote: Counting objects: 100% (1/1), done.
remote: Total 1 (delta 0), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (1/1), done.
From https://github.com/netoptimizer/prototype-kernel
* [new branch] master -> upstream/master
git checkout master
从你刚才的upstream/master分支合并最新最全的内容到master分支,并且不会修改你在master分支所做的目录。
yubo-2@debian:~/git/yubo-prototype$ git merge upstream/master
Updating ae6f3eb..a94339c
Fast-forward
kernel/Documentation/bpf/ebpf_maps_types.rst | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
你这个时候就可以把上游的变化推进到github账户去了,接下来就可以产生相应的pull request了。
git push origin master
这里需要注意就是你要推送到哪个分支,这里就是主分支,与产生pull request的分支是不一样的,请注意一下.