首先在github上创造一个fork,就是在你想要贡献的项目上的右上角,会有一个fork按钮。单击,就将这个项目fork到你的账户上了(fork俗称复制)
然后,你在本地git clone你刚刚fork的项目,记住,是git clone你github上的项目。
git clone https://github.com/yuzibo/neomutt.git
比如,我想fork neomutt项目,frok后我的账户下面就会多了这个:here.
当然,你的commit不应该落后于upstream,否则你就会制造conflict.此时你就需要添加上游分支。
git remote add upstream https://github.com/neomutt/neomutt.git
然后你就可以证实是否添加了上游分支。
git remote -v
无论什么时候做开发工作,你需要将主分支上commit更新过来。
git fetch upstream
#then view all branch
git branch -av
现在,切换到你的master分支and合并上游的主分支。
git checkout master
git merge upstream/master
这个工作有很多的益处,你自己所做的修改在自己的分支上,这样,很容易分离、独自管理,不会出现混乱。master是广播中的北京时间,自己的分支是你我自己手表上的时间,我们调整自己的时间,仅仅而且必须依靠北京时间。
首先,确保你是从master分支创建其他分支,这还是类似于标准北京时间。
git checkout master
#创建一个新分支,名字要自己的
# 或者使用git checkout -b new-fix 命令
git branch new-fix
#切换到你的新分支
git checkout new-fix
在你想要提交自己的pull 请求时,或许做一些清理工作以便使上游维护者更容易测试、接受和合并你的工作。
这里,首先,你的更新主分支上的commit。
# first, fetch master(for your master && upstream/master)
git fetch upstream
git checkout master
git merge upstream/master
# Rebase all commits on your branch
git checkout new-fix
git rebase master
这里假设你了解commit和push的流程,可以参考这里
然后,返回你的github项目,选择你的开发branch。
初次使用,难免会出现各种问题,下面是解决方法汇总。
其实这就涉及到了unix下从configure、make、make install一系列的步骤。
一般来说,拿到source packet后,我们首先运行./configure命令
./configure --prefix=/usr/local --enable-xx --with=yy
自己安装的软件,最后安装在/usr/local目录下,这样能够减少很多麻烦。好像,其他的软件如果需要其他的共享库,默认去/usr/local中查找,如果找不到,就会报链接错误。
Here is a task for tutorial_bcc_python_developer,how to deal with ctrl +c.From stackoverflow,the answer will be performaced:
from bcc import BPF
import signal
import sys
def bpf():
BPF(text='''
int kprobe__sys_sync(void *ctx)
{ bpf_trace_printk("Hello, yubo\\n");
return 0;}'''
).trace_print()
def signal_handler(signal, frame):
print('You pressed Ctrl+c!')
bpf()
sys.exit(0)
signal.signal(signal.SIGINT, signal_handler)
print('press Ctrl+c')
signal.pause()
Here, maybe some issue exist.But i thought, the model will be the same as c
在unix中,一个shell的执行流程如下:
读入命令
时间轴
------------------------>>
___ ____
sh----| |-------------------| |
^ |__| |___|
| | 新进程 |
| |____>"ls"--->退出 |____>"ps"
"ls"
使用execvp,这个函数有两个参数:要运行的程序名称和这个程序的命令行参数组。当程序运行时命令行参数以argv[]的形式传递给程序. 注意,将数组的第一个元素设置为程序的名称,最后一个元素必须是Null.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main()
{
char *arglist[3];
arglist[0] = "ls";
arglist[1] = "-l";
arglist[2] = 0;
printf("***中断这个程序的执行***\n");
execvp("ls", arglist);
printf("ls is done, bye\n");
}
yubo@debian:~/test/tmp/unix/shell$ ./exec
***中断这个程序的执行***
总用量 88
-rwxr-xr-x 1 yubo yubo 6108 5月 18 06:40 exec
-rw-r--r-- 1 yubo yubo 407 5月 18 07:08 exec1.c
-rw-r--r-- 1 yubo yubo 801 5月 16 19:28 execute.c
-rw-r--r-- 1 yubo yubo 29520 5月 16 19:05 segfault
-rwxr-xr-x 1 yubo yubo 13680 5月 16 19:30 smsh
-rw-r--r-- 1 yubo yubo 811 5月 16 19:30 smsh1.c
-rw-r--r-- 1 yubo yubo 389 5月 16 19:09 smsh.h
-rw-r--r-- 1 yubo yubo 2630 5月 16 19:30 splitline.c
-rwxr-xr-x 1 yubo yubo 6188 5月 16 19:49 test
-rw-r--r-- 1 yubo yubo 265 5月 16 19:48 test.c
这里你有没有注意到上面程序中的第二条printf语句消息了。这里书上的解释是:内核将新进程载入到当前进程,替换了当前进程的代码和数据,也就是说,execvp这个函数将“ls”这个命令加入到当前的程序,代替了后面的语句,从而使后面的printf语句消失掉。
下面的代码更深入一步:
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#include <unistd.h>
#define MAXARGS 20 /* cmdline args */
#define ARGLEN 100 /* token length */
int execute(char *const arglist[])
{
execvp(arglist[0], arglist);
perror("execvp failed");
exit(1);
}
char *makestring(char *buf)
{
char *cp;
buf[strlen(buf) - 1] = '\0';
cp = malloc(strlen(buf) + 1);
if (cp == NULL) {
fprintf(stderr, "no memory\n");
exit(1);
}
strcpy(cp, buf);
return cp;
}
int main()
{
char *arglist[MAXARGS + 1];
int numargs;
char argbuf[ARGLEN]; /* read stuff from here */
numargs = 0;
while( numargs < MAXARGS) {
printf("Arg[%d]?", numargs);
if (fgets(argbuf, ARGLEN, stdin) && *argbuf != '\n') {
arglist[numargs++] = makestring(argbuf);
}
else {
if (numargs > 0) {
printf("%s", argbuf);
arglist[numargs] = NULL;
if (execute(arglist))
numargs = 0;
}
}
}
return 0;
}
下面是演示:
yubo@debian:~/test/tmp/unix/shell$ ./psh
Arg[0]?ls
Arg[1]?-l
Arg[2]?.
Arg[3]?
总用量 28
-rwxr-xr-x 1 yubo yubo 6108 5月 18 18:01 exec
-rw-r--r-- 1 yubo yubo 413 5月 18 18:01 exec1.c
-rwxr-xr-x 1 yubo yubo 9012 5月 18 19:17 psh
-rw-r--r-- 1 yubo yubo 1116 5月 18 19:17 psh1.c
yubo@debian:~/test/tmp/unix/shell$ ls
exec exec1.c psh psh1.c
这个代码,条理还是比较清楚的。
上面的程序只可运行一次,如果想继续使用,需要再一次运行程序。那么,如何使用真正的shell呢?
答案是fork().
在这篇文章里,我们详细讲解了fork的用法。
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#define MAXARGS 20
#define ARGLEN 100
void execute(char **); /* execvp */
char *makestring(char *); /* to restore args vector */
int main()
{
char *arglist[MAXARGS + 1];
int numargs = 0;
char argbuf[ARGLEN];
while(numargs < MAXARGS){
printf("Arg[%d]", numargs);
if (fgets(argbuf, ARGLEN, stdin) && *argbuf != '\n')
arglist[numargs++] = makestring(argbuf);
else {
if (numargs > 0) {
arglist[numargs] = NULL;
execute(arglist);
numargs = 0;
}
}
}
return 0;
}
void execute(char *arglist[])
{
int pid, existstatus;
pid = fork(); /* make new process */
switch(pid){
case -1:
perror("fork failed");
exit(1);
case 0:
execvp(arglist[0], arglist);
perror("execvp failed");
exit(1);
default:
while(wait(&existstatus) != pid)
;
printf("child exited with status %d, %d\n",
existstatus >> 8, existstatus &0377);
}
}
char *makestring(char *buf)
{
char *cp;
buf[strlen(buf) - 1] = '\0';
cp = malloc(strlen(buf) + 1);
if (cp == NULL){
fprintf(stderr, "no memory\n");
exit(1);
}
strcpy(cp, buf);
return cp;
}
下面是输出结果:
yubo@debian:~/test/tmp/unix/shell$ ./psh
Arg[0]ls
Arg[1]-l
Arg[2].
Arg[3]
总用量 24
-rwxr-xr-x 1 yubo yubo 6108 5月 18 18:01 exec
-rwxr-xr-x 1 yubo yubo 9440 5月 18 23:00 psh
-rw-r--r-- 1 yubo yubo 1336 5月 18 23:00 psh2.c
child exited with status 0, 0
Arg[0]ps
Arg[1]
PID TTY TIME CMD
3393 pts/1 00:00:00 bash
3940 pts/1 00:00:00 psh
3944 pts/1 00:00:00 ps
child exited with status 0, 0
Arg[0]
但是,有一点是比较遗憾的:
./psh
Arg[0]tr
Arg[1][a-z]
Arg[2][A-Z]
Arg[3]
hello
HELLO
now to press
NOW TO PRESS
^C
yubo@debian:~/test/tmp/unix/shell$
针对tr程序(psh的子进程)的Ctrl+C将psh也给停止了,正确的做法是不影响父进程。
ICMP是layer 4协议,用户可以使用icmp来得到自己想要的东西。大名鼎鼎的ping就是利用ICMP的api写成的。
这个协议主要是用来处理(L3)层的错误信息与控制信息的,
这里主要是两类: 错误报文和消息报文。著名的ip(来自于iputils包)就是打开 raw socket并且发送一个ICMP_ECHO报文、产生ICMP_REPLY报文作为回应。
首先,在/net/ipv4/icmp.c中首先呼叫inet_init()(在boot阶段),方法,此方法激活icmp_init()方法,然后就会呼叫icmp_sk_init()产生ICMP包。
在 net/ipv4/af_inet.c 中有各种协议的注册。
static int __init inet_init(void)
{
struct inet_protosw *q;
struct list_head *r;
int rc = -EINVAL;
sock_skb_cb_check_size(sizeof(struct inet_skb_parm));
rc = proto_register(&tcp_prot, 1);
if (rc)
goto out;
rc = proto_register(&udp_prot, 1);
if (rc)
goto out_unregister_tcp_proto;
rc = proto_register(&raw_prot, 1);
if (rc)
goto out_unregister_udp_proto;
/*
* Tell SOCKET that we are alive...
*/
(void)sock_register(&inet_family_ops);
#ifdef CONFIG_SYSCTL
ip_static_sysctl_init();
#endif
/*
* Add all the base protocols.
*/
if (inet_add_protocol(&icmp_protocol, IPPROTO_ICMP) < 0)
pr_crit("%s: Cannot add ICMP protocol\n", __func__);
if (inet_add_protocol(&udp_protocol, IPPROTO_UDP) < 0)
pr_crit("%s: Cannot add UDP protocol\n", __func__);
if (inet_add_protocol(&tcp_protocol, IPPROTO_TCP) < 0)
pr_crit("%s: Cannot add TCP protocol\n", __func__);
#ifdef CONFIG_IP_MULTICAST
if (inet_add_protocol(&igmp_protocol, IPPROTO_IGMP) < 0)
pr_crit("%s: Cannot add IGMP protocol\n", __func__);
#endif
/* Register the socket-side information for inet_create. */
for (r = &inetsw[0]; r < &inetsw[SOCK_MAX]; ++r)
INIT_LIST_HEAD(r);
for (q = inetsw_array; q < &inetsw_array[INETSW_ARRAY_LEN]; ++q)
inet_register_protosw(q);
/*
* Set the ARP module up
*/
------->-------
接下来看看 icmp_protocal的结构
static const struct net_protocol icmp_protocol = {
.handler = icmp_rcv,
.err_handler = icmp_err,
.no_policy = 1,
.netns_ok = 1,
};
首先看看handler, 当接受到由外面发来的包,只要在ip头部等于IPPROTO_ICMP(0x1), icmp_rcv就会触发。
当设置为1,意味着不需要表现IPsec策略检查(policy check),
设置为1时提醒协议是network命名空间
icmp_sk_init() 位于net/ipv4/icmp.c 中
static int __net_init icmp_sk_init(struct net *net)
{
int i, err;
net->ipv4.icmp_sk = alloc_percpu(struct sock *);
if (!net->ipv4.icmp_sk)
return -ENOMEM;
for_each_possible_cpu(i) {
struct sock *sk;
err = inet_ctl_sock_create(&sk, PF_INET,
SOCK_RAW, IPPROTO_ICMP, net);
if (err < 0)
goto fail;
*per_cpu_ptr(net->ipv4.icmp_sk, i) = sk;
/* Enough space for 2 64K ICMP packets, including
* sk_buff/skb_shared_info struct overhead.
*/
sk->sk_sndbuf = 2 * SKB_TRUESIZE(64 * 1024);
/*
* Speedup sock_wfree()
*/
sock_set_flag(sk, SOCK_USE_WRITE_QUEUE);
inet_sk(sk)->pmtudisc = IP_PMTUDISC_DONT;
}
/* Control parameters for ECHO replies. */
net->ipv4.sysctl_icmp_echo_ignore_all = 0;
net->ipv4.sysctl_icmp_echo_ignore_broadcasts = 1;
/* Control parameter - ignore bogus broadcast responses? */
net->ipv4.sysctl_icmp_ignore_bogus_error_responses = 1;
/*
* Configurable global rate limit.
*
* ratelimit defines tokens/packet consumed for dst->rate_token
* bucket ratemask defines which icmp types are ratelimited by
* setting it's bit position.
*
* default:
* dest unreachable (3), source quench (4),
* time exceeded (11), parameter problem (12)
*/
net->ipv4.sysctl_icmp_ratelimit = 1 * HZ;
net->ipv4.sysctl_icmp_ratemask = 0x1818;
net->ipv4.sysctl_icmp_errors_use_inbound_ifaddr = 0;
return 0;
主要参考下面的这幅图片就可以了:

前64 bits是属于头文件的部分,后面的Payload包括原始包IPv4头文件和它的的负载。
头部的定义在include/uapi/linux/icmp.h
struct icmphdr {
__u8 type;
__u8 code;
__sum16 checksum;
union {
struct {
__be16 id;
__be16 sequence;
} echo;
__be32 gateway;
struct {
__be16 __unused;
__be16 mtu;
} frag;
__u8 reserved[4];
} un;
};
通过这张表,是不是可以得出这样一个结论:在网络协议中,它的bits size可以通过它的的类型的大小推断出来。
然后ICMPv4模块定义了icmp_control对象, 命名为 icmp_pointer,这个对象被ICMpv4消息的类型索引。
该定义位于 include/uapi/linux/icmp.c
/*
* ICMP control array. This specifies what to do with each ICMP.
*/
struct icmp_control {
bool (*handler)(struct sk_buff *skb);
short error; /* This ICMP is classed as an error message */
};
static const struct icmp_control icmp_pointers[NR_ICMP_TYPES+1];
其中, error是1,代表着错误消息(ICMP_DEST_UNREACH), 为0代表着正常消息(ICMP_ECHO)。 而handler有时会被赋值超过一种类型。
ping_rcv()接受一个ping的回应(ICMP_ECHOREPLY)。 位于net/ipv4/icmp.c
bool ping_rcv(struct sk_buff *skb)
{
struct sock *sk;
struct net *net = dev_net(skb->dev);
struct icmphdr *icmph = icmp_hdr(skb);
/* We assume the packet has already been checked by icmp_rcv */
pr_debug("ping_rcv(skb=%p,id=%04x,seq=%04x)\n",
skb, ntohs(icmph->un.echo.id), ntohs(icmph->un.echo.sequence));
/* Push ICMP header back */
skb_push(skb, skb->data - (u8 *)icmph);
sk = ping_lookup(net, skb, ntohs(icmph->un.echo.id));
if (sk) {
struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC);
pr_debug("rcv on socket %p\n", sk);
if (skb2)
ping_queue_rcv_skb(sk, skb2);
sock_put(sk);
return true;
}
pr_debug("no socket, dropping\n");
return false;
}
EXPORT_SYMBOL_GPL(ping_rcv);
在3.0以前的内核版本中,你需要在用户空间程序中创建一个raw socket,当接收到一个(ICMP_ECHOREPLY)消息, 原来的raw socket处理它(理解不准确)。为了详细理解这个过程,先看一个函数。
在 /net/ipv4/in_input.c中。
static int ip_local_deliver_finish(struct net *net,
struct sock *sk, struct sk_buff *skb)
{
__skb_pull(skb, skb_network_header_len(skb));
rcu_read_lock();
{
int protocol = ip_hdr(skb)->protocol;
const struct net_protocol *ipprot;
int raw;
resubmit:
raw = raw_local_deliver(skb, protocol);
ipprot = rcu_dereference(inet_protos[protocol]);
if (ipprot) {
int ret;
if (!ipprot->no_policy) {
if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb)) {
kfree_skb(skb);
goto out;
}
nf_reset(skb);
}
ret = ipprot->handler(skb);
if (ret < 0) {
protocol = -ret;
goto resubmit;
}
__IP_INC_STATS(net, IPSTATS_MIB_INDELIVERS);
} else {
if (!raw) {
if (xfrm4_policy_check(NULL,
XFRM_POLICY_IN, skb)) {
__IP_INC_STATS(net, IPSTATS_MIB_INUNKNOWNPROTOS);
icmp_send(skb, ICMP_DEST_UNREACH,
ICMP_PROT_UNREACH, 0);
}
kfree_skb(skb);
} else {
__IP_INC_STATS(net, IPSTATS_MIB_INDELIVERS);
consume_skb(skb);
}
}
}
out:
rcu_read_unlock();
return 0;
}