文章目录

  • ebtables
  • ebtables-standalone.c
  • ebt_options
  • ebt_matches
  • 数据结构
  • 总结

ebtables

ebtables就是以太网桥防火墙,以太网桥工作在数据链路层,ebtables主要用来过滤数据链路层数据包。
linux用户空间ebtables tool用于配置各种netfilter功能,配置到内核,由内核来实现各种过滤规则。
本文主要介绍ebtables tool框架。
源代码:https://ebtables.netfilter.org/downloads/latest.html
ebtables包含几种编译配置,包括c/s架构,动态命令行输入,单独命令行输入等。
ebtablesd.c和ebtablesu.c 为一组,ebtablesd.c编译作为守护程序,ebtablesu.c 编译作物用户接口程序,两者之间通过管道方式通信。
ebtables-restore.c 编译为动态命令行输入程序,等待用户输入ebtables配置命令
ebtables-standalone.c编译为单独命令行程序,用户运行程序时必须带ebtables配置参数。

extensions\目录下各个文件实现不同的过滤配置,注册到ebtables 程序管理的链表中。各个文件编译为动态库。

以ebtables-standalone.c为例分析。

ebtables-standalone.c

ebtables -t broute -A BROUTING -p ipv4 -i eth0 --ip-dst 172.16.1.1 -j DROP
在BROUTING 点broute 添加过滤规则

