linux 网络栈实现分析(linux 网络栈)-冯金伟博客园

进程栈与线程栈的关系?

内核栈、用户栈

32位Linux系统上,进程的地址空间为4G,包括1G的内核地址空间—–内核栈,和3G的用户地址空间—–用户栈。

内核栈,是各个进程在刚开始建立的时候通过内存映射共享的,但是每个进程拥有独立的4G的虚拟内存空间从这一点看又是独立的,互不干扰的(只是刚开始大家都是映射的同一份内存拷贝)

用户栈就是大家所熟悉的内存四区,包括:代码区、全局数据区、堆区、栈区

用户栈中的堆区、栈区即为进程堆、进程栈

进程堆、进程栈与线程栈

1.线程栈的空间开辟在所属进程的堆区与共享内存区之间,线程与其所属的进程共享进程的用户空间,所以线程栈之间可以互访。线程栈的起始地址和大小存放在pthread_attr_t 中,栈的大小并不是用来判断栈是否越界,而是用来初始化避免栈溢出的缓冲区的大小(或者说安全间隙的大小)

2.进程初始化的时候,系统会在进程的地址空间中创建一个堆,叫进程默认堆。进程中所有的线程共用这一个堆。当然,可以增加1个或几个堆,给不同的线程共同使用或单独使用。—-一个进程可以多个堆

3、创建线程的时候,系统会在进程的地址空间中分配1块内存给线程栈,通常是1MB或4MB或8MB。线程栈是独立的,但是还是可以互访,因为线程共享内存空间

4.堆的分配:从操作系统角度来看,进程分配内存有两种方式,分别由两个系统调用完成:brk()和mmap(),glibc中malloc封装了

5.线程栈位置-内存分布测试代码

view plain copy

#include <pthread.h>

#include <stdio.h>

#include <unistd.h>

#include <string.h>

#include <errno.h>

#include <malloc.h>

#include <sys/syscall.h>

void* func(void* arg)

{

long int tid = (long int)syscall(SYS_gettid);

printf(“The ID of this thread is: %ldn”, tid );

static int a=10;

int b=11;

int* c=(int *)malloc(sizeof(int));

printf(“in thread id:%u a:%p b:%p c:%pn”,tid,&a,&b,c);

printf(“leave thread id:%ldn”,tid);

sleep(20);

free((void *)c);

}

void main()

{

pthread_t th1,th2;

printf(“pid=%un”,(int)getpid());

func(NULL);

int ret=pthread_create(&th1,NULL,func,NULL);

if(ret!=0)

{

printf(“thread1:%sn”,th1,strerror(errno));

}

ret=pthread_create(&th2,NULL,func,NULL);

if(ret!=0)

{

printf(“thread2:%sn”,th2,strerror(errno));

}

pthread_join(th1,NULL);

pthread_join(th2,NULL);

}

输出:

$ ./threadStack_main pid=16433

The ID of this thread is: 16433

in thread id:16433 a:0x60107c b:0x7fffc89ce7ac c:0x1b54010

leave thread id:16433

The ID of this thread is: 16461

The ID of this thread is: 16460

in thread id:16461 a:0x60107c b:0x7f6abb096efc c:0x7f6ab40008c0

leave thread id:16461

in thread id:16460 a:0x60107c b:0x7f6abb897efc c:0x7f6aac0008c0

leave thread id:16460

主线程调用func后

$ sudo cat /proc/16433/maps

00400000-00401000 r-xp 00000000 fd:02 11666 /home/le/code/threadStack/threadStack_main

00600000-00601000 r–p 00000000 fd:02 11666 /home/le/code/threadStack/threadStack_main

00601000-00602000 rw-p 00001000 fd:02 11666 /home/le/code/threadStack/threadStack_main

01b54000-01b75000 rw-p 00000000 00:00 0

7f6abb899000-7f6abba4f000 r-xp 00000000 fd:00 100678959 /usr/lib64/libc-2.17.so

7f6abba4f000-7f6abbc4f000 —p 001b6000 fd:00 100678959 /usr/lib64/libc-2.17.so

7f6abbc4f000-7f6abbc53000 r–p 001b6000 fd:00 100678959 /usr/lib64/libc-2.17.so

7f6abbc53000-7f6abbc55000 rw-p 001ba000 fd:00 100678959 /usr/lib64/libc-2.17.so

7f6abbc55000-7f6abbc5a000 rw-p 00000000 00:00 0

7f6abbc5a000-7f6abbc70000 r-xp 00000000 fd:00 105796566 /usr/lib64/libpthread-2.17.so

7f6abbc70000-7f6abbe70000 —p 00016000 fd:00 105796566 /usr/lib64/libpthread-2.17.so

7f6abbe70000-7f6abbe71000 r–p 00016000 fd:00 105796566 /usr/lib64/libpthread-2.17.so

7f6abbe71000-7f6abbe72000 rw-p 00017000 fd:00 105796566 /usr/lib64/libpthread-2.17.so

7f6abbe72000-7f6abbe76000 rw-p 00000000 00:00 0