static struct ebt_u_replace replace;
void ebt_early_init_once();int main(int argc, char *argv[])
{ebt_silent = 0;ebt_early_init_once(); 初始化各个过滤配置,extensions\目录下各个配置strcpy(replace.name, "filter");//默认设置为filter表do_command(argc, argv, EXEC_STYLE_PRG, &replace);return 0;
}
do_command(int argc, char *argv[], int exec_style,struct ebt_u_replace *replace_)
{#def 需要先从kernel获取支持的过滤配置replace->flags &= OPT_KERNELDATA; /* ebtablesd needs OPT_KERNELDATA */replace->selected_chain = -1;replace->command = 'h';#def 申请new_entry 用于存储配置参数if (!new_entry) {new_entry = (struct ebt_u_entry *)malloc(sizeof(struct ebt_u_entry));if (!new_entry)ebt_print_memory();}/* Put some sane values in our new entry */ebt_initialize_entry(new_entry);new_entry->replace = replace;#def 解析配置参数,存储到new_entry中,包括-A:D:C:I:N:E:X::L::Z::F::P:Vhi:o:j:c:p:s:d:t:M: 和ebt_options,ebt_options后续讲解构成
while ((c = getopt_long(argc, argv,"-A:D:C:I:N:E:X::L::Z::F::P:Vhi:o:j:c:p:s:d:t:M:", ebt_options, NULL)) != -1) {switch (c) {case “A”replace->command = c;replace->flags |= OPT_COMMAND;#def 先从kernel获取支持的过滤配置,包括支持的链,表,规则等if (!(replace->flags & OPT_KERNELDATA))ebt_get_kernel_table(replace, 0);......else if (c == 'j') {ebt_check_option2(&(replace->flags), OPT_JUMP);for (i = 0; i < NUM_STANDARD_TARGETS; i++)if (!strcmp(optarg, ebt_standard_targets[i])) {t = ebt_find_target(EBT_STANDARD_TARGET);((struct ebt_standard_target *) t->t)->verdict = -i - 1;break;}if ((i = ebt_get_chainnr(replace, optarg)) != -1) {if (i < NF_BR_NUMHOOKS)ebt_print_error2("Don't jump to a standard chain");t = ebt_find_target(EBT_STANDARD_TARGET);((struct ebt_standard_target *) t->t)->verdict = i - NF_BR_NUMHOOKS;break;} else {/* Must be an extension then */struct ebt_u_target *t;t = ebt_find_target(optarg);/* -j standard not allowed either */if (!t || t == (struct ebt_u_target *)new_entry->t)ebt_print_error2("Illegal target name '%s'", optarg);#def -j 对应target 执行的动作 new_entry->t = (struct ebt_entry_target *)t;ebt_find_target(EBT_STANDARD_TARGET)->used = 0;t->used = 1;}break;} else if (c == 's') {..........case ‘p’:ebt_check_option2(&(replace->flags), OPT_PROTOCOL);if (ebt_check_inverse2(optarg))new_entry->invflags |= EBT_IPROTO;new_entry->bitmask &= ~((unsigned int)EBT_NOPROTO);#def 对于 -p ipv4   strtol计算后,i为0,buffer为ipv4i = strtol(optarg, &buffer, 16);if (*buffer == '\0' && (i < 0 || i > 0xFFFF))ebt_print_error2("Problem with the specified protocol");if (*buffer != '\0') {struct ethertypeent *ent;if (!strcasecmp(optarg, "LENGTH")) {new_entry->bitmask |= EBT_802_3;break;}#def 这里读取系统中/etc/ethertypes,查找支持的协议类型,比如:IPv4        0800    ip ip4      # Internet IP (IPv4)ent = getethertypebyname(optarg);if (!ent)ebt_print_error2("Problem with the specified Ethernet protocol '%s', perhaps "_PATH_ETHERTYPES " is missing", optarg);#def 这里ipv4为0800     new_entry->ethproto = ent->e_ethertype;} elsenew_entry->ethproto = i;#def 不在A:D:C:I:N:E:X::L::Z::F::P:Vhi:o:j:c:p:s:d:t:M中时,走defaultdefault:/* Is it a target option? */t = (struct ebt_u_target *)new_entry->t;if ((t->parse(c - t->option_offset, argv, argc, new_entry, &t->flags, &t->t))) {}/* Is it a match_option? */for (m = ebt_matches; m; m = m->next)#def 比如--ip-dst 172.16.1.1 在这里匹配解析if (m->parse(c - m->option_offset, argv, argc, new_entry, &m->flags, &m->m))break;if (m != NULL) {if (m->used == 0) {#def 添加匹配matchebt_add_match(new_entry, m);m->used = 1;}goto check_extension;}/* Is it a watcher option? */for (w = ebt_watchers; w; w = w->next)if (w->parse(c - w->option_offset, argv, argc, new_entry, &w->flags, &w->w))break;if (ebt_errormsg[0] != '\0')return -1;if (w->used == 0) {#def 添加匹配watcherebt_add_watcher(new_entry, w);w->used = 1;}}......} else if (replace->command == 'A' || replace->command == 'I') {#def 把规则添加到链中ebt_add_rule(replace, new_entry, rule_nr);.........#def 把配置传递到kernelebt_deliver_table(replace);
}/* The user will use the match, so put it in new_entry. The ebt_u_match* pointer is put in the ebt_entry_match pointer. ebt_add_rule will* fill in the final value for new->m. Unless the rule is added to a chain,* the pointer will keep pointing to the ebt_u_match (until the new_entry* is freed). I know, I should use a union for these 2 pointer types... */
void ebt_add_match(struct ebt_u_entry *new_entry, struct ebt_u_match *m)
{struct ebt_u_match_list **m_list, *new;for (m_list = &new_entry->m_list; *m_list; m_list = &(*m_list)->next);new = (struct ebt_u_match_list *)malloc(sizeof(struct ebt_u_match_list));if (!new)ebt_print_memory();*m_list = new;new->next = NULL;#def 这里把ebt_u_match 强制转换为了ebt_entry_match ,包括name,kernel中通过name找对对应匹配选项new->m = (struct ebt_entry_match *)m;
}#def 比如--ip-dst 172.16.1.1, ebt_u_match 把name和size      对应转换到了ebt_entry_match
static struct ebt_u_match ip_match =
{.name      = "ip",.size     = sizeof(struct ebt_ip_info),.help     = print_help,.init     = init,.parse      = parse,.final_check   = final_check,.print       = print,.compare   = compare,.extra_ops   = opts,
};struct ebt_entry_match
{union {char name[EBT_FUNCTION_MAXNAMELEN];struct ebt_match *match;} u;/* size of data */unsigned int match_size;
#ifdef KERNEL_64_USERSPACE_32unsigned int pad;
#endifunsigned char data[0] __attribute__ ((aligned (__alignof__(struct ebt_replace))));
};
void ebt_deliver_table(struct ebt_u_replace *u_repl)
{socklen_t optlen;struct ebt_replace *repl;/* Translate the struct ebt_u_replace to a struct ebt_replace */#def 配置数据,由用户空间格式转换为kernel使用格式 repl = translate_user2kernel(u_repl);if (u_repl->filename != NULL) {store_table_in_file(u_repl->filename, repl);goto free_repl;}/* Give the data to the kernel */optlen = sizeof(struct ebt_replace) + repl->entries_size;if (get_sockfd())goto free_repl;#def 配置到kernelif (!setsockopt(sockfd, IPPROTO_IP, EBT_SO_SET_ENTRIES, repl, optlen))goto free_repl;if (u_repl->command == 8) { /* The ebtables module may not* yet be loaded with --atomic-commit */ebtables_insmod("ebtables");if (!setsockopt(sockfd, IPPROTO_IP, EBT_SO_SET_ENTRIES,repl, optlen))goto free_repl;}}

ebt_options

前面main函数调用了如下函数,进行了early init

void ebt_early_init_once()
{ebt_iterate_matches(merge_match);ebt_iterate_watchers(merge_watcher);ebt_iterate_targets(merge_target);
}void ebt_iterate_matches(void (*f)(struct ebt_u_match *))
{struct ebt_u_match *i;#def 遍历ebt_matches中选项分别调用merge_match,ebt_matches链表中选项是来自extensions\目录下各个过滤文件注册for (i = ebt_matches; i; i = i->next)f(i);
}static void merge_match(struct ebt_u_match *m)
{#def ebt_options 为全局变量,作为入参,返回值也复制给ebt_options ebt_options = merge_options(ebt_options, m->extra_ops, &(m->option_offset));
}#def 函数就是把newopts选项合并到oldopts
static struct option *merge_options(struct option *oldopts,const struct option *newopts, unsigned int *options_offset)
{unsigned int num_old, num_new, i;struct option *merge;if (!newopts || !oldopts || !options_offset)ebt_print_bug("merge wrong");for (num_old = 0; oldopts[num_old].name; num_old++);for (num_new = 0; newopts[num_new].name; num_new++);global_option_offset += OPTION_OFFSET;*options_offset = global_option_offset;#def 为newopts申请空间merge = malloc(sizeof(struct option) * (num_new + num_old + 1));if (!merge)ebt_print_memory();#def 下面进行合并memcpy(merge, oldopts, num_old * sizeof(struct option));for (i = 0; i < num_new; i++) {merge[num_old + i] = newopts[i];merge[num_old + i].val += *options_offset;}memset(merge + num_old + num_new, 0, sizeof(struct option));/* Only free dynamically allocated stuff */if (oldopts != ebt_original_options)free(oldopts);return merge;
}

ebt_options 初始化为公共基础的ebt_original_options配置Default 选项

static struct option *ebt_options = ebt_original_options;

/* Default command line options. Do not mess around with the already* assigned numbers unless you know what you are doing */
static struct option ebt_original_options[] =
{{ "append"         , required_argument, 0, 'A' },{ "insert"         , required_argument, 0, 'I' },{ "delete"         , required_argument, 0, 'D' },{ "list"           , optional_argument, 0, 'L' },{ "Lc"             , no_argument      , 0, 4   },{ "Ln"             , no_argument      , 0, 5   },{ "Lx"             , no_argument      , 0, 6   },{ "Lmac2"          , no_argument      , 0, 12  },{ "zero"           , optional_argument, 0, 'Z' },{ "flush"          , optional_argument, 0, 'F' },{ "policy"         , required_argument, 0, 'P' },{ "in-interface"   , required_argument, 0, 'i' },{ "in-if"          , required_argument, 0, 'i' },{ "logical-in"     , required_argument, 0, 2   },{ "logical-out"    , required_argument, 0, 3   },{ "out-interface"  , required_argument, 0, 'o' },{ "out-if"         , required_argument, 0, 'o' },{ "version"        , no_argument      , 0, 'V' },{ "help"           , no_argument      , 0, 'h' },{ "jump"           , required_argument, 0, 'j' },{ "set-counters"   , required_argument, 0, 'c' },{ "change-counters", required_argument, 0, 'C' },{ "proto"          , required_argument, 0, 'p' },{ "protocol"       , required_argument, 0, 'p' },{ "db"             , required_argument, 0, 'b' },{ "source"         , required_argument, 0, 's' },{ "src"            , required_argument, 0, 's' },{ "destination"    , required_argument, 0, 'd' },{ "dst"            , required_argument, 0, 'd' },{ "table"          , required_argument, 0, 't' },{ "modprobe"       , required_argument, 0, 'M' },{ "new-chain"      , required_argument, 0, 'N' },{ "rename-chain"   , required_argument, 0, 'E' },{ "delete-chain"   , optional_argument, 0, 'X' },{ "atomic-init"    , no_argument      , 0, 7   },{ "atomic-commit"  , no_argument      , 0, 8   },{ "atomic-file"    , required_argument, 0, 9   },{ "atomic-save"    , no_argument      , 0, 10  },{ "init-table"     , no_argument      , 0, 11  },{ "concurrent"     , no_argument      , 0, 13  },{ 0 }
};

ebt_matches

三个全局变量存储动态过滤配置,extensions\目录下各个文件实现不同的过滤配置,注册到ebtables 程序管理的链表中,然后在上面的ebt_early_init_once中添加到ebt_options 中。

struct ebt_u_match *ebt_matches;
struct ebt_u_watcher *ebt_watchers;
struct ebt_u_target *ebt_targets;
例如 ebt_ip.c

#define IP_SOURCE ‘1’
#define IP_DEST ‘2’
#define IP_myTOS ‘3’ /* include/bits/in.h seems to already define IP_TOS */
#define IP_PROTO ‘4’
#define IP_SPORT ‘5’
#define IP_DPORT ‘6’


static struct ebt_u_match ip_match =
{.name      = "ip",.size     = sizeof(struct ebt_ip_info),.help     = print_help,.init     = init,.parse      = parse,.final_check   = final_check,.print       = print,.compare   = compare,.extra_ops   = opts, #def 用于解析opt
};#def 这个函数非常特殊_init 在动态链接加载时,在main函数前先调用
void _init(void)
{#def 注册到ebt_matches链表,前面ebt_early_init_once中使用ebt_register_match(&ip_match);
}/* Used in initialization code of modules */
void ebt_register_match(struct ebt_u_match *m)
{int size = EBT_ALIGN(m->size) + sizeof(struct ebt_entry_match); //大小为ebt_entry_matchstruct ebt_u_match **i;m->m = (struct ebt_entry_match *)malloc(size);if (!m->m)ebt_print_memory();strcpy(m->m->u.name, m->name);m->m->match_size = EBT_ALIGN(m->size);m->init(m->m);for (i = &ebt_matches; *i; i = &((*i)->next));m->next = NULL;*i = m;
}static void init(struct ebt_entry_match *match)
{struct ebt_ip_info *ipinfo = (struct ebt_ip_info *)match->data;// data指向了ebt_ip_info ipinfo->invflags = 0;ipinfo->bitmask = 0;
}static struct option opts[] =
{{ "ip-source"           , required_argument, 0, IP_SOURCE },{ "ip-src"              , required_argument, 0, IP_SOURCE },{ "ip-destination"      , required_argument, 0, IP_DEST   },{ "ip-dst"              , required_argument, 0, IP_DEST   },{ "ip-tos"              , required_argument, 0, IP_myTOS  },{ "ip-protocol"         , required_argument, 0, IP_PROTO  },{ "ip-proto"            , required_argument, 0, IP_PROTO  },{ "ip-source-port"      , required_argument, 0, IP_SPORT  },{ "ip-sport"            , required_argument, 0, IP_SPORT  },{ "ip-destination-port" , required_argument, 0, IP_DPORT  },{ "ip-dport"            , required_argument, 0, IP_DPORT  },{ 0 }
};

数据结构

ebt_u_replace为用户空间使用的结构,解析用户输入命令过滤配置都存储在ebt_u_replace中,之后转换为ebt_replace结构,ebt_replace为配置到内核时使用的结构。
ebt_u_replace

struct ebt_u_replace
{char name[EBT_TABLE_MAXNAMELEN]; //过滤配置表名字,broute,nat,filterunsigned int valid_hooks;/* nr of rules in the table */unsigned int nentries;unsigned int num_chains; //支持的chain hooks,就是NF_BR_BROUTING,NF_BR_POST_ROUTING,NF_BR_LOCAL_OUT,NF_BR_FORWARD,NF_BR_PRE_ROUTINGNF_BR_LOCAL_INunsigned int max_chains;struct ebt_u_entries **chains; //指向链上的规则/* nr of counters userspace expects back */unsigned int num_counters;/* where the kernel will put the old counters */struct ebt_counter *counters;/** can be used e.g. to know if a standard option* has been specified twice*/unsigned int flags;/* we stick the specified command (e.g. -A) in here */char command;/** here we stick the chain to do our thing on (can be -1 if unspecified)*/int selected_chain; //当前命令要处理的链/* used for the atomic option */char *filename;/* tells what happened to the old rules (counter changes) */struct ebt_cntchanges *cc;
};
struct ebt_u_entries
{int policy; //默认规则配置unsigned int nentries; //规则个数/* counter offset for this chain */unsigned int counter_offset;/* used for udc */unsigned int hook_mask;char *kernel_start;char name[EBT_CHAIN_MAXNAMELEN]; struct ebt_u_entry *entries; //规则链表
};
struct ebt_u_table
{char name[EBT_TABLE_MAXNAMELEN];void (*check)(struct ebt_u_replace *repl);void (*help)(const char **);struct ebt_u_table *next;
};#def 描述规则
struct ebt_u_entry
{   //规则参数unsigned int bitmask;unsigned int invflags;uint16_t ethproto;char in[IFNAMSIZ];char logical_in[IFNAMSIZ];char out[IFNAMSIZ];char logical_out[IFNAMSIZ];unsigned char sourcemac[ETH_ALEN];unsigned char sourcemsk[ETH_ALEN];unsigned char destmac[ETH_ALEN];unsigned char destmsk[ETH_ALEN];struct ebt_u_match_list *m_list; //规则匹配struct ebt_u_watcher_list *w_list;struct ebt_entry_target *t;  //规则执行动作struct ebt_u_entry *prev; //执向前一个规则描述struct ebt_u_entry *next;struct ebt_counter cnt;struct ebt_counter cnt_surplus; /* for increasing/decreasing a counter and for option 'C' */struct ebt_cntchanges *cc;/* the standard target needs this to know the name of a udc when* printing out rules. */struct ebt_u_replace *replace;
};
struct ebt_u_match_list
{struct ebt_u_match_list *next;struct ebt_entry_match *m;
};struct ebt_u_watcher_list
{struct ebt_u_watcher_list *next;struct ebt_entry_watcher *w;
};struct ebt_u_match
{char name[EBT_FUNCTION_MAXNAMELEN];/* size of the real match data */unsigned int size;void (*help)(void);void (*init)(struct ebt_entry_match *m);int (*parse)(int c, char **argv, int argc,const struct ebt_u_entry *entry, unsigned int *flags,struct ebt_entry_match **match);void (*final_check)(const struct ebt_u_entry *entry,const struct ebt_entry_match *match,const char *name, unsigned int hookmask, unsigned int time);void (*print)(const struct ebt_u_entry *entry,const struct ebt_entry_match *match);int (*compare)(const struct ebt_entry_match *m1,const struct ebt_entry_match *m2);const struct option *extra_ops;/** can be used e.g. to check for multiple occurance of the same option*/unsigned int flags;unsigned int option_offset;struct ebt_entry_match *m;/** if used == 1 we no longer have to add it to* the match chain of the new entry* be sure to put it back on 0 when finished*/unsigned int used;struct ebt_u_match *next;
};struct ebt_u_watcher
{char name[EBT_FUNCTION_MAXNAMELEN];unsigned int size;void (*help)(void);void (*init)(struct ebt_entry_watcher *w);int (*parse)(int c, char **argv, int argc,const struct ebt_u_entry *entry, unsigned int *flags,struct ebt_entry_watcher **watcher);void (*final_check)(const struct ebt_u_entry *entry,const struct ebt_entry_watcher *watch, const char *name,unsigned int hookmask, unsigned int time);void (*print)(const struct ebt_u_entry *entry,const struct ebt_entry_watcher *watcher);int (*compare)(const struct ebt_entry_watcher *w1,const struct ebt_entry_watcher *w2);const struct option *extra_ops;unsigned int flags;unsigned int option_offset;struct ebt_entry_watcher *w;unsigned int used;struct ebt_u_watcher *next;
};struct ebt_u_target
{char name[EBT_FUNCTION_MAXNAMELEN];unsigned int size;void (*help)(void);void (*init)(struct ebt_entry_target *t);int (*parse)(int c, char **argv, int argc,const struct ebt_u_entry *entry, unsigned int *flags,struct ebt_entry_target **target);void (*final_check)(const struct ebt_u_entry *entry,const struct ebt_entry_target *target, const char *name,unsigned int hookmask, unsigned int time);void (*print)(const struct ebt_u_entry *entry,const struct ebt_entry_target *target);int (*compare)(const struct ebt_entry_target *t1,const struct ebt_entry_target *t2);const struct option *extra_ops;unsigned int option_offset;unsigned int flags;struct ebt_entry_target *t;unsigned int used;struct ebt_u_target *next;
}

ebt_replace
ebt_replace为配置到内核时使用的结构

struct ebt_counter
{uint64_t pcnt;uint64_t bcnt;
};struct ebt_replace
{char name[EBT_TABLE_MAXNAMELEN];unsigned int valid_hooks;/* nr of rules in the table */unsigned int nentries;/* total size of the entries */unsigned int entries_size;/* start of the chains */
#ifdef KERNEL_64_USERSPACE_32uint64_t hook_entry[NF_BR_NUMHOOKS];
#elsestruct ebt_entries *hook_entry[NF_BR_NUMHOOKS]; //指向每个链上规则首指针
#endif/* nr of counters userspace expects back */unsigned int num_counters;/* where the kernel will put the old counters */
#ifdef KERNEL_64_USERSPACE_32uint64_t counters;uint64_t entries;
#elsestruct ebt_counter *counters;char *entries;//每个链上规则,可以多个规则,上面hook_entry[]指向每个链上第一个规则
#endif
};struct ebt_entries {/* this field is always set to zero* See EBT_ENTRY_OR_ENTRIES.* Must be same size as ebt_entry.bitmask */unsigned int distinguisher;/* the chain name */char name[EBT_CHAIN_MAXNAMELEN];/* counter offset for this chain */unsigned int counter_offset;/* one standard (accept, drop, return) per hook */int policy;/* nr. of entries */unsigned int nentries;/* entry list */char data[0] __attribute__ ((aligned (__alignof__(struct ebt_replace)))); //0数组,正好指向下一个ebt_entries
};

总结

1,ebt_options = ebt_original_options;为默认配置选项
2,程序运行时动态加载可选性ebt_early_init_once,来自extensions\目录下各个文件实现不同的过滤配置
3,do_command 解析用户输入参数,配置到内核

linux ebtables tool相关推荐

  1. linux upgrade tool使用_p4merge的使用

    P4merge是业界用于git merge和compare的图形化工具,本文简单介绍如何在windowns和linux下面配置和使用.1. P4merge的下载https://www.perforce ...

  2. linux mii tool源码,linux 下mii-tool ethtool 命令简单的使用

    ################################################################ 通过命令查看linux网卡的物理信息,状态: [root@node2 ...

  3. linux wifi tool,fluxion linux tool for wifi

    压缩包 : b3cb83d4ff391849ff255fb626c18d.zip 列表 fluxion/ fluxion/.git/ fluxion/.git/HEAD fluxion/.git/br ...

  4. linux kernel ebtables接口

    文章目录 框架 数据结构 ebtables_init translate_table find_table_lock 过滤配置表注册 过滤配置规则的注册 数据结构 ip过滤匹配 ebt_do_tabl ...

  5. Linux CAN 驱动实验

    目录 CAN 协议简析 何为CAN CAN 电气属性 显隐性电平 接线 端接电阻 速度距离 CAN 协议 1.数据帧 2.遥控帧 3.错误帧 4.过载帧 5.帧间隔 CAN 速率 I.MX6ULL F ...

  6. Linux 音频驱动

    Linux 音频驱动 硬件介绍 WM8960与IMX6ULL之间有两个通信接口:I2C和I2S 其中I2C用于配置WM8960 I2S用于音频数据传输 修改设备树文件 编写I2C子节点设备树 code ...

  7. cups源码下载 linux_正点原子Linux第七十章Linux WIFI驱动实验

    1)资料下载:点击资料即可下载 2)对正点原子Linux感兴趣的同学可以加群讨论:935446741 3)关注正点原子公众号,获取最新资料更新 第七十章Linux WIFI驱动实验 WIFI的使用已经 ...

  8. Linux内核RCU(Read Copy Update)锁简析-前传

    如果你用Linux perf tool的top命令做热点纠察时,你会发现,前10名嫌疑犯里面肯定有好几个都是锁! 在进行并行多处理时,不可 避免地会遇到锁的问题,这是不可避免的,因为这一直以来也许是保 ...

  9. linux nload_Linux nload命令

    linux nload nload is a Linux command-line tool used to monitor network traffic and bandwidth usage i ...

最新文章

  1. 可怕!Facebook竟能识别出性工作者!你怎么看?
  2. [译] 数据可视化教程:基于Google Sheets 和 RStudio Shiny 建立实时仪表盘
  3. SAP QM MB56 报表没有结果之分析与对策
  4. Nhibernate常见的错误
  5. oracle数据库link格式,Oracle创设DB Link
  6. docker 容器基础技术
  7. unity3d 截屏
  8. spring的基本配置和使用
  9. 深度优先搜索c语言详解,深度优先搜索 — C语言版
  10. linux下的遥控器软件下载,Linux操作系统下遥控器的配置及使用方法
  11. torch.max()函数==》返回该维度的最大值以及该维度最大值对应的索引
  12. 在 MacBook 中如何将外置屏幕设置为主屏幕?
  13. 一个段子教你如何认识大数据
  14. MATLAB数学建模教学 | 史上最强的MATLAB学习网站,你需要的这里统统都有!!!
  15. POJ 3744 Scout YYF I(矩阵快速幂优化+概率dp)
  16. 《游戏引擎架构》信息总汇
  17. win10下如何关闭445端口,教程演示
  18. 软件工程第二次作业——模仿实现主流网页
  19. 【转】cidaemon.exe进程CPU占用率高怎么办?
  20. 很遗憾,这就是现实!35岁之后软件测试工程师靠什么养家?

热门文章

  1. Java读取word文档里的复杂型表格(任免表)
  2. 一行Python代码能做什么?
  3. Google中国(谷歌)汉化大事记 1
  4. QGIS制作三维模型并利用Qgis2threejs发布至CGSCloud Portal平台和gltfviewer网站
  5. java 连接solrcloud_Java操作SolrCloud
  6. 加大科技投入,赋能产业发展,联想控股2022年实现收入4837亿元
  7. [Error]校赛/ACM初级纸质备战资料——刷题大总结
  8. Python实现的深度学习技术在水文水质领域应用
  9. 掌握未来趋势的产品经理成长之路
  10. [storm]#0_storm部署