7f6abbe76000-7f6abbe97000 r-xp 00000000 fd:00 105796545 /usr/lib64/ld-2.17.so

7f6abc073000-7f6abc076000 rw-p 00000000 00:00 0

7f6abc095000-7f6abc097000 rw-p 00000000 00:00 0

7f6abc097000-7f6abc098000 r–p 00021000 fd:00 105796545 /usr/lib64/ld-2.17.so

7f6abc098000-7f6abc099000 rw-p 00022000 fd:00 105796545 /usr/lib64/ld-2.17.so

7f6abc099000-7f6abc09a000 rw-p 00000000 00:00 0

7fffc89b0000-7fffc89d1000 rw-p 00000000 00:00 0

7fffc89fe000-7fffc8a00000 r-xp 00000000 00:00 0

ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0

两个子线程启动后

$ sudo cat /proc/16433/maps

00400000-00401000 r-xp 00000000 fd:02 11666 /home/le/code/threadStack/threadStack_main

00600000-00601000 r–p 00000000 fd:02 11666 /home/le/code/threadStack/threadStack_main

00601000-00602000 rw-p 00001000 fd:02 11666 /home/le/code/threadStack/threadStack_main

01b54000-01b75000 rw-p 00000000 00:00 0

7f6aac000000-7f6aac021000 rw-p 00000000 00:00 0

7f6aac021000-7f6ab0000000 —p 00000000 00:00 0

7f6ab4000000-7f6ab4021000 rw-p 00000000 00:00 0

7f6ab4021000-7f6ab8000000 —p 00000000 00:00 0

7f6aba897000-7f6aba898000 —p 00000000 00:00 0

7f6aba898000-7f6abb098000 rw-p 00000000 00:00 0

7f6abb098000-7f6abb099000 —p 00000000 00:00 0

7f6abb099000-7f6abb899000 rw-p 00000000 00:00 0

7f6abb899000-7f6abba4f000 r-xp 00000000 fd:00 100678959 /usr/lib64/libc-2.17.so

7f6abba4f000-7f6abbc4f000 —p 001b6000 fd:00 100678959 /usr/lib64/libc-2.17.so

7f6abbc4f000-7f6abbc53000 r–p 001b6000 fd:00 100678959 /usr/lib64/libc-2.17.so

7f6abbc53000-7f6abbc55000 rw-p 001ba000 fd:00 100678959 /usr/lib64/libc-2.17.so

7f6abbc55000-7f6abbc5a000 rw-p 00000000 00:00 0

7f6abbc5a000-7f6abbc70000 r-xp 00000000 fd:00 105796566 /usr/lib64/libpthread-2.17.so

7f6abbc70000-7f6abbe70000 —p 00016000 fd:00 105796566 /usr/lib64/libpthread-2.17.so

7f6abbe70000-7f6abbe71000 r–p 00016000 fd:00 105796566 /usr/lib64/libpthread-2.17.so

7f6abbe71000-7f

shell栈如何修改?

在/etc/profile 的最后面添加ulimit -s unlimited 保存,source /etc/profile使修改文件生效 linux查看修改线程默认栈空间大小 :ulimit -s 1、通过命令 ulimit -s 查看linux的默认栈空间大小,默认情况下 为10240 即10M 2、通过命令 ulimit -s 设置大小值 临时改变栈空间大小:ulimit -s 102400, 即修改为100M 3、可以在/etc/rc.local 内 加入 ulimit -s 102400 则可以开机就设置栈空间大小 4、在/etc/security/limits.conf 中也可以改变栈空间大小: #

为什么内核态转发性能低?

现在很多人都在诟病Linux内核协议栈收包效率低,不管他们是真的懂还是一点都不懂只是听别人说的,反正就是在一味地怼Linux内核协议栈,他们的武器貌似只有DPDK。

但是,即便Linux内核协议栈收包效率真的很低,这是为什么?有没有办法去尝试着优化?而不是动不动就DPDK。

我们从最开始说起。

Linux内核作为一个通用操作系统内核,脱胎于UNIX那一套现代操作系统理论。

但一开始不知道怎么回事将网络协议栈的实现塞进了内核态,从此它就一直在内核态了。既然网络协议栈的处理在内核态进行,那么网络数据包必然是在内核态被处理的。无论如何,数据包要先进入内核态,这就涉及到了进入内核态的方式:

外部可以从两个方向进入内核-从用户态系统调用进入或者从硬件中断进入。

也就是说,系统在任意时刻,必然处在两个上下文中的一个:

进程上下文

中断上下文 (在非中断线程化的系统,也就是任意进程上下文)

收包逻辑的协议栈处理显然是自网卡而上的,它显然是在中断上下文中,而数据包往用户进程的数据接收处理,显然是在应用程序的进程上下文中, 数据包通过socket在两个上下文中被转接。

在socket层的数据包转接处,必然存在着一个队列缓存,这是一个典型的 生产者-消费者 模型,中断上下文的终点作为生产者将数据包入队,而进程上下文作为消费者从队列消费数据包