linux

curl 各阶段耗时

curl -X GET -w “\ntime_namelookup:%{time_namelookup}\ntime_connect: %{time_connect}\ntime_starttransfer: %{time_starttransfer}\ntime_total: %{time_total}\n” xxxx

阅读全文

指针的最后三位永远是0

深入理解nginx中,第9章中有一句:利用指针的最后一位一定是0的特性。能解释一下这个特性?

阅读全文

非阻塞套接字

在阻塞和非阻塞两种模式下执行I/O操作。在阻塞模式下,在I/O操作完成前,执行的操作函数一直等候而不会立即返回,该函数所在的线程会阻塞在这里。相反,在非阻塞模式下,套接字函数会立即返回,而不管I/O是否完成,该函数所在的线程会继续运行。

阅读全文

vimmap

在vim配置文件中经常会看到map、nmap、imap、vmap、vnoremap、nunmap、nmapclear等,都是什么意思呢?

阅读全文

process

http://www.howtoip.com/how-to-use-the-mac-terminals-hidden-task-manager-to-see-background-processes/

vi /etc/ld.so.preload

删除相关内容 [root@redis02 ~]# echo $LD_PRELOAD 结果为空 清理完成后,top已经可以看到之前隐藏进程

阅读全文

nsenter

nsenter也可以进入mnt, uts, ipc, pid, user命令空间,以及指定根目录和工作目录。

阅读全文

net-tools

ifconfig

$ apt-get install net-tools

ip

$ apt-get install iproute2

ping

$ apt-get install iputils-ping

阅读全文

completion bash自动补全

要让可编程命令补全功能在你的终端起作用 ,你只需要如下执行/etc/bash_completion即可:

阅读全文

route 路由表

通过 route 命令查看 Linux 内核的路由表: [root@VM_139_74_centos ~]# route Kernel IP routing table Destination Gateway Genmask Flags Metric Ref Use Iface default gateway 0.0.0.0 UG 0 0 0 eth0 10.0.0.10 10.139.128.1 255.255.255.255 UGH 0 0 0 eth0 10.139.128.0 0.0.0.0 255.255.224.0 U 0 0 0 eth0 link-local 0.0.0.0 255.255.0.0 U 1002 0 0 eth0 172.17.0.0 0.0.0.0 255.255.0.0 U 0 0 0 docker0 172.18.0.0 0.0.0.0 255.255.0.0 U 0 0 0 br-0ab63c131848 172.19.0.0 0.0.0.0 255.255.0.0 U 0 0 0 br-bccbfb788da0 172.20.0.0 0.0.0.0 255.255.0.0 U 0 0 0 br-7485db25f958 [root@VM_139_74_centos ~]# route -n Kernel IP routing table Destination Gateway Genmask Flags Metric Ref Use Iface 0.0.0.0 10.139.128.1 0.0.0.0 UG 0 0 0 eth0 10.0.0.10 10.139.128.1 255.255.255.255 UGH 0 0 0 eth0 10.139.128.0 0.0.0.0 255.255.224.0 U 0 0 0 eth0 169.254.0.0 0.0.0.0 255.255.0.0 U 1002 0 0 eth0 172.17.0.0 0.0.0.0 255.255.0.0 U 0 0 0 docker0 172.18.0.0 0.0.0.0 255.255.0.0 U 0 0 0 br-0ab63c131848 172.19.0.0 0.0.0.0 255.255.0.0 U 0 0 0 br-bccbfb788da0 172.20.0.0 0.0.0.0 255.255.0.0 U 0 0 0 br-7485db25f958 列 含义 Destination 目标网络或目标主机。Destination 为 default(0.0.0.0)时,表示这个是默认网关,所有数据都发到这个网关(这里是 10.139.128.1) Gateway 网关地址,0.0.0.0 表示当前记录对应的 Destination 跟本机在同一个网段,通信时不需要经过网关 Genmask Destination 字段的网络掩码,Destination 是主机时需要设置为 255.255.255.255,是默认路由时会设置为 0.0.0.0 Flags 标记,含义参考表格后面的解释 Metric 路由距离,到达指定网络所需的中转数,是大型局域网和广域网设置所必需的 (不在Linux内核中使用。) Ref 路由项引用次数 (不在Linux内核中使用。) Use 此路由项被路由软件查找的次数 Iface 网卡名字,例如 eth0

阅读全文

rcfile

修改bash提示符: 首先将现在的进行备份: echo $PS1 >ps1.txt 然后在命令行设置如下一条命令: PS1=”[\e[1;31m\u\e[1;37m@\e[1;32m\h \e[1;33m\W\e[0m]$”

阅读全文

netns mac

https://github.com/vishvananda/netns I have successfully installed and configured tuntap (http://tuntaposx.sourceforge.net/) and it works great. By editing

阅读全文

iproute2mac IProute2

OS X是BSD系的内核,高级路由用pf(旧版的可能有ipfw可以用)

阅读全文

Makefile 增量编译

在Linux中,通过控制Makefile实现增量编译的效果

阅读全文

shell =~

在shell脚本中,经常遇到 “=~” ,表达形式如下:

阅读全文

nf_conntrack

nf_conntrack(在老版本的 Linux 内核中叫 ip_conntrack)是一个内核模块,用于跟踪一个连接的状态的。连接状态跟踪可以供其他模块使用,最常见的两个使用场景是 iptables 的 nat 的 state 模块。 iptables 的 nat 通过规则来修改目的/源地址,但光修改地址不行,我们还需要能让回来的包能路由到最初的来源主机。这就需要借助 nf_conntrack 来找到原来那个连接的记录才行。而 state 模块则是直接使用 nf_conntrack 里记录的连接的状态来匹配用户定义的相关规则。例如下面这条 INPUT 规则用于放行 80 端口上的状态为 NEW 的连接上的包。

阅读全文

Cockpit

Cockpit是CentOS 8内置的一款基于Web的可视化管理工具,对一些常见的命令行管理操作都有界面支持,比如用户管理、防火墙管理、服务器资源监控等,使用非常方便,号称人人可用的Linux管理工具。

阅读全文

echo 生成 md5多换行符

在Mac的bash下边默认有一个md5工具,所以直接调用即可,使用方法

阅读全文

bash

bash的«, «<, < <()用法

阅读全文

dnsutils bind-tools

dig and nslookup are in dnsutils on Ubuntu (debian) Install dig and nslookup on Alpine dig and nslookup are in bind-tools on Alpine:

阅读全文

ulimit

当用linux做高并发服务器时,会遇到”Too many open files”的错误。

阅读全文

nginx惊群问题

Nginx处于充分发挥多核CPU架构性能的考虑,使用了多个worker子进程监听相同端口的设计,这样多个子进程在accept建立新连接时会有争抢,这会带来著名的“惊群”问题,子进程数量越多越明显,这会造成系统性能的下

阅读全文

htop

https://htop.dev/ This is htop, a cross-platform interactive process viewer. It is a text-mode application (for console or X terminals) and requires ncurses.

阅读全文

iptables自定义链

当默认链中的规则非常多时,不方便我们管理。

阅读全文

dns

rpc error: code = Unknown desc = dial tcp: lookup on: write udp : write: operation not permitted

阅读全文

Shell去除空格和空行的方法

一、去除空行的方法

阅读全文

route print netstat nr

route print命令可以查看路由表,在dos下面输入route print 就可以了,如何读懂路由表 Active Routes:
Network Destination Netmask Gateway Interface Metric
0.0.0.0 0.0.0.0 202.256.257.1 202.256.257.258 1
127.0.0.0 255.0.0.0 127.0.0.1 127.0.0.1 1
202.256.257.0 255.255.255.0 202.256.257.258 202.256.257.258 1
202.256.257.258 255.255.255.255 127.0.0.1 127.0.0.1 1
202.256.257.255 255.255.255.255 202.256.257.258 202.256.257.258 1
224.0.0.0 224.0.0.0 202.256.257.258 202.256.257.258 1
255.255.255.255 255.255.255.255 202.256.257.258 202.256.257.258 1
Default Gateway: 202.256.257.1

阅读全文

pfctl

iptables是Linux下的防火墙,可以进行数据包的过滤,在网络层进行数据的转发、拦截或丢弃等,使用非常普遍,功能也非常强大。但是Mac下没有iptables,为了实现流量转发和过滤,要使用到Mac自带的PFctl。PFctl即control the packet filter,是Unix LIKE系统上进行TCP/IP流量过滤和网络地址转换的系统,也能提供流量整形和控制等

阅读全文

Cgroup

两种方法来查看系统的当前 cgroup 信息。第一种方法是通过 systemd-cgls 命令来查看,它会返回系统的整体 cgroup 层级,cgroup 树的最高层由 slice 构成 可以看到系统 cgroup 层级的最高层由 user.slice 和 system.slice 组成。因为系统中没有运行虚拟机和容器,所以没有 machine.slice,所以当 CPU 繁忙时,user.slice 和 system.slice 会各获得 50% 的 CPU 使用时间。 user.slice 下面有两个子 slice:user-1000.slice 和 user-0.slice,每个子 slice 都用 User ID (UID) 来命名,因此我们很容易识别出哪个 slice 属于哪个用户。例如:从上面的输出信息中可以看出 user-1000.slice 属于用户 tom,user-0.slice 属于用户 root。 systemd-cgls 命令提供的只是 cgroup 层级的静态信息快照,要想查看 cgroup 层级的动态信息,可以通过 systemd-cgtop 命令查看 https://juejin.cn/post/6844903858116755463

阅读全文

局部变量及local命令

local一般用于局部变量声明,多在在函数内部使用。

阅读全文

查看Linux系统架构类型的5条常用命令

uname -a Linux 6c8638f93366 5.10.47-linuxkit #1 SMP PREEMPT Sat Jul 3 21:50:16 UTC 2021 aarch64 GNU/Linux

阅读全文

http_proxy

对于curl,wget等命令,会受http_proxy和https_proxy的环境变量的影响 vi /etc/profile #设置http代理 http_proxy=http://172.16.2.1:8000 #设置https代理 https_proxy=http://172.16.2.1:8000 #设置不通过代理服务器链接 no_proxy=.xxx.com,10.,www.baidu.com export all_proxy=http://proxy.example.com:8080 https://blog.csdn.net/weixin_42488171/article/details/108742570 https://keqingrong.cn/blog/2021-02-19-proxy-and-pac/

阅读全文

env 环境变量存哪里

Linux中环境变量包括系统级和用户级,系统级的环境变量是每个登录到系统的用户都要读取的系统变量,而用户级的环境变量则是该用户使用系统时加载的环境变量。 所以管理环境变量的文件也分为系统级和用户级的

阅读全文

The Linux Programming Interface

https://www.man7.org/tlpi/index.html

阅读全文

硬链接软连接

ln –s 被链接的源文件 链接文件

建立文件的软连接,用通俗的是方式类似于Windows下的快捷链接

阅读全文

ip 伪造

由于TCP需要三次握手连接,在实现正常的TCP/IP 双方通信情况下,是无法伪造来源 IP 的,也就是说,在 TCP/IP 协议中,可以伪造数据包来源 IP ,但这会让发送出去的数据包有去无回,无法实现正常的通信。

阅读全文

wget apt-get yum rpm 区别

wget 类似于迅雷,是一种命令行下载工具,通过HTTP、HTTPS、FTP三个最常见的TCP/IP协议下载。

阅读全文

iTerm2保持ssh连接不断开

方法一:修改iTerm2配置,每隔60s发送一个字符 profiles -> sessions -> When idle, send ASCII code,建议填ASCII码填0,对应是空格 方法二:修改ssh配置,间隔60s发送一个no-op包 编辑本地ssh配置vim ~/.ssh/config,在首行添加如下配置

阅读全文

wss RSS/VSS/USS/PSS

Working Set Size (WSS) is how much memory an application needs to keep working. Your application may have 100 Gbytes of main memory allocated and page mapped, but it is only touching 50 Mbytes each second to do its job. That’s the working set size: the “hot” memory that is frequently used. It is useful to know for capacity planning and scalability analysis.

阅读全文

shell 生成 UUID

几乎所有Unix和类Unix环境中都包含了一个名为uuidgen的小工具,运行即可生成一个UUID。

阅读全文

iptables 实现负载均衡

iptables -t nat -A PREROUTING -d 10.192.0.65/32 -p tcp -m tcp –dport 8080 -m statistic –mode nth –every 2 –packet 0 -j DNAT –to-destination 10.1.160.14:8080 iptables -t nat -A POSTROUTING -d 10.1.160.14/32 -p tcp -m tcp –dport 8080 -j SNAT –to-source 10.192.0.65

阅读全文

tso

tso(TCP Segment Offload)是一种利用网卡的少量处理能力,降低CPU发送数据包负载的技术,需要网卡硬件及驱动的支持。

阅读全文

tgw LVS

TGW,全称Tencent GateWay,是一套实现多网统一接入、外网网络请求转发、支持自动负载均衡的系统。 TGW向开发者免费提供服务,基于HTTP协议的应用可以直接接入,基于其他私有协议的应用只需要进行少量改造即可快速接入。 TGW自动进行域名解析,应用接入TGW后,即可使用域名方式对外提供服务,以及电信/联通/移动三网接入。 此外,TGW支持后端带权重的负载均衡,应用无需关注负载均衡。

阅读全文

jp2a Textaizer 用ASCII字符拼成图案

https://github.com/cslarsen/jp2a

阅读全文

dd

linux下创建大文件

阅读全文

bin目录区别

https://php-osx.liip.ch/

阅读全文

SIP

有时候我们安装一些工具软件需要将文件拷贝到系统限制更改的文件夹中,甚至有时需要更改系统限制的文件,而这时Mac会提示系统文件不能修改之类的内容,而这时我们想要继续操作必须关闭Mac电脑的“系统完整性保护”机制(SIP)

阅读全文

split

分割文件 文件分割可以使用split命令,该即支持文本文件分割,又支持二进制文件分割;而合并文件可以使用cat命令。

阅读全文

Mac使用Launchd命令行lauchctl

mac的守护进程目录有以下几处:

阅读全文

Bash中如何判断一个命令是否存在

command 命令

阅读全文

iptables 限制连接数

上面限制端口连接数主要用到的模块是connlimit。 -A INPUT -p tcp –syn –dport 22 -m connlimit –connlimit-above 50 –connlimit-mask 0 -j DROP

阅读全文

chisel lldb

https://github.com/facebook/chisel

阅读全文

set

执行脚本的时候,如果遇到不存在的变量,Bash 默认忽略它。 set -u就用来改变这种行为。脚本在头部加上它,遇到不存在的变量就会报错,并停止执行

阅读全文

解决 ln -s 软链接产生的Too many levels of symbolic links错误

解决 ln -s 软链接产生的Too many levels of symbolic links错误 解决方法:在使用ln -s命令时,使用绝对路径取代相对路径,例如:

阅读全文

libfaketime

https://github.com/wolfcw/libfaketime

阅读全文

ld_preload

https://www.cnblogs.com/net66/p/5609026.html https://www.zcfy.cc/article/dynamic-linker-tricks-using-ld-preload-to-cheat-inject-features-and-investigate-programs https://stackoverflow.com/questions/426230/what-is-the-ld-preload-trick

阅读全文

grep 正则表达式

grep 转义字符“\” 查找,因为“\”grep 命令里是转义字符,所以需要特殊的处理,

阅读全文

cURL与wget的区别

当想要直接通过 Linux 命令行下载文件,马上就能想到两个工具:wget 和 cURL。它们有很多一样的特征,可以很轻易的完成一些相同的任务,虽然它们有一些相似的特征,但它们并不是完全一样。这两个程序适用与不同的场合,在特定场合下,都拥有各自的特性。

阅读全文

expand_aliases Bash非交互模式下alias不能使用

在Bash的非交互模式下(一般的脚本), alias是无效的.

阅读全文

log

目前业界比较流行的日志采集主要有Fluentd、Logstash、Flume、scribe等,阿里巴巴内部则是LogAgent、阿里云则是LogTail,这些产品中Fluentd占据了绝对的优势并成功入驻CNCF阵营,它提出的统一日志层(Unified Logging Layer)大大的减少了整个日志采集和分析的复杂度。Fluentd认为大多数现存的日志格式其结构化都很弱,这得益于人类出色的解析日志数据的能力,因为日志数据其最初是面向人类的,人类是其主要的日志数据消费者。为此Fluentd希望通过统一日志存储格式来降低整个日志采集接入的复杂度,假想下输入的日志数据比如有M种格式,日志采集Agent后端接入了N种存储,那么每一种存储系统需要实现M种日志格式解析的功能,总的复杂度就是M*N,如果日志采集Agent统一了日志格式那么总的复杂度就变成了M + N。这就是Fluentd的核心思想,另外它的插件机制也是一个值得称赞的地方。Logstash和Fluentd类似是属于ELK技术栈,在业界也被广泛使用,关于两者的对比可以参考这篇文章Fluentd vs. Logstash: A Comparison of Log Collectors

阅读全文

inode

$ls -i a b 523669 a 523669 b $rm -f a $ls -i b 523669 b 因为它会在访问控制规则中打开一个漏洞。能否打开一个文件不仅取决于它自己的访问权限位,还取决于每个包含目录的权限位。(例如,在您的示例中,如果test.txt是模式644,但包含目录test是模式700,则只有root和test的所有者可以打开test.txt)inode编号仅标识文件,而不标识包含目录(文件可能位于多个目录中;请阅读“硬链接”),因此内核无法仅使用inode号执行完整的访问控制检查集。

阅读全文

文件IO 文件位置偏移量

每一个文件被打开之后,内核都维护一个所谓的当前文件位置偏移量,读和写操作都会对这个偏移量产生影响。

阅读全文

CodeViz

CodeViz是《Understanding The Linux Virtual Memory Manager》(at Amazon,下载地址在页尾)的作者 Mel Gorman 写的一款分析C/C++源代码中函数调用关系的open source工具(类似的open source软件有 egypt、ncc)。其基本原理是给 GCC 打个补丁,让它在编译时每个源文件时 dump 出其中函数的 call graph,然后用 Perl 脚本收集并整理调用关系,转交给Graphviz绘制图形。

阅读全文

cmake

如何彻底清除cmake产生的缓存 cmake并没有提供类似于

阅读全文

tcpdump

sudo tcpdump -i lo tcp and host 127.0.0.1 -vvvX -w xiazemin.cap tcpdump tcpdump - dump traffic on a network

阅读全文

shell 常用命令

Shell中的 test 命令用于检查某个条件是否成立,它可以进行数值、字符和文件三个方面的测试。

阅读全文

Curl命令的data data-ascii data-binary data-raw和data-urlencode选项

Curl命令为HTTP POST方法提供了若干种设置数据的选项,这里比较一下。

阅读全文

dsl

程序转换系统,它将一种语言(你的DSL)的语法结构映射到其他语言中的语法模式。这样的工具可以在代码生成项目期间执行任意转换(树重写一般化字符串重写,这是Post系统,具有完全图灵功能),这意味着您生成的内容和生成过程的复杂程度仅由您的野心决定,而不是“代码生成器框架”属性。 Sophtisticated程序转换系统结合了各种类型的范围,流量分析和/或自定义分析器来实现转换。这并没有增加任何理论能力,但它增加了许多实际功能:大多数真实语言(甚至DSL)都有名称空间,控制和数据流,需要类型推断等。 我们的DMS软件再造工具包就是这种类型的转换系统。它已被用于分析/转换传统语言和DSL,简单和复杂的语言,以及小型,大型甚至是巨大的软件系统。 与OP关于“将AST转换为其他语言”的评论相关,这是由DMS通过编写将DSL的表面语法(在他的DSL的AST后面实现)映射到目标语言的表面语法(使用目标实现)的转换来完成的。语言AST)。然后由DMS自动绘制得到的目标语言AST,以提供目标语言的实际源代码,其对应于目标AST。
https://zhuanlan.zhihu.com/p/25696396 https://segmentfault.com/a/1190000015127116

阅读全文

正则表达式 分组匹配

具名组匹配 问号 + 尖括号 + 组名 + 要匹配的内容

阅读全文

proto3

https://segmentfault.com/a/1190000009157513 https://cloud.google.com/apis/design/proto3 https://landing.google.com/sre/

阅读全文

prometheus

https://github.com/prometheus/prometheus Prometheus 是由 SoundCloud 开源监控告警解决方案。

阅读全文

让Linux终端中执行的程序在后台运行 从前台变到后台

在Linux中,如果要让进程在后台运行,一般情况下,我们在命令后面加上&即可,实际上,这样是将命令放入到一个作业队列中了:

阅读全文

Supervisor

supervisor是一个Linux/Unix系统上的进程监控工具,supervisor是一个Python开发的通用的进程管理程序,可以管理和监控Linux上面的进程,能将一个普通的命令行进程变为后台daemon,并监控进程状态,异常退出时能自动重启。不过同daemontools一样,它不能监控daemon进程

阅读全文

zabbix 监控系统

zabbix能监视各种网络参数,保证服务器系统的安全运营;并提供灵活的通知机制以让系统管理员快速定位/解决存在的各种问题。 zabbix由2部分构成,zabbix server与可选组件zabbix agent。 zabbix server可以通过SNMP,zabbix agent,ping,端口监视等方法提供对远程服务器/网络状态的监视,数据收集等功能,它可以运行在Linux,Solaris,HP-UX,AIX,Free BSD,Open BSD,OS X等平台上。 https://www.cnblogs.com/clsn/p/7885990.html

阅读全文

proc 文件系统 系统监控

https://github.com/c9s/goprocinfo http://blog.x2know.org/2016/06/28/monitor-system/ http://blog.x2know.org/2016/07/03/monitor-system2/ https://github.com/open-falcon/book/blob/master/zh/faq/linux-metrics.md

阅读全文

监控知识体系

https://zhuanlan.zhihu.com/p/26369145

阅读全文

open falcon

基于内存的时序数据库 http://www.vldb.org/pvldb/vol8/p1816-teller.pdf http://www.evanlin.com/gorilla-facebook-tsdb/ https://github.com/dgryski/go-tsz https://github.com/open-falcon/rrdlite

阅读全文

Smokeping

https://oss.oetiker.ch/smokeping/ Smokeping is a latency measurement tool. It sends test packets out to the net and measures the amount of time they need to travel from one place to the other and back. For every round of measurement smokeping sends several packets. It then sorts the different round trip times and selects the median, (ie. the middle one). This means when there are 10 time values, value number 5 is selected and drawn. The other values are drawn as successively lighter shades of gray in the background (smoke).

阅读全文

ss 查看SOCKET使用情况

1、命令格式: ss [参数] ss [参数] [过滤] 2、命令功能: ss(Socket Statistics的缩写)命令可以用来获取 socket统计信息,此命令输出的结果类似于 netstat输出的内容,但它能显示更多更详细的 TCP连接状态的信息,且比 netstat 更快速高效。它使用了 TCP协议栈中 tcp_diag(是一个用于分析统计的模块),能直接从获得第一手内核信息,这就使得 ss命令快捷高效。在没有 tcp_diag,ss也可以正常运行。 3、命令参数: -h, –help 帮助信息 -V, –version 程序版本信息 -n, –numeric 不解析服务名称 -r, –resolve 解析主机名 -a, –all 显示所有套接字(sockets) -l, –listening 显示监听状态的套接字(sockets) -o, –options 显示计时器信息 -e, –extended 显示详细的套接字(sockets)信息 -m, –memory 显示套接字(socket)的内存使用情况 -p, –processes 显示使用套接字(socket)的进程 -i, –info 显示 TCP内部信息 -s, –summary 显示套接字(socket)使用概况 -4, –ipv4 仅显示IPv4的套接字(sockets) -6, –ipv6 仅显示IPv6的套接字(sockets) -0, –packet 显示 PACKET 套接字(socket) -t, –tcp 仅显示 TCP套接字(sockets) -u, –udp 仅显示 UCP套接字(sockets) -d, –dccp 仅显示 DCCP套接字(sockets) -w, –raw 仅显示 RAW套接字(sockets) -x, –unix 仅显示 Unix套接字(sockets) -f, –family=FAMILY 显示 FAMILY类型的套接字(sockets),FAMILY可选,支持 unix, inet, inet6, link, netlink -A, –query=QUERY, –socket=QUERY QUERY := {all|inet|tcp|udp|raw|unix|packet|netlink}[,QUERY] -D, –diag=FILE 将原始TCP套接字(sockets)信息转储到文件 -F, –filter=FILE 从文件中都去过滤器信息 FILTER := [ state TCP-STATE ] [ EXPRESSION ] 4、使用实例:

阅读全文

shell脚本设置环境变量不生效

碰到在shell脚本设置了环境变量,如export PATH=$PATH:/usr/local/HNR_target/bin,执行了此脚本后,环境变量没有生效的问题。

阅读全文

bz2 Mac 文件解压缩

tar

阅读全文

bash if

1、if的单分支语法格式:

阅读全文

检测三种不同操作系统的Bash脚本

检测三种不同操作系统(GNU/Linux, Mac OS X, Windows NT)的Bash脚本。

阅读全文

sed 替换括号

我试图使用这个命令: sed -i ‘s#{test1}#test2#’ /example/myfile.txt 用test2替换{test1}的实例.

阅读全文

cores 用lldb打不开 invalid process

$lldb /cores/core.68807 (lldb) target create “/cores/core.68807” warning: (x86_64) /cores/core.68807 load command 107 LC_SEGMENT_64 has a fileoff + filesize (0x2820e000) that extends beyond the end of the file (0x2820d000), the segment will be truncated to match warning: (x86_64) /cores/core.68807 load command 108 LC_SEGMENT_64 has a fileoff (0x2820e000) that extends beyond the end of the file (0x2820d000), ignoring this section Current executable set to ‘/cores/core.68807’ (x86_64). (lldb) bt error: invalid process $lldb –core “/cores/core.68807” (lldb) target create –core “/cores/core.68807” warning: (x86_64) /cores/core.68807 load command 107 LC_SEGMENT_64 has a fileoff + filesize (0x2820e000) that extends beyond the end of the file (0x2820d000), the segment will be truncated to match warning: (x86_64) /cores/core.68807 load command 108 LC_SEGMENT_64 has a fileoff (0x2820e000) that extends beyond the end of the file (0x2820d000), ignoring this section Core file ‘/cores/core.68807’ (x86_64) was loaded. (lldb) bt

  • thread #1: tid = 0x0000, 0x00007fff94c100ae libsystem_kernel.dylib`__pthread_kill + 10, stop reason = signal SIGSTOP
    • frame #0: 0x00007fff94c100ae libsystem_kernel.dylib__pthread_kill + 10 frame #1: 0x00007fff92523500 libsystem_pthread.dylibpthread_kill + 90 frame #2: 0x00007fff9a8ad41b libsystem_c.dylib__abort + 145 frame #3: 0x00007fff9a8adcfa libsystem_c.dylib__stack_chk_fail + 200 frame #4: 0x0000000104ff8a61 myFile.sozif_file_read(execute_data=0x0000000105018230, return_value=0x00000001050181a0) + 1153 at myFile.c:395 frame #5: 0x000000010454135d phpZEND_DO_ICALL_SPEC_RETVAL_USED_HANDLER(execute_data=0x0000000105018030) + 141 at zend_vm_execute.h:675 frame #6: 0x00000001044e5964 phpexecute_ex(ex=0x0000000105018030) + 100 at zend_vm_execute.h:432 frame #7: 0x0000000104ff7d08 myFile.somy_execute_ex(execute_data=0x0000000105018030) + 392 at myFile.c:218 frame #8: 0x00000001044e5b6a phpzend_execute(op_array=0x0000000105075400, return_value=0x0000000000000000) + 234 at zend_vm_execute.h:474 frame #9: 0x000000010447ef12 phpzend_execute_scripts(type=8, retval=0x0000000000000000, file_count=3) + 594 at zend.c:1447 frame #10: 0x00000001043d4721 phpphp_execute_script(primary_file=0x00007fff5bc6a8a8) + 1201 at main.c:2533 frame #11: 0x000000010459a2b5 phpdo_cli(argc=2, argv=0x00007fff5bc6afd0) + 3941 at php_cli.c:990 frame #12: 0x000000010459915a phpmain(argc=2, argv=0x00007fff5bc6afd0) + 1898 at php_cli.c:1378 frame #13: 0x00007fff8ad7c5ad libdyld.dylibstart + 1

    https://stackoverflow.com/questions/32731663/gdb-doesnt-read-the-core-dump-on-macos

阅读全文

Bus error 10

Bus error: 10 ulimit -c ulimited

Bus error: 10 (core dumped)

$lldb /cores/core.14533 (lldb) target create “/cores/core.14533” warning: (x86_64) /cores/core.14533 load command 108 LC_SEGMENT_64 has a fileoff + filesize (0x28a0e000) that extends beyond the end of the file (0x28a0d000), the segment will be truncated to match warning: (x86_64) /cores/core.14533 load command 109 LC_SEGMENT_64 has a fileoff (0x28a0e000) that extends beyond the end of the file (0x28a0d000), ignoring this section Current executable set to ‘/cores/core.14533’ (x86_64). (lldb) bt error: invalid process https://stackoverflow.com/questions/212466/what-is-a-bus-error

阅读全文

make

config/configure/Configure、make 、make test/make check、sudo make install 的作用

阅读全文

文件句柄泄漏

问题出现的表象:

  1. 所有客户端都无法登陆服务器,正常游戏的玩家退出后重新登陆也失败.
  2. 查看日志,出现大量socket加入队列失败的情况。
阅读全文

mac上分析coredump文件

首先 开启 ulimit -c unlimited. $dphpcbf -vvv src/Client/AthenaApiV3Client.php Segmentation fault: 11 (core dumped)

阅读全文

mac gdb 安装

最开始brew install 提示需要安装gcc 安装gcc 卡死了 make BOOT_LDFLAGS=-Wl,-headerpad_max_install_names 于是放弃,直接用源码安装

阅读全文

core 文件 调试 追踪 Segmentation fault 11

phpcbf -vvv src/xxx.php Segmentation fault ls /cores/ 空的 mac 默认没有开启 ulimit -c unlimited phpcbf -vvv src/xxx.php Segmentation fault: 11 (core dumped) ls /cores $ls /cores/ core.62036 core.63911 core.83675

阅读全文

find

  1. 用文件名查找文件
阅读全文

rsync scp sftp 文件同步方式

对于线下环境 直接 scp -r /home/user/xxx user@10.96.10.10:/home/user/xxx

阅读全文

sched_init

这个很熟是进程调度初始化函数,主要做了设置进程的GDT,LDT描述符,设置系统定时器中断,系统调用终端,代码如下:

阅读全文

du

【1】du命令用来查看目录或文件所占用磁盘空间的大小。常用选项组合为:

阅读全文

linux下使用binfmt_misc设定不同二进制的打开程序

在Windows平台上,可以绑定拥有特定扩展名的文件,使用特定的程序打开。比如,PDF文件就使用Acrobat Reader打开。这样做确实极大的方便了用户的使用体验。

阅读全文

Debug实现原理

https://tpaschalis.github.io/delve-debugging/ https://jiajunhuang.com/articles/2020_04_23-go_gdb.md.html

阅读全文

TCP协议中的疑难杂症

说到TCP协议,相信大家都比较熟悉了,对于TCP协议总能说个一二三来,但是TCP协议又是一个非常复杂的协议,其中有不少细节点让人头疼点。本文就是来说说这些头疼点的,浅谈一些TCP的疑难杂症。那么从哪说起呢?当然是从三次握手和四次挥手说起啦,可能大家都知道TCP是三次交互完成连接的建立,四次交互来断开一个连接,那为什么是三次握手和四次挥手呢?反过来不行吗?

阅读全文

Epoll的本质 内部实现原理

从事服务端开发,少不了要接触网络编程。epoll作为linux下高性能网络服务器的必备技术至关重要,nginx、redis、skynet和大部分游戏服务器都使用到这一多路复用技术。

阅读全文

socket 的 connect、listen、accept 和全连接队列、半连接队列的原理

  1. accept 只是从全连接队列拿出一个已经建立好的socket,如果队列为空,则阻塞。
阅读全文

pagecache 预读

Linux文件预读算法磁盘I/O性能的发展远远滞后于CPU和内存,因而成为现代计算机系统的一个主要瓶颈。预读可以有效的减少磁盘的寻道次数和应用程序的I/O等待时间,是改进磁盘读I/O性能的重要优化手段之一… 从寄存器、L1/L2高速缓存、内存、闪存,到磁盘/光盘/磁带/存储网络,计算机的各级存储器硬件组成了一个金字塔结构。越是底层存储容量越大。然而访问速度也越慢,具体表现为更小的带宽和更大的延迟。因而这很自然的便成为一个金字塔形的逐层缓存结构。由此产生了三类基本的缓存管理和优化问题:

阅读全文

Linux如何实现共享内存

为什么实现共享内存? 采用共享内存通信的一个显而易见的好处是效率高,因为进程可以直接读写内存,而不需要任何数据的拷贝。对于像管道和消息队列等通信方式,则需要在内核和用户空间进行四次的数据拷贝,而共享内存则只拷贝两次数据[1]:一次从输入文件到共享内存区,另一次从共享内存区到输出文件。实际上,进程之间在共享内存时,并不总是读写少量数据后就解除映射,有新的通信时,再重新建立共享内存区域。而是保持共享区域,直到通信完毕为止,这样,数据内容一直保存在共享内存中,并没有写回文件。共享内存中的内容往往是在解除映射时才写回文件的。因此,采用共享内存的通信方式效率是非常高的。

阅读全文

cgroup

cgroup从2.6.4引入linux内核主线,目前默认已启用该特性。在cgroup出现之前,只能对一个进程做资源限制,比如通过sched_setaffinity设置进程cpu亲和性,使用ulimit限制进程打开文件上限、栈大小等。

阅读全文

tcp

tcp向上层提供的数据肯定是按顺序排好的。比如一共有1 2 3 4 5五个包,假设前3个包的大小可以填满tcp的缓冲区,那么如果第4个包丢失,这时候并不会影响应用层接收前三个包。而如果是前三个包里面发生丢失,即使4 5都正常接收也不行,因为前面还有包没有收到,这时候tcp是不会向上层提供数据的。

阅读全文

SO_REUSEPORT 端口复用

SO_REUSEPORT 套接字选项 从 Linux 3.9内核版本之后Linux网络协议栈开始支持 SO_REUSEPORT 套接字选项,这个新的选项允许一个主机上的多个套接字绑定到同一个端口上,它的目的是提高运行在多核CPU上的多线程网络服务应用的处理性能。

阅读全文

两个进程同时监听同一个端口

问题一:我自己写了一个TCP程序监听本机的9530端口,程序显示绑定成功。此时,我又打开一个socket调试工具(该工具名称为“Sokit”)也监听本机的9530端口,此时未报错,而是成功进入监听状态。按理说应该会提示“端口被占用”,如何解释两边同时成功监听了同一端口? 问题二:上述两者正在监听同一端口,为了验证谁真正进入了监听状态,我给该端口发了些数据。结果只有“Sokit”调试工具收到了数据,而我自己的程序没有任何反应。这说明我自己的程序并没有真正在侦听该端口,可是明明是我的程序先于“Sokit”调试工具绑定了9530端口,而我自己的程序反而收到不数据。如何解释?

阅读全文

c10k

同时工作的进程数 历史上UNIX的进程ID是16位整数,也就是进程最多不超过32767。现代的Linux系统进程ID虽然是32位,但是系统仍然限制了大小。想要一个连接一个进程,必然会触发其上限。

阅读全文

IO 多路复用

https://gocn.vip/topics/10090 什么是 IO 多路复用 IO 多路复用解决什么问题 目前有哪些 IO 多路复用的方案 具体怎么用 不同 IO 多路复用方案优缺点

  1. 什么是 IO 多路复用 一句话解释:单线程或单进程同时监测若干个文件描述符是否可以执行 IO 操作的能力。
  2. 解决什么问题 说在前头 应用程序通常需要处理来自多条事件流中的事件,比如我现在用的电脑,需要同时处理键盘鼠标的输入、中断信号等等事件,再比如 web 服务器如 nginx,需要同时处理来来自 N 个客户端的事件。
阅读全文

linux的wc -l 命令统计文件少一行

wc(Word Count)命令的功能为统计指定文件中的字节数、字数、行数,并将统计结果显示输出

阅读全文

moesi CPU缓存知识

https://coolshell.cn/articles/20793.html 基础知识、缓存的命中、缓存的一致性、相关的代码示例和延伸阅读。其中会讲述一些多核 CPU 的系统架构以及其原理,包括对程序性能上的影响,以及在进行并发编程的时候需要注意到的一些问题。这篇文章我会尽量地写简单和通俗易懂一些,主要是讲清楚相关的原理和问题,而对于一些细节和延伸阅读我会在文章最后会给出相关的资源。

阅读全文

TCP 协议有粘包问题

https://draveness.me/whys-the-design-tcp-message-frame TCP/IP 协议簇建立了互联网中通信协议的概念模型,该协议簇中的两个主要协议就是 TCP 和 IP 协议。TCP/ IP 协议簇中的 TCP 协议能够保证数据段(Segment)的可靠性和顺序,有了可靠的传输层协议之后,应用层协议就可以直接使用 TCP 协议传输数据,不在需要关心数据段的丢失和重复问题1。 P 协议解决了数据包(Packet)的路由和传输,上层的 TCP 协议不再关注路由和寻址2,那么 TCP 协议解决的是传输的可靠性和顺序问题,上层不需要关心数据能否传输到目标进程,只要写入 TCP 协议的缓冲区的数据,协议栈几乎都能保证数据的送达。

阅读全文

代码执行的效率

要调优性需要找到程序中的Hotspot,也就是被调用最多的地方,这种地方,只要你能优化一点点,你的性能就会有质的提高。在这里我给大家举三个关于代码执行效率的例子 https://coolshell.cn/articles/7886.html 第一个例子 PHP中Getter和Setter的效率(来源reddit)

阅读全文

underlay overlay

sdn网络中underlay和overlay网络的区别:

阅读全文

seccomp

什么是seccomp seccomp(全称securecomputing mode)是linuxkernel从2.6.23版本开始所支持的一种安全机制。

阅读全文

ssh +trap 实现跳板机

运行Shell脚本时,如果按下快捷键Ctrl+c或Ctrl+x(x为其他字符),程序就会终止运行,

阅读全文

进程ID、父进程ID、进程组ID、会话和控制终端

进程基本属性

阅读全文

ps ef 和 -ef 区别

  1. pstree

    通过系统的进程树来查看某个进程的父进程;

  2. ps -ef |grep <进程名>

    在显示的输出中,第三列就是该进程的父进程PID,然后可以再使用ps命令来查看父进程的名称

    ps -ef |grep <父进程PID>

    man ps says:

阅读全文

信号的生命周期

当向目标进程发送一枚信号SIGXXX时,Linux内核收到了产生的信号,然后再目标进程的进程描述符里记录了一笔:收到了信号SIGXXX,但是还没有递送给目标进程的这一段时间里,信号处于挂起状态,被称为( pending )信号,也称为未决信号。内核将信号递送给进程,进程就会暂停当前的控制流,转而去执行信号处理函数,这就是一个信号的完整生命周期。

阅读全文

linux 的nohup & 和daemon 总结

我们在linux终端中运行程序,有希望不因为终端的退出或异常断开导致运行的程序退出的需求。而之所以终端退出会异常断开程序退出的原因是因为其会向终端中运行的程序发送SIGHUP信号。

阅读全文

router

192.168.1.1属于IP地址的C类地址,属于保留IP,专门用于路由器设置。一般来讲这个地址的密码根据路由器厂商的设置会有所不同,但一般会是:用户名(区分大小写):admin 密码:admin如果您已经修改了这个密码,请输入您修改后的密码。如果不是您修改的,可以重设路由器管理员来获取这个密码,或者重置路由器设置来恢复设备初始密码。 IP地址由两部分组成,即网络号和主机号。网络号标识的是internet上的一个网络,主机号标识的是网络中的某台主机。 IPv4的地址长度为32位,共4个字节,但实际中我们用点分十进制记法。 注:部分路由器的IP地址为192.168.0.0和192.168.0.1。 http://192-168-1-1.com.cn/ https://www.lifewire.com/192-168-0-1-818066 https://www.lifewire.com/what-is-a-broadband-router-816301

阅读全文

abi

每个操作系统都会为运行在该系统下的应用程序提供应用程序二进制接口(Application Binary Interface,ABI)。ABI包含了应用程序在这个系统下运行时必须遵守的编程约定。ABI总是包含一系列的系统调用和使用这些系统调用的方法,以及关于程序可以使用的内存地址和使用机器寄存器的规定。从一个应用程序的角度看,ABI既是系统架构的一部分也是硬件体系结构的重点,因此只要违反二者之一的条件约束就会导致程序出现严重错误。在很多情况下,链接器为了遵守ABI的约定需要做一些重要的工作。例如,ABI要求每个应用程序包含一个程序中各例程使用的静态数据的所有地址表,链接器通过收集所有链接到程序中的模块的地址信息来创建地址表。ABI经常影响链接器的是对标准过程调用的定义 ABI(Application Binary Interface):应用程序二进制接口,描述了应用程序和操作系统之间,一个应用和它的库之间,或者应用的组成部分之间的低接口。ABI涵盖了各种细节,如: 数据类型的大小、布局和对齐; 调用约定(控制着函数的参数如何传送以及如何接受返回值),例如,是所有的参数都通过栈传递,还是部分参数通过寄存器传递;哪个寄存器用于哪个函数参数;通过栈传递的第一个函数参数是最先push到栈上还是最后; 系统调用的编码和一个应用如何向操作系统进行系统调用; 以及在一个完整的操作系统ABI中,目标文件的二进制格式、程序库等等。 与应用程序接口区别编辑 应用程序接口(Application Programming Interface,API),又称为应用编程接口,就是软件系统不同组成部分衔接的约定。由于近年来软件的规模日益庞大,常常需要把复杂的系统划分成小的组成部分,编程接口的设计十分重要。程序设计的实践中,编程接口的设计首先要使软件系统的职责得到合理划分。良好的接口设计可以降低系统各部分的相互依赖,提高组成单元的内聚性,降低组成单元间的耦合程度,从而提高系统的维护性和扩展性。 ABI不同于API ,API定义了源代码和库之间的接口,因此同样的代码可以在支持这个API的任何系统中编译 ,然而ABI允许编译好的目标代码在使用兼容ABI的系统中无需改动就能运行。 ABI掩盖了各种细节,例如:调用约定控制着函数的参数如何传送以及如何接受返回值;系统调用的编码和一个应用如何向操作系统进行系统调用;以及在一个完整的操作系统ABI中,对象文件的二进制格式、程序库等等。一个完整的ABI,像 Intel二进制兼容标准 (iBCS) ,允许支持它的操作系统上的程序不经修改在其他支持此ABI的操作系统上运行。其他的 ABI 标准化细节包括C++ name decoration和同一个平台上的编译器之间的调用约定,但是不包括跨平台的兼容性。在Unix的操作系统中,存在很多运行在同一件平台上互相相关但是不兼容的操作系统(尤其是80386兼容系统)。有一些努力尝试标准化A I,以减少销售商将程序移植到其他系统时所需的工作。然而,还没有很成功的例子,虽然LSB正在为Linux做这方面的努力。 它描述了应用程序与OS之间的底层接口。ABI涉及了程序的各个方面,比如:目标文件格式、数据类型、数据对齐、函数调用约定以及函数如何传递参数、如何返回值、系统调用号、如何实现系统调用等。 一套完整的ABI(比如:Intel Binary Compatibility Standard (iBCS)),可以让程序在所有支持该ABI的系统上运行,而无需对程序进行修改。

  1. ABI 解决什么问题
阅读全文

线程池

0.首先什么是线程池?

阅读全文

多线程模式下全局变量竞争

优点:在一个进程内的所有线程共享全局变量,很方便在多个线程间共享数据

阅读全文

流水线冒险及解决方法

由于一段机器语言程序的邻近指令之间出现了某种关联后,为了避免出错而使得它们不能同时被解释的现象,又称相关冲突。

阅读全文

连接池

连接池是创建和管理一个连接的缓冲池的技术,这些连接准备好被任何需要它们的线程使用 这种连接“汇集”起来的技术基于这样的一个事实:对于大多数应用程序,当它们正在处理通常需要数毫秒完成的事务时,仅需要能够访问JDBC连接的 1 个线程。当不处理事务时,这个连接就会闲置。相反,连接池允许闲置的连接被其它需要的线程使用。 事实上,当一个线程需要用 JDBC 对一个 GBase 或其它数据库操作时,它从池中请求一个连接。当这个线程使用完了这个连接,将它返回到连接池中,这样这就可以被其它想使用它的线程使用。 当连接从池中“借出”,它被请求它的线程专有地使用。从编程的角度来看,这和用户的线程每当需要一个 JDBC 连接的时候调用DriverManager.getConnection() 是一样的,采用连接池技术,可通过使用新的或已有的连接结束线程。 连接池可以极大的改善用户的 Java 应用程序的性能,同时减少全部资源的使用。连接池主要的优点有: 减少连接创建时间 虽然与其它数据库相比 GBase 提供了较为快速连接功能,但是创建新的 JDBC 连接仍会招致网络和 JDBC 驱动的开销。如果这类连接是“循环”使用的,使用该方式这些花销就可避免。 简化的编程模式 当使用连接池时,每一个单独的线程能够像创建了一个自己的 JDBC 连接一样操作,允许用户直接使用JDBC编程技术。 受控的资源使用 如果用户不使用连接池,而是每当线程需要时创建一个新的连接,那么用户的应用程序的资源使用会产生非常大的浪费并且可能会导致高负载下的异常发生。 注意,每个连到 GBase 的连接在客户端和服务器端都有花销(内存,CPU,上下文切换等等)。每个连接均会对应用程序和 GBase 服务器的可用资源带来一定的限制。不管这些连接是否在做有用的工作,仍将使用这些资源中的相当一部分。 连接池能够使性能最大化,同时还能将资源利用控制在一定的水平之下,如果超过该水平,应用程序将崩溃而不仅仅是变慢。 在实际应用开发中,特别是在WEB应用系统中,如果JSP、Servlet或EJB使用JDBC直接访问数据库中的数据,每一次数据访问请求都必须经历建立数据库连接、打开数据库、存取数据和关闭数据库连接等步骤,而连接并打开数据库是一件既消耗资源又费时的工作,如果频繁发生这种数据库操作,系统的性能必然会急剧下降,甚至会导致系统崩溃。数据库连接池技术是解决这个问题最常用的方法,在许多应用程序服务器(例如:Weblogic,WebSphere,JBoss)中,基本都提供了这项技术,无需自己编程,但是,深入了解这项技术是非常必要的。 数据库连接池技术的思想非常简单,将数据库连接作为对象存储在一个Vector对象中,一旦数据库连接建立后,不同的数据库访问请求就可以共享这些连接,这样,通过复用这些已经建立的数据库连接,可以克服上述缺点,极大地节省系统资源和时间。 数据库连接池的主要操作如下: (1)建立数据库连接池对象(服务器启动)。 (2)按照事先指定的参数创建初始数量的数据库连接(即:空闲连接数)。 (3)对于一个数据库访问请求,直接从连接池中得到一个连接。如果数据库连接池对象中没有空闲的连接,且连接数没有达到最大(即:最大活跃连接数),创建一个新的数据库连接。 (4)存取数据库。 (5)关闭数据库,释放所有数据库连接(此时的关闭数据库连接,并非真正关闭,而是将其放入空闲队列中。如实际空闲连接数大于初始空闲连接数则释放连接)。 (6)释放数据库连接池对象(服务器停止、维护期间,释放数据库连接池对象,并释放所有连接)。 实现模式编辑 1、连接池模型 本文讨论的连接池包括一个连接池类(DBConnectionPool)和一个连接池管理类(DBConnetionPoolManager)。连接池类是对某一数据库所有连接的“缓冲池”,主要实现以下功能:①从连接池获取或创建可用连接;②使用完毕之后,把连接返还给连接池;③在系统关闭前,断开所有连接并释放连接占用的系统资源;④还能够处理无效连接(原来登记为可用的连接,由于某种原因不再可用,如超时,通讯问题),并能够限制连接池中的连接总数不低于某个预定值和不超过某个预定值。 连接池管理类是连接池类的外覆类(wrapper),符合单例模式,即系统中只能有一个连接池管理类的实例。其主要用于对多个连接池对象的管理,具有以下功能:①装载并注册特定数据库的JDBC驱动程序;②根据属性文件给定的信息,创建连接池对象;③为方便管理多个连接池对象,为每一个连接池对象取一个名字,实现连接池名字与其实例之间的映射;④跟踪客户使用连接情况,以便需要时关闭连接释放资源。连接池管理类的引入主要是为了方便对多个连接池的使用和管理,如系统需要连接不同的数据库,或连接相同的数据库但由于安全性问题,需要不同的用户使用不同的名称和密码。 2、连接池实现 下面给出连接池类和连接池管理类的主要属性及所要实现的基本接口: public class DBConnectionPool implements TimerListener{ private int checkedOut;//已被分配出去的连接数 private ArrayList freeConnections = new ArrayList();//容器,空闲池,根据//创建时间顺序存放已创建但尚未分配出去的连接 private int minConn;//连接池里连接的最小数量 private int maxConn;//连接池里允许存在的最大连接数 private String name;//为这个连接池取个名字,方便管理 private String password;//连接数据库时需要的密码 private String url;//所要创建连接的数据库的地址 private String user;//连接数据库时需要的用户名 public Timer timer;//定时器 public DBConnectionPool(String name, String URL, String user, String password, int maxConn)//公开的构造函数 public synchronized void freeConnection(Connection con) //使用完毕之后,//把连接返还给空闲池 public synchronized Connection getConnection(long timeout)//得到一个连接,//timeout是等待时间 public synchronized void release()//断开所有连接,释放占用的系统资源 private Connection newConnection()//新建一个数据库连接 public synchronized void TimerEvent() //定时器事件处理函数 } public class DBConnectionManager { static private DBConnectionManager instance;//连接池管理类的唯一实例 static private int clients;//客户数量 private ArrayList drivers = new ArrayList();//容器,存放数据库驱动程序 private HashMap pools = new HashMap ();//以name/value的形式存取连接池//对象的名字及连接池对象 static synchronized public DBConnectionManager getInstance()//如果唯一的//实例instance已经创建,直接返回这个实例;否则,调用私有构造函数,创//建连接池管理类的唯一实例 private DBConnectionManager()//私有构造函数,在其中调用初始化函数init() public void freeConnection(String name, Connection con)// 释放一个连接,//name是一个连接池对象的名字 public Connection getConnection(Stringname)//从名字为name的连接池对象//中得到一个连接 public Connection getConnection(Stringname, long time)//从名字为name //的连接池对象中取得一个连接,time是等待时间 public synchronized void release()//释放所有资源 private void createPools(Properties props)//根据属性文件提供的信息,创建//一个或多个连接池 private void init()//初始化连接池管理类的唯一实例,由私有构造函数调用 private void loadDrivers(Properties props)//装载数据库驱动程序 } 3、连接池使用 上面所实现的连接池在程序开发时如何应用到系统中呢?下面以Servlet为例说明连接池的使用。 Servlet的生命周期是:在开始建立servlet时,调用其初始化(init)方法。之后每个用户请求都导致一个调用前面建立的实例的service方法的线程。最后,当服务器决定卸载一个servlet时,它首先调用该servlet的 destroy方法。 根据servlet的特点,我们可以在初始化函数中生成连接池管理类的唯一实例(其中包括创建一个或多个连接池)。如: public void init() throws ServletException { connMgr = DBConnectionManager.getInstance(); } 然后就可以在service方法中通过连接池名称使用连接池,执行数据库操作。最后在destroy方法中释放占用的系统资源,如: public void destroy() { connMgr.release(); super.destroy(); }

阅读全文

Mutex/Semaphore/Spinlock

Mutex是一把钥匙,一个人拿了就可进入一个房间,出来的时候把钥匙交给队列的第一个。一般的用法是用于串行化对critical section代码的访问,保证这段代码不会被并行的运行。

阅读全文

IPC Inter-Process Communication,进程间通信

IPC是一组编程接口,让程序员能够协调不同的进程,使之能在一个操作系统里同时运行,并相互传递、交换信息。这使得一个程序能够在同一时间里处理许多用户的要求。因为即使只有一个用户发出要求,也可能导致一个操作系统中多个进程的运行,进程之间必须互相通话。

阅读全文

内存屏障原理分析

1 速度不对等 Cpu的速度比cpu之间的互联性能及cpu试图要访问的内存性能,都要快上几个数量级 现代处理器基本都是多核,并且每个cpu都有自己独立的cache,不同cpu共享主内存,然后不同cpu通过总线互联,cpu -> cache -> memory 访问速度成大数量级递减,cpu最快,cache慢一点,memory更慢。 2 MESI协议 cpu从内存中加载数据到自己的cache,当不同的cpu都加载了同样的内存数据的时候,并且对数据进行操作的时候,需要维护数据在不同的cache 中的一致性视图就需要MESI协议,cache里面的缓存行有四种状态分别是Modified,Exclusive,Shared,Invalid。协议在每一个缓存行中维护 一个两位的状态“tag”, 这个“tag”附着在缓存行的物理地址或者数据后 ,标识着缓存行的状态

阅读全文

linux最大进程数、最大打开文件数

ulimit 是一种 linux 系统的内键功能,它具有一套参数集,用于为由它生成的 shell 进程及其子进程的资源使用设置限制。本文将在后面的章节中详细说明 ulimit 的功能,使用以及它的影响,并以具体的例子来详细地阐述它在限制资源使用方面的影响。

阅读全文

内存池

从内核中的伙伴系统,页高速缓存系统,slab内存管理系统,常规内存高速缓存系统,到用户线性区管理,用户动态内存分配malloc/free,最终因时制宜选择自定义内存区管理策略,到底有哪些驱动力? 接下来我们来梳理一下 1.伙伴系统 伙伴系统是内核为解决外碎化问题引入的内存管理机制。在32位体系结构中,虚拟内存空间的第四个GB用来线性的映射物理内存开始的DMA和低端内存管理区。而内存管理的基本单位是页,一个页的大小为4kB。所谓的外碎化指的是多次申请多个页的内存并释放后,会导致内存中存在不间隔的无法集中利用的页,其基本单位仍然是页,只是没有办法找到连续的可用来分配的多个页框。为了应对这样的事情,伙伴系统应运而生。伙伴系统首先将内存分为11个不同的2指数个大小的内存对象集合,每个集合用双向链表表示。分配内存时从小到大选择第一个能够满足大小的内存对象(2的order指数个),在这个过程中,如果没有办法找到适配的块,则对于大块的内存需要分割,分割时候将剩下的2k-2order大小的内存区分别放入到k~order大小的内存对象集合中,如果发现伙伴(相邻的)中有空闲的内存块,则进行合并。释放内存块的时候同理。

阅读全文

rename

rename命令用字符串替换的方式批量改变文件名。

阅读全文

mmap的原理

很多文章分析了mmap的实现原理。从代码的逻辑来分析,总是觉没有把mmap后读写映射区域和普通的read/write联系起来。不得不产生疑问:

阅读全文

log

近日,有幸拜读《日志管理与分析权威指南》一书,本书由三位业界资深安全专家编著,从日志的基本概念开始,由浅入深讲述了整个日志生命周期的详细过程,从日志的概念、数据概念、人工分析日志、以及日志与合规的依从性、自动化分析日志引申到SIEM日志管理。而其一作者Anton A.Chuvakin博士是日志管理、SIEM和PCI DSS依从性领域公认的安全专家,他的博客www.securitywarrior.org是该领域中最受欢迎的博客之一 日志数据

阅读全文

LD_PRELOAD和dlopen选项RTLD_DEEPBIND之间的优先级

RTLD_DEEPBIND的优先级更高,在符号名发生冲突时,LD_PRELOAD的全局符号介入对于使用RTLD_DEEPBIND标志位dlopen开的动态连接库无效。 发现背景: 在使用《在应用程序中替换Linux中Glibc的malloc的四种方法》一文中第3个方法接管程序内存时发现已接管内存总量比top命令看到的虚拟内存值小很多。于是查看/proc/[pid]/smaps查看虚拟内存页的使用情况(pmap命令也可以)。在进程中使用gdb查看各个内存页的首地址,转换为内存管理结构体发现,有很多内存没有接管,导致统计结果要比top少。 挑选了一个未接管的内存,通过 gdb 的dump binary memory 的命令存为磁盘文件,在通过UltralEdit等二进制编辑器大致浏览一下,根据内存包含的文本判断该内存是由某个动态链接库申请和使用的。而该链接库恰恰是通过dlopendlopen(strLibName,RTLD_NOW|RTLD_LOCAL|RTLD_DEEPBIND)方式打开的。 RTLD_DEEPBIND (since glibc 2.3.4) Place the lookup scope of the symbols in this library ahead of the global scope. This means that a self-contained library will use its own symbols in preference to global symbols with the same name contained in libraries that have already been loaded. This flag is not specified in POSIX.1-2001.

阅读全文

linux 下调试coredump文件

1、coredump简介

阅读全文

SATA硬盘和SSD硬盘性能测试对比

测试工具: fio

阅读全文

OOM原理分析

OOM全称是Out Of Memory,指的是kernel因分配不出内存而报的错误,同时会触发kernel调用OOM killer杀进程来解除这种状况。

阅读全文

检测内存泄露

1、win下的内存泄露检测方法:_CrtDumpMemoryLeaks

阅读全文

双缓冲(Double Buffer)原理和使用

一、双缓冲作用

阅读全文

文件锁

1.文件锁可以对将要修改文件的某个部分进行加锁,精确控制到字节

阅读全文

xargs

xargs 可以将 stdin 中以空格或换行符进行分隔的数据,形成以空格分隔的参数(arguments),传递给其他命令。因为以空格作为分隔符,所以有一些文件名或者其他意义的名词内含有空格的时候,xargs 可能会误判。简单来说,xargs 是给其他命令传递参数的一个过滤器,是构建单行命令的重要组件之一。

阅读全文

UNIX域套接字传递描述符的应用

 传送文件描述符是高并发网络服务编程的一种常见实现方式。Nebula 高性能通用网络框架即采用了UNIX域套接字传递文件描述符设计和实现  https://github.com/Bwar/Nebula 在Unix 域套接字概述一节中介绍了什么是 Unix 及相关函数,本文将继续介绍 Unix 域套接字在进程间传递描述符的应用。 在进程间传递打开的描述符时通常会采用如下两种方法: (1)fork 调用返回后,子进程自动共享父进程的所有打开的描述符。 (2)exec 调用执行后,所有描述符通常保持打开状态不变。 第一种方法中,进程先打开一个描述符,再调用 fork,之后父进程关闭这个描述符,子进程则处理该描述符。这样一个打开的描述符就从父进程传递到子进程。不过有时候可能想让子进程打开一个描述符并把他传递给父进程。 使用 Unix 域套接字,可以从一个进程向任一其他进程传递打开的描述符,而无需这两个进程之间存在亲缘关系。这种技术要求首先在这两个进程之间创建一个 Unix 域套接字,然后使用 sendmsg 跨这个套接字发送一个特殊消息。该消息由内核来专门处理,会把打开的描述符从发送进程传递到接收进程。 使用 Unix 域套接字在两个进程之间传递描述符涉及的步骤如下。 (1)创建一个字节流或数据报的 Unix 域套接字。如果目标是让子进程将打开的描述符传递回父进程,则父进程可以预先调用 socketpair 函数创建一个可用于在父子进程之间交换描述符的流管道。如果进程间没有亲缘关系,则服务器进程必须先创建一个 Unix 域字节流套接字(也可以是 Unix 域数据报套接字,不过这没什么好处,而且数据报还存在被丢弃的可能性),然后 bind 一个路径名到该套接字,以允许客户进程 connect 到套接字,发送一个打开某个描述符的请求。 (2)发送进程通过调用返回描述符的任一 Unix 函数(如 open、pipe、mkfifo、socket 和 accept,可以在进程之间传递的描述符不限类型)打开一个描述符。 (3)发送进程创建一个 msghdr 结构,其中含有待传递的描述符。POSIX 规定描述符作为辅助数据(msghdr 结构的 msg_control 成员,见辅助数据)发送。发送进程调用 sendmsg 跨来自步骤 1 的 Unix 域套接字发送该描述符。至此,称这个描述符“在飞行中(in flight)”。即使发送进程在调用 sendmsg 之后但在接收进程调用 recvmsg 之前关闭了该描述符,对于接收进程它仍然保持打开状态。发送一个描述符会使该描述符的引用计数加一。 (4)接收进程调用 recvmsg 在来自步骤 1 的 Unix 域套接字上接收这个描述符。这个描述符在接收进程中的描述符号不同于它在发送进程中的描述符号是正常的,因为传递一个描述符并不是传递一个描述符号,而是涉及在接收进程中创建一个新的描述符,该描述符和发送进程中飞行前的那个描述符指向内核中相同的文件表项。

    参考文献:1) 《Unix网络编程》
    2)  http://book.51cto.com/art/200912/168560.htm

最近学习了使用Unix域套接字在进程间传递文件描述符,仿照参考资料,自己也写了简单的程序来实践这种技术。

其他不多说了,具体理论知识参见参考资料,开始我自己的程序介绍(在OpenSolaris 2009.06平台上测试):

1 程序作用说明:父进程,子进程以及另外一个进程向同一个文件的文件描述符向这个文件中写内容。 具体如下: 1)父进程指定要打开的文件名,打开权限,打开模式; 2)fork一个子进程; 3)子进程调用execl函数来执行程序openfile:该新程序取得指定文件的文件描述符;向指定文件中写入“openfileprog write test”;向父进程返回该文件描述符; 4)父进程收到该文件描述符后,向文件中写“paraent process write ppp”; 5)父进程作为server端建立域socket等待客户端进程连接; 6)客户端进程连接父进程; 7)父进程向该客户端进程返回从子进程得到的文件描述符; 8)客户端进程收到该文件描述符后使用它在文件中写“this is client process ccc”;

其中父子进程传递文件描述符通过建立的一对套接字管道,父进程和客户端进程传递文件描述符通过Unix域套接字。

2 具体代码说明:

1) 首先看openfile程序,这时子进程通过调用execl执行的。调用方法如下: execl(“./openfileprog”, “openfileprog”, permit, mode, argsockfd, (char *)NULL); 其中参数1: openfile程序路径; 参数2: openfile程序名; 参数3: 待打开文件的权限; 参数4: 待打开文件模式; 参数5: 父进程建立的一对套接字管道的其中之一;

作为openfile程序,主要按照execl传的参数,打开指定文件,取得文件描述符;向该文件中写入内容;然后调用func_send_fd函数通过argsockfd把取得的文件描述符传给父进程。该程序代码如下:

int main(int argc, char argv[]) / openfileprog / { int i, fd, ret; ssize_t size; size_t buflen; char data[10]; char buf[] = “openfileprog write test\n”; / 向文件中写入的内容 / / execl(“./openfileprog”, permit, mode, argsockfd, (char *)NULL); */ fd = -1; if((fd = open(“./file”, atoi(argv[1]), atoi(argv[2]))) < 0) { printf(“in openfileprog, open failed\n”); exit(-1); }

size = -1; buflen = sizeof(buf); if((size = write(fd, buf, buflen)) <= 0) { printf(“in openfileprog, write failed\n”); }

/* 把设定的data信息也传给父进程 */ ret = ‘a’; for(i = 0; i < sizeof(data); i++, ret++) { data[i] = ret; } data[sizeof(data) - 1] = ‘\0’;

ret = -1; if(0 > (ret = func_send_fd(atoi(argv[3]), fd, data, 10))) { printf(“in openfileprog, func_send_fd failed\n”); }

close(fd);

return 0; }

func_send_fd函数负责把取得的文件描述符传出去:

int func_send_fd(int send_sock, int send_fd, void *data, int bytes) { struct msghdr msghead; struct iovec passdata[1]; int ret;

/* 填充msghead结构 */ msghead.msg_accrights = (caddr_t)&send_fd; msghead.msg_accrightslen = sizeof(send_fd);

msghead.msg_name = NULL; msghead.msg_namelen = 0; passdata[0].iov_base = data; passdata[0].iov_len = bytes;

msghead.msg_iov = passdata; msghead.msg_iovlen = 1;

/* 发送信息 */ if(0 > (ret = sendmsg(send_sock, &msghead, 0))) { printf(“in func_send, send_fd is %d, sendsock is %d, sendmsg failed,errno is %d\n”, send_fd,send_sock,errno); return -1; }

return ret; }

在上述两个函数之前,加上以下必要头文件和宏:

#include #include #include #include #include #include #include #define SLEEPTIME 3 #define ARGLEN 20

以上作为一个c文件。

2)然后看父进程代码

下面是父进程程序:

int main(int argc, char *argv) { int status,sockfd[2]; char permit[ARGLEN]; char mode[ARGLEN]; char argsockfd[ARGLEN]; int recvfd; char data[20]; int bytes; int ret,i; ssize_t size; int buflen; pid_t pid,chldpid;

/* 以下几行是使用域套接字必要变量 */ int fdsock, fdaccept; struct sockaddr_un addr_server;
int len;
const char path[] = “/export/home/temp/test/other_prog/fengxianzhong”;

/* 以下是父进程写入文件的内容 */
char buf[] = “paraent process write ppp\n”;

/* 父进程同时向处理向client发送的数据 */ char datasend[] = “send by myopen\n”;

memset(permit, ‘\0’, sizeof(permit)); memset(mode, ‘\0’, sizeof(mode)); memset(argsockfd, ‘\0’, sizeof(argsockfd)); memset(data, ‘\0’, sizeof(data));

printf(“now it is parent process,now will fork a child process\n”); sleep(SLEEPTIME);

/* 设置文件权限和打开模式 */ snprintf(permit, sizeof(permit), “%d”,PERMIT); snprintf(mode, sizeof(mode), “%d”,MODE); // printf(“in myopen %s, %s\n”, permit, mode);

/* 建立和子进程通信的socket套接字管道 */ ret = socketpair(AF_UNIX,SOCK_STREAM,0,sockfd); if(0 > ret) { printf(“socketpair failed,errno is %d \n”,errno); }

/* fork 子进程 / if(0 == (chldpid = fork())) / child process */ { printf(“now it is child process, sendsock is %d\n”,sockfd[1]); close(sockfd[0]); snprintf(argsockfd, sizeof(argsockfd), “%d”, sockfd[1]);

/* 子进程中执行新程序openfile */ execl(“./openfileprog”, “openfileprog”, permit, mode, argsockfd, (char *)NULL); printf(“execl failed, perimit is %s, mode is %s\n”,permit, mode); exit(-1); }

/* paraent process start to write the file opened by child process */

printf(“now it is parent process\n”); close(sockfd[1]); bytes = sizeof(data);

/* 等待子进程结束 / pid = wait(&status); if((status = WEXITSTATUS(status)) == 0) / child process terminate */ { printf(“child %d process terminate,now parent will write file …\n”,pid); }

/* 从子进程取得文件描述符 / recvfd = -1; // printf(“recv sock is %d\n”, sockfd[0]); ret = func_recv_fd(sockfd[0], &recvfd, data, bytes); if(ret < 0) { printf(“paraent recv failed\n”); } / else { printf(“fd %d paraent recv %d bytes data is %s\n”, recvfd,strlen(data),data); } */

/* 向文件写入数据 */ size = -1; buflen = sizeof(buf);

if((size = write(recvfd, buf, buflen)) <= 0) { printf(“in openfileprog, write failed\n”); }

/* 父进程作为server建立域套接字,等待client连接 */ printf(“parent write over! Accept other process ……\n”);

fdsock = socket(AF_UNIX, SOCK_STREAM, 0); if(-1 == fdsock) { printf(“myopen creat socket error!errno is %d\n”, errno); }

unlink(path);

memset(&addr_server, 0, sizeof(addr_server)); addr_server.sun_family = AF_UNIX; strcpy(addr_server.sun_path, path); len = sizeof(struct sockaddr_un);

ret = bind(fdsock, (struct sockaddr*)&addr_server, len); if(-1 == ret) { printf(“in myopen bind error, errorno is %d\n”,errno); close(fdsock); unlink(path); }

ret = listen(fdsock,1); if(-1 == ret) { printf(“in myopen listen error, errorno is %d\n”,errno); close(fdsock); unlink(path); }

fdaccept = accept(fdsock, (struct sockaddr*)&addr_server, &len); if(-1 == ret) { printf(“in myopen accept error, errorno is %d\n”,errno); close(fdsock); unlink(path); }

/* 向已经连接的client传递该文件描述符 */ ret = func_send_fd(fdaccept, recvfd, datasend, sizeof(datasend)); if(0 > ret) { printf(“in myopen, func_send_fd failed\n”); }

printf(“send fd over! Will sleep 10s \n”);

sleep(10);

exit(0);

}

func_recv_fd函数负责从子进程接受文件描述符:

int func_recv_fd(int recv_sock, int *recvfd, void *data, int bytes) { struct msghdr msghead; struct iovec passdata[1]; int ret; int temp; int newfd;

struct cmsghdr *msgptr1;

struct cmsghdr *msgptr = NULL;

memset(&msghead, 0, sizeof(msghead));

/* 同func_send_fd ,填充所需要的结构 */ msghead.msg_accrights = (caddr_t)&newfd; msghead.msg_accrightslen = sizeof(recvfd);

msghead.msg_name = NULL; msghead.msg_namelen = 0; passdata[0].iov_base = data; passdata[0].iov_len = bytes;

msghead.msg_iov = passdata; msghead.msg_iovlen = 1;

/* 接收信息(文件描述符 )*/ if(0 > (ret = recvmsg(recv_sock, &msghead, 0))) { printf(“in func_recv_fd, recvmsg failed\n”); return -1; }

if(msghead.msg_accrightslen == sizeof(recvfd)) { recvfd = newfd; / 文件描述符 */ }

return ret; }

其中父进程向client进程发送文件描述符也使用了func_send_fd函数,该函数在此c文件中重新写了一遍;其实没有必要这样重复。我们可以把它作为一个库来使用;不过这里暂且这样使用。函数代码参考上面所写的。

在这个c文件中还要加入以下头文件和宏定义:

#include #include #include #include #include #include #include #include #include #include #define SLEEPTIME 3 #define ARGLEN 20 #define MODE S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IROTH /* -rwxr–r– / #define PERMIT O_RDWR | O_APPEND | O_CREAT / if the file not exit ,creat it , data written to it append */

3)最后看client进程代码:

int main() { int sockfd, recvfd,ret;

struct sockaddr_un addr_client;
int length,buflen; char data[10]; ssize_t size; const char path[] = “/export/home/temp/test/other_prog/fengxianzhong”;
char buf[] = “this is client process ccc\n” ;

sockfd = socket(AF_UNIX, SOCK_STREAM, 0); if(-1 == sockfd) { printf(“client creat socket error!errno is %d\n”, errno); }

addr_client.sun_family = AF_UNIX; strcpy(addr_client.sun_path, path); length = sizeof(addr_client.sun_family) + sizeof(addr_client.sun_path);

阅读全文

进程中线程同步的四种方式

三种方法:

阅读全文

206 http 1.1断点续传

要实现断点续传的功能,通常都需要客户端记录下当前的下载进度,并在需要续传的时候通知服务端本次需要下载的内容片段。

阅读全文

uniq

Linux uniq 命令用于检查及删除文本文件中重复出现的行列,一般与 sort 命令结合使用。

阅读全文

sort

sort是在Linux里非常常用的一个命令,管排序的,集中精力,五分钟搞定sort,现在开始!

阅读全文

nginx 日志访问量统计

1.根据访问IP统计UV

阅读全文

httpCache

HTTP Caching 用好了,可以极大的减小服务器负载和减少网络带宽。十分有必要深入了解下 http 的 caching 协议。

阅读全文

tcp errno

EAGAIN、EWOULDBLOCK、EINTR与非阻塞 长连接 EWOULDBLOCK用于非阻塞模式,不需要重新读或者写

阅读全文

systemctl init.d

docker容器不支持 systemctl

阅读全文

Connect reset by peer

1,如果一端的Socket被关闭(或主动关闭,或因为异常退出而 引起的关闭),另一端仍发送数据,发送的第一个数据包引发该异常(Connect reset by peer)。

阅读全文

Connection refused

TCP/IP:连接服务器失败(错误原因:Connection refused) Linux中,通过系统调用(system call) connect 连接指定服务器建立TCP连接。

阅读全文

Failed to get D-Bus connection

I’m trying to install ambari 2.6 on a docker centos7 image but in the the ambari setup step and exactly while intializing the postgresql db I receive this error:

阅读全文

dbus

有那么个组织叫freedesktop,它是专门为linux桌面制定标准的。什么KDE,GNOME都是按他的标准来的。而dbus是其中的桌面消息机制的一个标准。

阅读全文

dapper

随着微服务架构的流行,服务按照不同的维度进行拆分,一次请求往往需要涉及到多个服务。互联网应用构建在不同的软件模块集上,这些软件模块,有可能是由不同的团队开发、可能使用不同的编程语言来实现、有可能布在了几千台服务器,横跨多个不同的数据中心。因此,就需要一些可以帮助理解系统行为、用于分析性能问题的工具,以便发生故障的时候,能够快速定位和解决问题。 全链路监控组件就在这样的问题背景下产生了。最出名的是谷歌公开的论文提到的 Google Dapper。想要在这个上下文中理解分布式系统的行为,就需要监控那些横跨了不同的应用、不同的服务器之间的关联动作。 所以,在复杂的微服务架构系统中,几乎每一个前端请求都会形成一个复杂的分布式服务调用链路。一个请求完整调用链可能如下图所示:

阅读全文

Broken Pipe

写了一个server和一个client,UNIX套接字的,server不断接收消息并打印出来,client是一个交互程序,输入一个消息回车发送,接着又可以输入消息。 出问题了: 当server监听着,client第一次发送消息成功,server接收并打印出来了。 client第二次发送消息没成功并且结束程序了,server没接收到消息,保持继续监听。 我用GDB调试时,发现client第二次发送消息时,client收到SIGPIPE(Broken Pipe)信号。server明明还监听着,而且再次启动client还是第一次成功,第二次失败退出。

阅读全文

rst

https://zhangbinalan.gitbooks.io/protocol/content/tcpde_rst.html 一、RST介绍 RST标示复位、用来异常的关闭连接。 1. 发送RST包关闭连接时,不必等缓冲区的包都发出去,直接就丢弃缓冲区中的包,发送RST。 2. 而接收端收到RST包后,也不必发送ACK包来确认。 二、什么时候发送RST包

阅读全文

full nat Session的Hash表

那么为什么LVS是在第四层做负载均衡?

阅读全文

SWRR 算法

Smooth Weighted Round-Robin (SWRR) 是 nginx 默认的加权负载均衡算法,它的重要特点是平滑,避免低权重的节点长时间处于空闲状态,因此被称为平滑加权轮询。

阅读全文

LVS

https://github.com/alibaba/LVS

阅读全文

tcp_tw_reuse

linux TIME_WAIT 相关参数:

阅读全文

tcp_tw_recycle

Linux 只能收到 SYN 包 不能回包 问题 如果用户发现云主机不能登录,例如无法远程 22 端口或其他端口,但是更换网络环境正常,服务端抓包发现客户端发包只有 SYN,没有回包,可以执行 netstat -s |grep rejec 查看下是否是 tcp_timestamps 的问题 [root@hfgo2 ~]# netstat -s |grep rejec 8316 passive connections rejected because of time stamp 780 packets rejects in established connections because of timestamp 如果出现很多数据包的 timestamp 被拒绝,则检查下内核参数 tcp_tw_recycle 是否开启,如果开启,将其关闭即可。 [root@hfgo2 ~]# cat /proc/sys/net/ipv4/tcp_tw_recycle 原因 这个主意是和内核的 2 个参数相关

阅读全文

tcp_retries

https://pracucci.com/linux-tcp-rto-min-max-and-tcp-retries2.html TCP retransmits an unacknowledged packet up to tcp_retries2 sysctl setting times (defaults to 15) using an exponential backoff timeout for which each retransmission timeout is between TCP_RTO_MIN (200 ms) and TCP_RTO_MAX (120 seconds). Once the 15th retry expires (by default), the TCP stack will notify the layers above (ie. app) of a broken connection.

阅读全文

MTU、MSS、MSL、RTT、TTL、RTO

MTU Maximum Transfer Unit 最大传输单元 链路层的帧(frame)中的数据部分的最大字节数 以太网中的一般为1500字节 MSS Maximum Segment Size 最大报文段大小 TCP的报文段中的数据部分的最大字节数,MTU减去IPv4的Header和TCP的Header IPv4的Header和TCP的Header一般都是20字节,则MSS=1500-20-20 = 1460字节 MSL Maximum Segment Lifetime 报文最大生存时间 报文在网络上存在的最长时间,TCP四次挥手是主动断开连接的一方再发送完最后一个ACK后进入TIME_WAIT状态时,需要等待2MSL时间后才变成CLOSED状态 RFC 793建议为2分钟 RTT Round-Trip Time 从发送端发送数据开始,到发送端收到来自接收端的确认(接收端收到数据后便立即发送确认),总共经历的时延 TCP中保留了RTT的加权平均值RTTS(下标S表示Smoothed) 对于i=1,RTTS[i]=新RTT样本 对于i>1,RTTS[i]=(1-a) * RTTS[i-1] + a * 新RTT样本,RFC2988建议a=1/8 TTL Time To Live 该字段指定IP包被路由器丢弃之前允许通过的最大网段数量。TTL是IPv4包头的一个8 bit字段。 RTO Retransmission Timeout 超时重传时间 TCP中触发超时重传机制的时间,应略大于RTT RFC2988中建议RTO = RTTS + 4 * RTTD RTTD时RTT的偏差的加权平均值 对于i=1,RTTD[i] = 新RTT样本/2 对于i>1,RTTD[i] = (1 - b) * RTTD[i-1] + b * | 新RTT样本 - RTTD[i] |,建议b=1/4 net.ipv4.tcp_timestamps 是linux的内核参数,用来控制是否开启tcp时间戳,开启后会吧发送端的内核时间记录在TCP包头。

阅读全文

gateway

网关即Gateway,它是连接基于不同通信协议的网络的设备,使文件可以在这些网络之间传输。网关除传输信息外,还将这些信息转化为接收网络所用协议认可的形式。它同路由器有密切关系。

阅读全文

ip netns

ip netns 命令用来管理 network namespace。它可以创建命名的 network namespace,然后通过名字来引用 network namespace,所以使用起来很方便。

阅读全文

netfilter iptables

https://www.netfilter.org/ https://arthurchiao.github.io/blog/deep-dive-into-iptables-and-netfilter-arch-zh/ https://www.digitalocean.com/community/tutorials/a-deep-dive-into-iptables-and-netfilter-architecture

https://arthurchiao.github.io/blog/nat-zh/

防火墙是保护服务器和基础设施安全的重要工具。在 Linux 生态系统中,iptables 是使 用很广泛的防火墙工具之一,它基于内核的包过滤框架(packet filtering framework) netfilter。如果管理员或用户不了解这些系统的架构,那可能就无法创建出可靠的防火 墙策略,一方面是因为 iptables 的语法颇有挑战性,另外一方面是 netfilter 框架内部 相互交织而变得错综复杂。
阅读全文

tcp

作者:灵剑 链接:https://www.zhihu.com/question/51438786/answer/125920692 来源:知乎 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

阅读全文

resolv.conf

resolv.conf配置DNS客户,它包含了主机的域名搜索顺序和DNS服务器的地址,每一行应包含一个关键字和一个或多个的由空格隔开的参数。 cat /etc/resolv.conf

Generated by NetworkManager

domain localdomain
search localdomain
nameserver xxx.xxx.xxx.xxx
nameserver 表明DNS服务器的IP地址。可以有很多行的nameserver,每一个带一个IP地址。在查询时就按nameserver在本文件中的顺序进行,且只有当第一个nameserver没有反应时才查询下面的nameserver。

阅读全文

pkgconfig

用第三方库,就少不了要使用到第三方的头文件和库文件。我们在编译、链接的时候,必须要指定这些头文件和库文件的位置。

阅读全文

hook glibc

Linux的用C库的都是glibc,有一个叫libc.so.6的文件,这是几乎所有Linux下命令的动态链接中,其中有标准C的各种函数,默认情况下,linux所编译的程序中对标准C函数的链接,都是通过动态链接方式来链接libc.so.6这个函数库的。这也意味着我们在通过我们注入的.so来实现函数覆盖劫持之后需要从libc.so.6中取得原本的正常函数,让程序继续正常执行 #include #include

阅读全文

dlsym

加载动态链接库——dlopen dlsym dlclose NAME dlclose, dlopen, dlmopen - 打开/关闭共享对象

阅读全文

DYLD_INTERPOSE

https://opensource.apple.com/source/dyld/dyld-353.2.1/include/mach-o/dyld-interposing.h // 演示代码 // #import <mach-o/dyld-interposing.h> // from dyld-interposing.h #define DYLD_INTERPOSE(replacement,_replacee) attribute((used)) static struct{ const void* replacement; const void* replacee; } _interpose##_replacee attribute ((section (“__DATA,__interpose”))) = { (const void)(unsigned long)&_replacement, (const void)(unsigned long)&_replacee };

阅读全文

DYLD_FORCE_FLAT_NAMESPACE

1、gcc生成dylib。

阅读全文

LD_LIBRARY_PATH

PATH: 可执行程序的查找路径

阅读全文

set

set -x 与 set +x 在liunx脚本中可用set -x就可有详细的日志输出.免的老是要echo了

阅读全文

perf

Perf 是用来进行软件性能分析的工具。

阅读全文

pstree

pstree命令是用于查看进程树之间的关系,即哪个进程是父进程,哪个是子进程,可以清楚的看出来是谁创建了谁 #pstree 几个重要的参数: -A: 各进程树之间的连接以ASCII码字符来连接 -U:各进程树之间的连接以utf8字符来连接,某些终端可能会有错误 -p:同时列出每个进程的PID -u: 同时列出每个进程的所属账号名称: 例子: #pstree -up systemd(1)-+-agetty(2021) |-agetty(2022) |-bash(23254,ffy) |-chronyd(1180,chrony) |-crond(1277)—crond(25734)—sogou-agent(25736)—sleep(25759) |-dbus-daemon(1123,dbus) |-python(25707,dlj)-+-python(25778)-+-{python}(25781) | | |-{python}(25783) | | |-{python}(25784) | | |-{python}(27547) | | -{python}(27548) | |-python(25779)-+-{python}(25785) | | |-{python}(25786) | | -{python}(25788) | |-python(25780)-+-{python}(27549) | | |-{python}(27550) | | |-{python}(27551) | | |-{python}(27552) | | |-{python}(27553) | | |-{python}(27554) | | -{python}(27555) | |-python(25782)-+-{python}(29319) | | |-{python}(29320) | | |-{python}(29321) | | |-{python}(29322) | | |-{python}(29323) | | |-{python}(29324) | | -{python}(29325) | `-python(25787)
pstree命令以树状图显示进程间的关系(display a tree of processes)。ps命令可以显示当前正在运行的那些进程的信息,但是对于它们之间的关系却显示得不够清晰。在Linux系统中,系统调用fork可以创建子进程,通过子shell也可以创建子进程,Linux系统中进程之间的关系天生就是一棵树,树的根就是进程PID为1的init进程。 以树状图只显示进程的名字,且相同进程合并显示: 格式:pstree

阅读全文

chroot

chroot命令用来在指定的根目录下运行指令。chroot,即 change root directory (更改 root 目录)。在 linux 系统中,系统默认的目录结构都是以/,即是以根 (root) 开始的。而在使用 chroot 之后,系统的目录结构将以指定的位置作为/位置。

阅读全文

netns

主要是三个系统调用

阅读全文

virtual(虚函数) vtbl(虚函数表)与vptr(虚函数表指针)

虚 函数(Virtual Function) 虚函数表(Virtual Table)vtbl vptr(虚函数表指针) virtual pointer 类的虚函数表是一块连续的内存,每个内存单元中记录一个JMP指令的地址 注意的是,编译器会为每个有虚函数的类创建一个虚函数表,该虚函数表将被该类的所有对象共享。类的每个虚成员占据虚函数表中的一行。如果类中有N个虚函数,那么其虚函数表将有N*4字节的大小。 虚 函数(Virtual Function)是通过一张虚函数表(Virtual Table)来实现的。简称为V-Table。在这个表中,主要是一个类 的虚函数的地址表,这张表解决了继承、覆盖的问题,保证其真实反应实际的函数。这样,在有虚函数的类的实例中这个表被分配在了这个实例的内存中,所以,当 用父类的指针来操作一个子类的时候,这张虚函数表就显得由为重要了,它就像一个地图一样,指明了实际所应该调用的函数。 编译器应该是保证虚函数表的指针存在于对象实例中最前面的位置(这是为了保证取到虚函数表的有最高的性能——如果有多层继承或是多重继承的情况下)。 这意味着可以通过对象实例的地址得到这张虚函数表,然后就可以遍历其中函数指针,并调用相应的函数。 在方法定义时加上virtual,表示此方法是虚拟方法,可供子类覆盖,修改父类的执行

阅读全文

sbrk brk break linux-malloc底层实现原理

很明显是32位系统,寻址空间是4G,linux系统下0-3G是用户模式,3-4G是内核模式。而在用户模式下又分为代码段、数据段、.bss段、堆、栈。各个segment所含内容在图中有具体说明。

阅读全文

文件描述符与文件指针的区别

C语言中使用文件指针做为I/O的句柄。文件指针指向进程用户区中的一个被称为FILE结构的数据结构。FILE结构包括缓冲区和文件描述符。而文件描述符是文件描述符表的一个索引,也就是说c语言的文件指针是Linux系统中对文件描述符的一种封装。
FILE结构体

阅读全文

malloc()与 alloc()区别

C语言跟内存分配方式 (1) 从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,static变量。 (2) 在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。 (3)从堆上分配,亦称动态内存分配。程序在运行的时候用malloc或new申请任意多少的内存,程序员自己负责在何时用free或delete释放内存。动态内存的生存期由我们决定,使用非常灵活,但问题也最多 C语言跟内存申请相关的函数主要有 alloca,calloc,malloc,free,realloc,sbrk等. alloca是向栈申请内存,因此无需释放。 malloc分配的内存是位于堆中的,并且没有初始化内存的内容,因此基本上malloc之后,调用函数memset来初始化这部分的内存空间,需要用Free方式释放空间. calloc则将初始化malloc这部分的内存,设置为0. realloc则对malloc申请的内存进行大小的调整. free将malloc申请的内存最终需要通过该函数进行释放. sbrk则是增加数据段的大小;

阅读全文

alloca

内存分配函数,与malloc,calloc,realloc类似. 但是注意一个重要的区别,_alloca是在栈(stack)上申请空间,该变量离开其作用域之后被自动释放,无需手动调用释放函数。 包含在头文件malloc.h中。 在某些系统中会宏定义成_alloca使用。 在调用 alloca的函数返回的时候, 它分配的内存会自动释放。 也就是说, 用 alloca 分配的内存在栈上。 alloca不具可移植性, 而且在没有传统堆栈的机器上很难实现。 当它的返回值直接传入另一个函数时会带来问题,因为他分配在栈上. 由于这些原因, alloca不宜使用在必须广泛移植的程序中, 不管它可能多么有用。 既然 C99 支持变长数组(VLA), 它可以用来更好的 完成 alloca() 以前的任务。

阅读全文

tcmalloc原理剖析

tcmalloc是google开发的一个专门为高并发场景优化的内存分配器,全称为”thread cache malloc”。按照官网的介绍,tcmalloc相比于glibc2.3的malloc(底层实现为ptmalloc2)主要有以下优点:

阅读全文

malloc

TCMalloc的全称为Thread-Caching Malloc,是谷歌开发的开源工具google-perftools中的一个成员。与标准的glibc库的Malloc相比,TCMalloc库在内存分配效率和速度上要高很多,这在很大程度上提高了服务器在高并发情况下的性能,从而降低了系统的负载。

阅读全文

slab/slob/slub的区别

一个叫做Mark Hemment的哥儿们写了Slab。在接下来的一些年里,其他人对Slab进行了完善。一年半以前,SLOB问世了。SLOB的目标是针对嵌入式系统的,主要是适用于那些内存非常有限的系统,比如32MB以下的内存,它不太注重large smp系统,虽然最近在这方面有一些小的改进。几个月之前,SLUB闪亮登场。它基本上属于对Slab的重设计(redesign),但是代码更少,并且能更好的适应large NUMA系统。SLUB被很认为是Slab和Slob的取代者,大概在2.6.24/2.6.25将会被同志们抛弃。而SLUB将是未来Linux Kernel中的首选 本来Linux内核只有Slab的,现在好了,Slab多了两个兄弟:Slob和Slub 简单的说:Slab是基础,是最早从Sun OS那引进的;Slub是在Slab上进行的改进,在大型机上表现出色(不知道在普通PC上如何),据说还被IA-64作为默认;而Slob是针对小型系统设计的,当然了,主要是嵌入式。相关文章如下:

阅读全文

进程的页表

页表是一种特殊的数据结构,放在系统空间的页表区,存放逻辑页与物理页帧的对应关系。 每一个进程都拥有一个自己的页表,PCB表中有指针指向页表。 逻辑地址:CPU所生成的地址。CPU产生的逻辑地址被分为 :p (页号) 它包含每个页在物理内存中的基址, 地址转化 地址转化 用来作为页表的索引;d (页偏移),同基址相结合,用来确定送入内存设备的物理内存地址。 物理地址:内存单元所看到的地址。逻辑地址空间为2^m,且页大小为2^n,那么逻辑地址的高m-n位表示页号,低n位表示页偏移。 逻辑地址空间:由程序所生成的所有逻辑地址的集合。 物理地址空间:与逻辑地址相对应的内存中所有物理地址的集合,用户程序看不见真正的物理地址。 注:用户只生成逻辑地址,且认为进程的地址空间为0到max。物理地址范围从R+0到R+max,R为基地址,地址映射-将程序地址空间中使用的逻辑地址变换成内存中的物理地址的过程。由内存管理单元(MMU)来完成。 分页逻辑地址 =P(页号).d(页内位移) 分页物理地址=f(页帧号).d(同上) P = 线性逻辑地址/页面大小 d= 线性逻辑地址-P*页面大小 [1]

阅读全文

TCMalloc

在tcmalloc内存管理的体系之中,一共有三个层次:ThreadCache、CentralCache、PageHeap 分配内存和释放内存的时候都是按从前到后的顺序,在各个层次中去进行尝试。基本思想是:前面的层次分配内存失败,则从下一层分配一批补充上来;前面的层次释放了过多的内存,则回收一批到下一层次。 这几个层次从前到后,主要有这么几方面的变化:

阅读全文

page

用户程序一般只面对虚拟内存,知道执行的时候才通过MMU根据内核中的页表将page和page frame进行对应。

阅读全文

c语言内存管理

在C语言中,关于内存管理的知识点比较多,如函数、变量、作用域、指针等 1.变量:不解释。但需要搞清楚这几种变量类型:

阅读全文

memory 内存管理机制

所有进程(执行的程序)都必须占用一定数量的内存,它或是用来存放从磁盘载入的程序代码,或是存放取自用户输入的数据等等。不过进程对这些内存的管理方式因内存用途不一而不尽相同,有些内存是事先静态分配和统一回收的,而有些却是按需要动态分配和回收的。

阅读全文

crontab

cron工作原理:cron由crond守护进程和一组表(crontab文件)组成。 crond守护进程是在系统启动时由init进程启动的,受init进程的监视,如果它不存在了,会被init进程重新启动。这个守护进程每分钟唤醒一次,并通过检查crontab文件判断需要做什么。 每个用户有一个以用户名命名的crontab文件,存放在/var/spool/cron/crontabs目录里。若管理员允许或者禁止其他用户拥有crontab文件,则应编辑/etc/下面的cron.deny和cron.allow这两个文件来禁止或允许用户拥有自己的crontab文件。每一个用户都可以有自己的crontab文件,但在一个较大的系统中,系统管理员一般会禁止这些文件,而只在整个系统保留一个这样的文件。

阅读全文

livenessProbe

通用tcp探活原理 其实,探活原理特别简单,只要稍懂计算机网络就能够理解。

阅读全文

lock

死锁概念及产生原理     概念:多个并发进程因争夺系统资源而产生相互等待的现象。     原理:当一组进程中的每个进程都在等待某个事件发生,而只有这组进程中的其他进程才能触发该事件,这就称这组进程发生了死锁。     本质原因:         1)、系统资源有限。         2)、进程推进顺序不合理。 死锁产生的4个必要条件     1、互斥:某种资源一次只允许一个进程访问,即该资源一旦分配给某个进程,其他进程就不能再访问,直到该进程访问结束。     2、占有且等待:一个进程本身占有资源(一种或多种),同时还有资源未得到满足,正在等待其他进程释放该资源。     3、不可抢占:别人已经占有了某项资源,你不能因为自己也需要该资源,就去把别人的资源抢过来。     4、循环等待:存在一个进程链,使得每个进程都占有下一个进程所需的至少一种资源。        当以上四个条件均满足,必然会造成死锁,发生死锁的进程无法进行下去,它们所持有的资源也无法释放。这样会导致CPU的吞吐量下降。所以死锁情况是会浪费系统资源和影响计算机的使用性能的。那么,解决死锁问题就是相当有必要的了。

阅读全文

ssh tty

-t -t Force pseudo-tty allocation. This can be used to execute arbitrary screen-based programs on a remote machine, which can be very useful, e.g. when implementing menu services.
Multiple -t options force tty allocation, even if ssh has no local tty. Try ssh -t (or ssh -tt for short) to force pseudo-tty allocation even if stdin isn’t a terminal. 【我的理解是:即使想连接的目标主机不是在终端执行,也会强制使用在终端执行。待指正】

阅读全文

tty

由于串口的缘故,TTY是Linux系统中最普遍的一类设备,稍微了解Linux系统的同学,对它都不陌生。尽管如此,相信很少有人能回到这样的问题:TTY到底是什么东西?我们常常挂在嘴边的终端(terminal)、控制台(console)等概念,到底是什么意思?

阅读全文

session

Linux是一个多任务操作系统,可以方便的在一个控制台(或shell)下同时执行多条命令,达到这样的目标并不是一件容易的事情。本文帮助理解下面几个跟控制台有关的概念:tty/pty,control terminal,session,process group,signal;并设计实现一个多任务控制程序

阅读全文

ptmx

TTY设备 对于每一个终端,TTY driver都会创建一个TTY设备与它对应,如果有多个终端连接过来,那么看起来就是这个样子的:

阅读全文

命令行界面 (CLI)、终端 (Terminal)、Shell、TTY的区别

命令行界面 (CLI) = 使用文本命令进行交互的用户界面 终端 (Terminal) = TTY = 文本输入/输出环境 控制台 (Console) = 一种特殊的终端 Shell = 命令行解释器,执行用户输入的命令并返回结果

  1. 什么是命令行界面? 命令行界面,通俗来讲,就是你看过的那种满屏幕都是字符的界面。
  2. 终端 —— 人与机器交互的接口 2.2. 控制台 (Console) 是什么? 在历史上,终端是连接到计算机上的一种带输入输出功能的外设。但是有一个终端与众不同,它与计算机主机是一体的,是计算机的一个组成部分。这个特殊的终端就叫做 控制台(Console)。
阅读全文

top、free、uptime、htop

在linux中查看系统资源占用的三个命令:top、free、uptime 一,top命令

阅读全文

load

CPU负载和 CPU使用率 这两个从一定程度上都可以反映一台机器的繁忙程度.

阅读全文

内存屏障

linux内核(4.0版本)的Documentation/memory-barriers.txt。 .1 内存访问的的抽象模型

阅读全文

孤儿进程、僵尸进程和守护进程

孤儿进程指的是在其父进程执行完成或被终止 后仍继续运行的一类进程。

阅读全文

radix 基数树

Linux基数树(radix tree)是将指针与long整数键值相关联的机制,它存储有效率,并且可快速查询,用于指针与整数值的映射(如:IDR机制)、内存管理等。 IDR(ID Radix)机制是将对象的身份鉴别号整数值ID与对象指针建立关联表,完成从ID与指针之间的相互转换。IDR机制使用radix树状结构作为由id进行索引获取指针的稀疏数组,通过使用位图可以快速分配新的ID,IDR机制避免了使用固定尺寸的数组存放指针。IDR机制的API函数在lib/idr.c中实现,这里不加分析。 Linux radix树最广泛的用途是用于内存管理,结构address_space通过radix树跟踪绑定到地址映射上的核心页,该radix树允许内存管理代码快速查找标识为dirty或writeback的页。Linux radix树的API函数在lib/radix-tree.c中实现。 radix树是通用的字典类型数据结构,radix树又称为PAT位树(Patricia Trie or crit bit tree)。Linux内核使用了数据类型unsigned long的固定长度输入的版本。每级代表了输入空间固定位数。 radix tree是一种多叉搜索树,树的叶子结点是实际的数据条目。每个结点有一个固定的、2^n指针指向子结点(每个指针称为槽slot),并有一个指针指向父结点。

阅读全文

sed

sed [选项] [命令] 1.1 选项 -n,使用安静(silent)模式。在一般 sed 的用法中,所有来自 STDIN 的数据一般都会被列出到终端上。但如果加上 -n 参数后,则只有经过sed特殊处理的那一行(或者动作)才会被列出来。

阅读全文

/dev/udp

往192.168.1.27的机器的8080端口通过UDP协议发送数据,使用以下命令: 注意:半角英文输入: echo “hello world!” > /dev/udp/192.168.1.27/8080

阅读全文

linux 5.3

https://github.com/torvalds/linux/releases Linux Kernel 5.3 能够:兼容 Intel Xeon 服务器中使用的 Intel Speed Select;由于对 Apple SPI 驱动程序的更新,一些 2015 年版的 MacBook 和 MacBook Pro 所使用的键盘和触摸板也得到了支持;而 Raspberry Pi 中使用的 Broadcom SoC 的 CPUFreq 驱动程序也得到了修补。

阅读全文

CentOS

CentOS(Community Enterprise Operating System,中文意思是社区企业操作系统)是Linux发行版之一,它是来自于Red Hat Enterprise Linux依照开放源代码规定释出的源代码所编译而成。由于出自同样的源代码,因此有些要求高度稳定性的服务器以CentOS替代商业版的Red Hat Enterprise Linux使用。两者的不同,在于CentOS完全开源。 CentOS 是RHEL(Red Hat Enterprise Linux)源代码再编译的产物,而且在RHEL的基础上修正了不少已知的 Bug ,相对于其他 Linux 发行版,其稳定性值得信赖。 CentOS在2014初,宣布加入Red Hat。 CentOS 加入红帽后不变的是:

  1. CentOS 继续不收费
  2. 保持赞助内容驱动的网络中心不变
  3. Bug、Issue 和紧急事件处理策略不变
  4. Red Hat Enterprise Linux 和 CentOS 防火墙也依然存在
阅读全文

system call table

系统调用号 函数名 入口点 源代码 0 read sys_read fs/read_write.c 1 write sys_write fs/read_write.c 2 open sys_open fs/open.c 3 close sys_close fs/open.c 4 stat sys_newstat fs/stat.c 5 fstat sys_newfstat fs/stat.c 6 lstat sys_newlstat fs/stat.c 7 poll sys_poll fs/select.c 8 lseek sys_lseek fs/read_write.c 9 mmap sys_mmap arch/x86/kernel/sys_x86_64.c 10 mprotect sys_mprotect mm/mprotect.c 11 munmap sys_munmap mm/mmap.c 12 brk sys_brk mm/mmap.c 13 rt_sigaction sys_rt_sigaction kernel/signal.c 14 rt_sigprocmask sys_rt_sigprocmask kernel/signal.c 15 rt_sigreturn stub_rt_sigreturn arch/x86/kernel/signal.c 16 ioctl sys_ioctl fs/ioctl.c 17 pread64 sys_pread64 fs/read_write.c 18 pwrite64 sys_pwrite64 fs/read_write.c 19 readv sys_readv fs/read_write.c 20 writev sys_writev fs/read_write.c 21 access sys_access fs/open.c 22 pipe sys_pipe fs/pipe.c 23 select sys_select fs/select.c 24 sched_yield sys_sched_yield kernel/sched/core.c 25 mremap sys_mremap mm/mmap.c 26 msync sys_msync mm/msync.c 27 mincore sys_mincore mm/mincore.c 28 madvise sys_madvise mm/madvise.c 29 shmget sys_shmget ipc/shm.c 30 shmat sys_shmat ipc/shm.c 31 shmctl sys_shmctl ipc/shm.c 32 dup sys_dup fs/file.c 33 dup2 sys_dup2 fs/file.c 34 pause sys_pause kernel/signal.c 35 nanosleep sys_nanosleep kernel/hrtimer.c 36 getitimer sys_getitimer kernel/itimer.c 37 alarm sys_alarm kernel/timer.c 38 setitimer sys_setitimer kernel/itimer.c 39 getpid sys_getpid kernel/sys.c 40 sendfile sys_sendfile64 fs/read_write.c 41 socket sys_socket net/socket.c 42 connect sys_connect net/socket.c 43 accept sys_accept net/socket.c 44 sendto sys_sendto net/socket.c 45 recvfrom sys_recvfrom net/socket.c 46 sendmsg sys_sendmsg net/socket.c 47 recvmsg sys_recvmsg net/socket.c 48 shutdown sys_shutdown net/socket.c 49 bind sys_bind net/socket.c 50 listen sys_listen net/socket.c 51 getsockname sys_getsockname net/socket.c 52 getpeername sys_getpeername net/socket.c 53 socketpair sys_socketpair net/socket.c 54 setsockopt sys_setsockopt net/socket.c 55 getsockopt sys_getsockopt net/socket.c 56 clone stub_clone kernel/fork.c 57 fork stub_fork kernel/fork.c 58 vfork stub_vfork kernel/fork.c 59 execve stub_execve fs/exec.c 60 exit sys_exit kernel/exit.c 61 wait4 sys_wait4 kernel/exit.c 62 kill sys_kill kernel/signal.c 63 uname sys_newuname kernel/sys.c 64 semget sys_semget ipc/sem.c 65 semop sys_semop ipc/sem.c 66 semctl sys_semctl ipc/sem.c 67 shmdt sys_shmdt ipc/shm.c 68 msgget sys_msgget ipc/msg.c 69 msgsnd sys_msgsnd ipc/msg.c 70 msgrcv sys_msgrcv ipc/msg.c 71 msgctl sys_msgctl ipc/msg.c 72 fcntl sys_fcntl fs/fcntl.c 73 flock sys_flock fs/locks.c 74 fsync sys_fsync fs/sync.c 75 fdatasync sys_fdatasync fs/sync.c 76 truncate sys_truncate fs/open.c 77 ftruncate sys_ftruncate fs/open.c 78 getdents sys_getdents fs/readdir.c 79 getcwd sys_getcwd fs/dcache.c 80 chdir sys_chdir fs/open.c 81 fchdir sys_fchdir fs/open.c 82 rename sys_rename fs/namei.c 83 mkdir sys_mkdir fs/namei.c 84 rmdir sys_rmdir fs/namei.c 85 creat sys_creat fs/open.c 86 link sys_link fs/namei.c 87 unlink sys_unlink fs/namei.c 88 symlink sys_symlink fs/namei.c 89 readlink sys_readlink fs/stat.c 90 chmod sys_chmod fs/open.c 91 fchmod sys_fchmod fs/open.c 92 chown sys_chown fs/open.c 93 fchown sys_fchown fs/open.c 94 lchown sys_lchown fs/open.c 95 umask sys_umask kernel/sys.c 96 gettimeofday sys_gettimeofday kernel/time.c 97 getrlimit sys_getrlimit kernel/sys.c 98 getrusage sys_getrusage kernel/sys.c 99 sysinfo sys_sysinfo kernel/sys.c 100 times sys_times kernel/sys.c 101 ptrace sys_ptrace kernel/ptrace.c 102 getuid sys_getuid kernel/sys.c 103 syslog sys_syslog kernel/printk/printk.c 104 getgid sys_getgid kernel/sys.c 105 setuid sys_setuid kernel/sys.c 106 setgid sys_setgid kernel/sys.c 107 geteuid sys_geteuid kernel/sys.c 108 getegid sys_getegid kernel/sys.c 109 setpgid sys_setpgid kernel/sys.c 110 getppid sys_getppid kernel/sys.c 111 getpgrp sys_getpgrp kernel/sys.c 112 setsid sys_setsid kernel/sys.c 113 setreuid sys_setreuid kernel/sys.c 114 setregid sys_setregid kernel/sys.c 115 getgroups sys_getgroups kernel/groups.c 116 setgroups sys_setgroups kernel/groups.c 117 setresuid sys_setresuid kernel/sys.c 118 getresuid sys_getresuid kernel/sys.c 119 setresgid sys_setresgid kernel/sys.c 120 getresgid sys_getresgid kernel/sys.c 121 getpgid sys_getpgid kernel/sys.c 122 setfsuid sys_setfsuid kernel/sys.c 123 setfsgid sys_setfsgid kernel/sys.c 124 getsid sys_getsid kernel/sys.c 125 capget sys_capget kernel/capability.c 126 capset sys_capset kernel/capability.c 127 rt_sigpending sys_rt_sigpending kernel/signal.c 128 rt_sigtimedwait sys_rt_sigtimedwait kernel/signal.c 129 rt_sigqueueinfo sys_rt_sigqueueinfo kernel/signal.c 130 rt_sigsuspend sys_rt_sigsuspend kernel/signal.c 131 sigaltstack sys_sigaltstack kernel/signal.c 132 utime sys_utime fs/utimes.c 133 mknod sys_mknod fs/namei.c 134 uselib   fs/exec.c 135 personality sys_personality kernel/exec_domain.c 136 ustat sys_ustat fs/statfs.c 137 statfs sys_statfs fs/statfs.c 138 fstatfs sys_fstatfs fs/statfs.c 139 sysfs sys_sysfs fs/filesystems.c 140 getpriority sys_getpriority kernel/sys.c 141 setpriority sys_setpriority kernel/sys.c 142 sched_setparam sys_sched_setparam kernel/sched/core.c 143 sched_getparam sys_sched_getparam kernel/sched/core.c 144 sched_setscheduler sys_sched_setscheduler kernel/sched/core.c 145 sched_getscheduler sys_sched_getscheduler kernel/sched/core.c 146 sched_get_priority_max sys_sched_get_priority_max kernel/sched/core.c 147 sched_get_priority_min sys_sched_get_priority_min kernel/sched/core.c 148 sched_rr_get_interval sys_sched_rr_get_interval kernel/sched/core.c 149 mlock sys_mlock mm/mlock.c 150 munlock sys_munlock mm/mlock.c 151 mlockall sys_mlockall mm/mlock.c 152 munlockall sys_munlockall mm/mlock.c 153 vhangup sys_vhangup fs/open.c 154 modify_ldt sys_modify_ldt arch/x86/um/ldt.c 155 pivot_root sys_pivot_root fs/namespace.c 156 _sysctl sys_sysctl kernel/sysctl_binary.c 157 prctl sys_prctl kernel/sys.c 158 arch_prctl sys_arch_prctl arch/x86/um/syscalls_64.c 159 adjtimex sys_adjtimex kernel/time.c 160 setrlimit sys_setrlimit kernel/sys.c 161 chroot sys_chroot fs/open.c 162 sync sys_sync fs/sync.c 163 acct sys_acct kernel/acct.c 164 settimeofday sys_settimeofday kernel/time.c 165 mount sys_mount fs/namespace.c 166 umount2 sys_umount fs/namespace.c 167 swapon sys_swapon mm/swapfile.c 168 swapoff sys_swapoff mm/swapfile.c 169 reboot sys_reboot kernel/reboot.c 170 sethostname sys_sethostname kernel/sys.c 171 setdomainname sys_setdomainname kernel/sys.c 172 iopl stub_iopl arch/x86/kernel/ioport.c 173 ioperm sys_ioperm arch/x86/kernel/ioport.c 174 create_module   NOT IMPLEMENTED 175 init_module sys_init_module kernel/module.c 176 delete_module sys_delete_module kernel/module.c 177 get_kernel_syms   NOT IMPLEMENTED 178 query_module   NOT IMPLEMENTED 179 quotactl sys_quotactl fs/quota/quota.c 180 nfsservctl   NOT IMPLEMENTED 181 getpmsg   NOT IMPLEMENTED 182 putpmsg   NOT IMPLEMENTED 183 afs_syscall   NOT IMPLEMENTED 184 tuxcall   NOT IMPLEMENTED 185 security   NOT IMPLEMENTED 186 gettid sys_gettid kernel/sys.c 187 readahead sys_readahead mm/readahead.c 188 setxattr sys_setxattr fs/xattr.c 189 lsetxattr sys_lsetxattr fs/xattr.c 190 fsetxattr sys_fsetxattr fs/xattr.c 191 getxattr sys_getxattr fs/xattr.c 192 lgetxattr sys_lgetxattr fs/xattr.c 193 fgetxattr sys_fgetxattr fs/xattr.c 194 listxattr sys_listxattr fs/xattr.c 195 llistxattr sys_llistxattr fs/xattr.c 196 flistxattr sys_flistxattr fs/xattr.c 197 removexattr sys_removexattr fs/xattr.c 198 lremovexattr sys_lremovexattr fs/xattr.c 199 fremovexattr sys_fremovexattr fs/xattr.c 200 tkill sys_tkill kernel/signal.c 201 time sys_time kernel/time.c 202 futex sys_futex kernel/futex.c 203 sched_setaffinity sys_sched_setaffinity kernel/sched/core.c 204 sched_getaffinity sys_sched_getaffinity kernel/sched/core.c 205 set_thread_area   arch/x86/kernel/tls.c 206 io_setup sys_io_setup fs/aio.c 207 io_destroy sys_io_destroy fs/aio.c 208 io_getevents sys_io_getevents fs/aio.c 209 io_submit sys_io_submit fs/aio.c 210 io_cancel sys_io_cancel fs/aio.c 211 get_thread_area   arch/x86/kernel/tls.c 212 lookup_dcookie sys_lookup_dcookie fs/dcookies.c 213 epoll_create sys_epoll_create fs/eventpoll.c 214 epoll_ctl_old   NOT IMPLEMENTED 215 epoll_wait_old   NOT IMPLEMENTED 216 remap_file_pages sys_remap_file_pages mm/fremap.c 217 getdents64 sys_getdents64 fs/readdir.c 218 set_tid_address sys_set_tid_address kernel/fork.c 219 restart_syscall sys_restart_syscall kernel/signal.c 220 semtimedop sys_semtimedop ipc/sem.c 221 fadvise64 sys_fadvise64 mm/fadvise.c 222 timer_create sys_timer_create kernel/posix-timers.c 223 timer_settime sys_timer_settime kernel/posix-timers.c 224 timer_gettime sys_timer_gettime kernel/posix-timers.c 225 timer_getoverrun sys_timer_getoverrun kernel/posix-timers.c 226 timer_delete sys_timer_delete kernel/posix-timers.c 227 clock_settime sys_clock_settime kernel/posix-timers.c 228 clock_gettime sys_clock_gettime kernel/posix-timers.c 229 clock_getres sys_clock_getres kernel/posix-timers.c 230 clock_nanosleep sys_clock_nanosleep kernel/posix-timers.c 231 exit_group sys_exit_group kernel/exit.c 232 epoll_wait sys_epoll_wait fs/eventpoll.c 233 epoll_ctl sys_epoll_ctl fs/eventpoll.c 234 tgkill sys_tgkill kernel/signal.c 235 utimes sys_utimes fs/utimes.c 236 vserver   NOT IMPLEMENTED 237 mbind sys_mbind mm/mempolicy.c 238 set_mempolicy sys_set_mempolicy mm/mempolicy.c 239 get_mempolicy sys_get_mempolicy mm/mempolicy.c 240 mq_open sys_mq_open ipc/mqueue.c 241 mq_unlink sys_mq_unlink ipc/mqueue.c 242 mq_timedsend sys_mq_timedsend ipc/mqueue.c 243 mq_timedreceive sys_mq_timedreceive ipc/mqueue.c 244 mq_notify sys_mq_notify ipc/mqueue.c 245 mq_getsetattr sys_mq_getsetattr ipc/mqueue.c 246 kexec_load sys_kexec_load kernel/kexec.c 247 waitid sys_waitid kernel/exit.c 248 add_key sys_add_key security/keys/keyctl.c 249 request_key sys_request_key security/keys/keyctl.c 250 keyctl sys_keyctl security/keys/keyctl.c 251 ioprio_set sys_ioprio_set fs/ioprio.c 252 ioprio_get sys_ioprio_get fs/ioprio.c 253 inotify_init sys_inotify_init fs/notify/inotify/inotify_user.c 254 inotify_add_watch sys_inotify_add_watch fs/notify/inotify/inotify_user.c 255 inotify_rm_watch sys_inotify_rm_watch fs/notify/inotify/inotify_user.c 256 migrate_pages sys_migrate_pages mm/mempolicy.c 257 openat sys_openat fs/open.c 258 mkdirat sys_mkdirat fs/namei.c 259 mknodat sys_mknodat fs/namei.c 260 fchownat sys_fchownat fs/open.c 261 futimesat sys_futimesat fs/utimes.c 262 newfstatat sys_newfstatat fs/stat.c 263 unlinkat sys_unlinkat fs/namei.c 264 renameat sys_renameat fs/namei.c 265 linkat sys_linkat fs/namei.c 266 symlinkat sys_symlinkat fs/namei.c 267 readlinkat sys_readlinkat fs/stat.c 268 fchmodat sys_fchmodat fs/open.c 269 faccessat sys_faccessat fs/open.c 270 pselect6 sys_pselect6 fs/select.c 271 ppoll sys_ppoll fs/select.c 272 unshare sys_unshare kernel/fork.c 273 set_robust_list sys_set_robust_list kernel/futex.c 274 get_robust_list sys_get_robust_list kernel/futex.c 275 splice sys_splice fs/splice.c 276 tee sys_tee fs/splice.c 277 sync_file_range sys_sync_file_range fs/sync.c 278 vmsplice sys_vmsplice fs/splice.c 279 move_pages sys_move_pages mm/migrate.c 280 utimensat sys_utimensat fs/utimes.c 281 epoll_pwait sys_epoll_pwait fs/eventpoll.c 282 signalfd sys_signalfd fs/signalfd.c 283 timerfd_create sys_timerfd_create fs/timerfd.c 284 eventfd sys_eventfd fs/eventfd.c 285 fallocate sys_fallocate fs/open.c 286 timerfd_settime sys_timerfd_settime fs/timerfd.c 287 timerfd_gettime sys_timerfd_gettime fs/timerfd.c 288 accept4 sys_accept4 net/socket.c 289 signalfd4 sys_signalfd4 fs/signalfd.c 290 eventfd2 sys_eventfd2 fs/eventfd.c 291 epoll_create1 sys_epoll_create1 fs/eventpoll.c 292 dup3 sys_dup3 fs/file.c 293 pipe2 sys_pipe2 fs/pipe.c 294 inotify_init1 sys_inotify_init1 fs/notify/inotify/inotify_user.c 295 preadv sys_preadv fs/read_write.c 296 pwritev sys_pwritev fs/read_write.c 297 rt_tgsigqueueinfo sys_rt_tgsigqueueinfo kernel/signal.c 298 perf_event_open sys_perf_event_open kernel/events/core.c 299 recvmmsg sys_recvmmsg net/socket.c 300 fanotify_init sys_fanotify_init fs/notify/fanotify/fanotify_user.c 301 fanotify_mark sys_fanotify_mark fs/notify/fanotify/fanotify_user.c 302 prlimit64 sys_prlimit64 kernel/sys.c 303 name_to_handle_at sys_name_to_handle_at fs/fhandle.c 304 open_by_handle_at sys_open_by_handle_at fs/fhandle.c 305 clock_adjtime sys_clock_adjtime kernel/posix-timers.c 306 syncfs sys_syncfs fs/sync.c 307 sendmmsg sys_sendmmsg net/socket.c 308 setns sys_setns kernel/nsproxy.c 309 getcpu sys_getcpu kernel/sys.c 310 process_vm_readv sys_process_vm_readv mm/process_vm_access.c 311 process_vm_writev sys_process_vm_writev mm/process_vm_access.c 312 kcmp sys_kcmp kernel/kcmp.c 313 finit_module sys_finit_module kernel/module.c 在linux 查看32位的系统调用号 cat /usr/include/asm/unistd_32.h

阅读全文

函数调用帧栈

区域 作用 栈区(stack) 由编译器自动分配和释放,存放函数的参数值,局部变量的值等。操作方式类似与数据结构中的栈 堆区(heap) 一般由程序员分配和释放,若程序员不释放,程序结束时可能由操作系统回收。与数据结构中的堆是两码事,分配方式类似于链表 静态区(static) 全局变量和静态变量存放于此 文字常量区 常量字符串放在此,程序结束后由系统释放 程序代码区 存放函数体的二进制代码 栈帧就是一个函数执行的环境。实际上,栈帧可以简单理解为:栈帧就是存储在用户栈上的(当然内核栈同样适用)每一次函数调用涉及的相关信息的记录单元。 栈是从高地址向低地址延伸的。每个函数的每次调用,都有它自己独立的一个栈帧,这个栈帧中维持着所需要的各种信息。寄存器ebp指向当前的栈帧的底部(高地址),寄存器esp指向当前的栈帧的顶部(地址地)。

阅读全文

C语言的异常机制 setjump longjump函数

与刺激的abort()和exit()相比,goto语句看起来是处理异常的更可行方案。不幸的是,goto是本地的:它只能跳到所在函数内部的标号上,而不能将控制权转移到所在程序的任意地点(当然,除非你的所有代码都在main体中)。 为了解决这个限制,C函数库提供了setjmp()和longjmp()函数,它们分别承担非局部标号和goto作用。头文件申明了这些函数及同时所需的jmp_buf数据类型。 原理非常简单: 1.setjmp(j)设置“jump”点,用正确的程序上下文填充jmp_buf对象j。这个上下文包括程序存放位置、栈和框架指针,其它重要的寄存器和内存数据。当初始化完jump的上下文,setjmp()返回0值。

  1. 以后调用longjmp(j,r)的效果就是一个非局部的goto或“长跳转”到由j描述的上下文处(也就是到那原来设置j的setjmp()处)。当作为长跳转的目标而被调用时,setjmp()返回r或1(如果r设为0的话)。(记住,setjmp()不能在这种情况时返回0。)
阅读全文

rtld_fini

C/C++程序员在写程序时,总是默认程序是从main函数开始的,我们会认为这理所当然,但事实上,当程序在执行到main函数时,很多事情已经完成了。我们可以看看一下几个例子:

阅读全文

main

ld有多种方法设置进程入口地址, 按一下顺序: (编号越前, 优先级越高) 1, ld命令行的-e选项 2, 连接脚本的ENTRY(SYMBOL)命令 eg. ENTRY(_start) /* Entry point of application*/ 3, 如果定义了start符号, 使用_start符号值 4, 如果存在.text section, 使用.text section的第一字节的位置值 5, 使用值0 (一)通常例子

阅读全文

heap 内存管理之堆和栈

32位Linux系统,即起始地址为0x08048000,可以看到顺序为只读段(代码段等)、读写段(数据段、bss段等)、堆(向上即高地址扩展)、用于堆扩展的未使用空间、动态库的映射位置(0x40000000开始)、之后就是栈(向下即低地址扩展)以及用于栈扩展的未使用空间、最后是内核空间。 其中栈最大的不同就是从高地址向低地址扩展,同时有esp指针一直指向栈顶(低地址),另外栈的原则是先入后出。

阅读全文

dumpbin

有时候我们想查看一个exe引用了哪些动态库,或者我们想看某个动态库包含哪些接口函数,这个时候可以使用dumpbin.exe工具:

阅读全文

c++filt

我们知道, 在C++中, 是允许函数重载的, 也就引出了编译器的name mangling机制, 今天我们要介绍的c++filt命令便与此有关。

阅读全文

Linux进程分配内存的两种方式--brk() 和mmap()

如何查看进程发生缺页中断的次数?

阅读全文

atexit

按照ISO C的规定,一个进程可以登记至少32个函数,这些函数将由exit自动调用。atexit()注册的函数类型应为不接受任何参数的void函数。 http://man7.org/linux/man-pages/man3/atexit.3.html 函数名: atexit 头文件:#include 功 能: 注册终止函数(即main执行结束后调用的函数) 用 法: void atexit(void (*func)(void)); 注意:exit调用这些注册函数的顺序与它们 登记时候的顺序相反。同一个函数如若登记多次,则也会被调用多次。

阅读全文

atexit

1、作用      - 注册函数,main函数返回或者exit函数调用,函数执行顺序与注册顺序相反      -程序退出时调用静态全局变量析构函数(包含静态成员变量) 2、静态对象       全局静态对象构造函数最早,到局部成员静态对象构造函数,析构则相反      想在程序退出时析构,要在atexit注册 1> 在C/C++中,怎样在程序退出main函数后仍然进行一些操作,比如资源堆栈方面的清理?

阅读全文

0x80 0x21 软中断

系统调用是一个软中断,中断号是0x80,它是上层应用程序与Linux系统内核进行交互通信的唯一接口。

阅读全文

readelf

readelf命令,一般用于查看ELF格式的文件信息,常见的文件如在Linux上的可执行文件,动态库(.so)或者静态库(.a) 等包含ELF格式的文件。 语法:readelf (选项)(参数:文件),除了-v和-H之外,其它的选项必须有一个被指定参数

阅读全文

ar

我们已经很熟悉linux中的tar命令了, 英文原文是tape archive,  磁带归档。 今天, 我们要说的是ar命令, 也就是archive, 也是归档。 其实, 对目标文件.o进行归档, 就形成了静态库.a文件。实际上, ar命令可以对一个或者多个目标文件.o进行归档, 形成一个静态库.a文件。 可见, 静态库还是很简单的,无非就是众多目标文件的集合。

阅读全文

压栈的工作原理

内存的不同用途   根据不同的操作系统,一个进程可能被分配到不同的内存区域去执行。但是不管什么样的操作系统、什么样的计算机架构,进程使用的内存都可以按照功能大致分为以下4个部分:

阅读全文

perf pprof

常用的数据采样工具包括 linux perf、golang pprof 等 perf是一款Linux性能分析工具。Linux性能计数器是一个新的基于内核的子系统,它提供一个性能分析框架,比如硬件(CPU、PMU(Performance Monitoring Unit))功能和软件(软件计数器、tracepoint)功能。 通过perf,应用程序可以利用PMU、tracepoint和内核中的计数器来进行性能统计。它不但可以分析制定应用程序的性能问题(per thread),也可以用来分析内核的性能问题,当然也可以同事分析应用程序和内核,从而全面理解应用程序中的性能瓶颈。

阅读全文

gprof 原理

google profile工具以及其他常用profile的工具,如GNU gprof、oprofile等(都是开源项目),并对其实现原理做了简单分析和比较。 一、 GUN Gropf Gprof是GNU profiler工具。可以显示程序运行的“flatprofile”,包括每个函数的调用次数,每个函数消耗的处理器时间。也可以显示“调用图”,包括 函数的调用关系,每个函数调用花费了多少时间。还可以显示“注释的源代码”,是程序源代码的一个复本,标记有程序中每行代码的执行次数。关于Gprof的 使用以及实现原理网上已有多篇文章提及,本文就不再详述,只是对其进行梳理和总结,方便阅读。(Gprof的官方网 址:http://www.cs.utah.edu/dept/old/texinfo/as/gprof_toc.html,http: //sourceware.org/binutils/docs/gprof/index.html 绝对权威的参考资料。) 1.1 安装 Glibc自带,无需另外安装

阅读全文

flamegraph

学习文档: https://blog.csdn.net/gatieme/article/details/78885908

阅读全文

load average

在Linux系统中,我们一般使用uptime命令查看(w命令和top命令也行)。(另外,它们在苹果公司的Mac电脑上也适用。) 显示”load average”,它的意思是”系统的平均负荷”,里面有三个数字,我们可以从中判断系统负荷是大还是小。 它们的意思分别是1分钟、5分钟、15分钟内系统的平均负荷。 平均负荷为0;当CPU工作量饱和的时候,平均负荷为1。

阅读全文

tty stdin stdout stderr

stdin是针对你的应用程序的,/dev/tty是针对设备的入口 正常情况下stdin bind到/dev/tty,但是stdin不一定是/dev/tty 比如在cgi环境中 #include “apue.h” 查看句柄是不是tty if(isatty(0)){ 获取tty名字 name = ttyname(0);

阅读全文

elf

UNIX系统实验室(USL)作为应用程序二进制接口(Application Binary Interface,ABI)而开发和发布的,也是Linux的主要可执行文件格式。 被86open项目选为x86架构上的类Unix操作系统的二进制文件标准格式,用来取代COFF ELF文件由4部分组成,分别是ELF头(ELF header)、程序头表(Program header table)、节(Section)和节头表(Section header table)。实际上,一个文件中不一定包含全部内容,而且他们的位置也未必如同所示这样安排,只有ELF头的位置是固定的,其余各部分的位置、大小等信息由ELF头中的各项值来决定。

阅读全文

tty 及其在远程登录(SSH,telnet等)中的应用

[功能]

阅读全文

ssh 原理

SSH(22端口)是Secure Shell Protocol的简写,由IETF网络工作小组(Network Working Group)制定;在进行数据传输之前,SSH先对联机数据包通过加密技术进行加密处理,加密后在进行数据传输。确保了传递的数据安全。

阅读全文

Linux中tty、pty、pts的概念区别

telnet客 1. 需要有一个任务不停的从socket中读取远端client传过来的原始字符, 然后加以处理之后写入到pty Master中. (类比, 即键盘在往串口输入数据啦) 2. 需要有一个任务不停的从pty Master读取执行命令后的回显啊, 打印信息啊之类的数据, 送到socket中去给远端显示. 3. 从pty slave中读取pty中的数据了之后, 传给cli模块进行解析, 像命令行一样解析. 4. cli命令行模块执行命令, 然后所有的打印输出, 如printf之类的, 重定向到pty slave这个设备. 好了, 这里有一点必须要说明的是, pty Master和pty slave不是两个设备, 是一个设备, 是一个虚拟的设备. 也就是说, 你执行函数write(ptyMasterFd), 是可以通过read(ptySlaveFd)读取到数据的!!! 反之亦然.

vxworks是怎么把这个pty设备建立起来的呢, 这个可以从帮助文件中找到:
1. 先调用ptyDrv( ) 初始化驱动
2. 调用ptyDevCreate( ) 来创建pty设备, 当然也可以用ptyDevRemove( )来移除一个已经创建过的pty设备.
    在调用ptyDevCreate( )的时候, 举例来说, ptyDevCreate ("/pty/0.", 512, 512); 那么就会生成一个pty Master, 自动命名为"/pty0.M", 并且生成一个pty Slave, 自动命名为"/pty0.S". 然后Master和Slave就可以通过open()函数打开啦! 比如说 open ("/pty0.M", O_RDWR, 0), 就可以得到master的fd了, slave同样.户端的具体实现:
阅读全文

SSH

如果一个用户从本地计算机,使用SSH协议登录另一台远程计算机,我们就可以认为,这种登录是安全的,即使被中途截获,密码也不会泄露。

阅读全文

Quagga

Quagga软件原名是Zebra是由一个日本开发团队编写的一个以GNU版权方式发布的软件。Quagga项目开始与1996年,当前版本是0.98.4版 可以使用Quagga将linux机器打造成一台功能完备的路由器。 Quagga支持的路由协议 Quagga能够同时支持RIPv1、RIPv2、RIPng、OSPFv2、OSPFv3、BGP-4和 BGP-4+等诸多TCP/IP协议。其中: RIPv1、RIPv2、OSPFv2适用于Ipv4的自治域系统内部网络路由协议。 BGP-4是用于Ipv4的自治域系统之间的外部网络路由协议。 RIPng、OSPFv3、BGP-4+主要扩展对Ipv6的支持。 Quagga的特性 模块化设计:Quagga基于模块化方案的设计,即对每一个路由协议使用单独的守护进程。 运行速度快:因为使用了模块化的设计,使得Quagga的运行速度比一般的路由选择程序要快。 可靠性高:在所有软件模块都失败的情况下,路由器可以继续保持连接并且daemons也会继续运行。故障诊断不必离线的状态下被诊断和更正 支持Ipv6:Quagga不仅支持Ipv4,还支持Ipv6。 Quagga的运行机制 由于Quagga采用模块化的设计,因此Quagga运行时要运行多个守护进程,包括ripd ripngd ospfd ospf6d bgpd 和Zebra。 其中,Zebra守护进程用来更新内核的路由表,而其他的守护进程负责进行相应路由选择协议的路由更新。 Quagga的好处 就路由器而论,虽然有各种硬件可用,但是费用较高。所以想到用一个运行Linux系统构件的功能丰富的路由器作为代替。Quagga路由守护程序已经使这一切变为现实。因为Quagga支持Ipv4、Ipv6和其他各式各样的协议,所以能够满足通常所有的路由需要。 使用Quagga的另一个好处是,这个软件配置的很多方面跟Cisco的IOS配置几乎完全相同,如果你在Cisco IOS环境中工作,可以轻松的过渡到Quagga系统,同时,使用Quagga特能让你积累起丰富的类似于使用Cisco IOS路由器的经验和知识。 总之,现在完全可以拿一台PC机来完成一些必须用昂贵的Cisco路由器才能完成的比较复杂的路由协议处理控制功能。 Quagga使用基础 1、 Quagga的安装与启动 Red Hat EL 3自带了Quagga,下面以RPM包的安装为例介绍Quagga的安装。若用户已经安装了Quagga则可跳过下面的安装步骤。

阅读全文

prctl

prctl(PR_SET_PDEATHSIG,SIGNAL)在父线程退出时调用,而不是父进程退出(prctl(PR_SET_PDEATHSIG, SIGNAL) is called on parent thread exit, not parent process exit)

阅读全文

Namespace 资源隔离

如果要自己实现一个资源隔离的容器,应该从哪些方面下手呢?也许你第一反应可能就是 chroot 命令,这条命令给用户最直观的感觉就是使用后根目录 / 的挂载点切换了,即文件系统被隔离了。然后,为了在分布式的环境下进行通信和定位,容器必然需要一个独立的 IP、端口、路由等等,自然就想到了网络的隔离。同时,你的容器还需要一个独立的主机名以便在网络中标识自己。想到网络,顺其自然就想到通信,也就想到了进程间通信的隔离。可能你也想到了权限的问题,对用户和用户组的隔离就实现了用户权限的隔离。最后,运行在容器中的应用需要有自己的 PID, 自然也需要与宿主机中的 PID 进行隔离。

阅读全文

ioctl 网桥管理

用户空间程序brctl是如何通过ioctl系统调用在kernel空间内创建上述的数据结构。创建网桥,我们不需要预知任何网络设备信息,因此我们通过ioctl来创建网桥时不应该与任何网络设备绑定到一起。网桥模块为此ioctl函数提供了一个恰如其分的名字 br_ioctl_deviceless_stub。Brctl工具使用的ioctl系统调用最终会调用此函数 linux网桥是个内核进程,加载模块首先执行模块初始化

阅读全文

clone

Linux中的clone()函数 int clone(int (*fn)(void *), void *child_stack, int flags, void *arg); 这里fn是函数指针,我们知道进程的4要素,这个就是指向程序的指针,就是所谓的“剧本”, child_stack明显是为子进程分配系统堆栈空间(在linux下系统堆栈空间是2页面,就是8K的内存,其中在这块内存中,低地址上放入了值,这个值就是进程控制块task_struct的值),flags就是标志用来描述你需要从父进程继承那些资源, arg就是传给子进程的参数)。下面是flags可以取的值

阅读全文

cidr

CIDR(无类别域间路由,Classless Inter-Domain Routing)是一个在Internet上创建附加地址的方法,这些地址提供给服务提供商(ISP),再由ISP分配给客户。CIDR将路由集中起来,使一个IP地址代表主要骨干提供商服务的几千个IP地址,从而减轻Internet路由器的负担。 所有发送到这些地址的信息包都被送到如MCI或Sprint等ISP。1990年,Internet上约有2000个路由。五年后,Internet上有3万多个路由。如果没有CIDR,路由器就不能支持Internet网站的增多。 CIDR采用8~30位可变网络ID,而不是A-B-C类网络ID所用的固定的8、16和24位。 适当分配多个合适的IP地址,使得这些地址能够进行聚合,减少这些地址在路由表中的表项数。 如,给某个网络分配16个C类地址,采用适当的方法分配这些地址,使得16个地址能够聚合成一个地址。 “无分类”指不考虑IP地址所属的类别,路由的策略完全基于整个32bit IP地址的掩码来操作。

阅读全文

setfacl

setfacl命令可以用来细分linux下的文件权限。 chmod命令可以把文件权限分为u,g,o三个组,而setfacl可以对每一个文件或目录设置更精确的文件权限。 换句话说,setfacl可以更精确的控制权限的分配。 比如:让某一个用户对某一个文件具有某种权限。

阅读全文

shell $(( ))、$( )、``与${ }的区别

$( )与 (反引号)都是用来作命令替换的。 命令替换与变量替换差不多,都是用来重组命令行的,先完成引号里的命令行,然后将其结果替换出来,再重组成新的命令行。 $( )与`` 在操作上,这两者都是达到相应的效果,但是建议使用$( ),理由如下:

阅读全文

SystemTap

SystemTap 是监控和跟踪运行中的 Linux 内核的操作的动态方法。这句话的关键词是动态,因为 SystemTap 没有使用工具构建一个特殊的内核,而是允许您在运行时动态地安装该工具。它通过一个名为Kprobes 的应用编程接口(API)来实现该目的 SystemTap 与一种名为 DTrace 的老技术相似,该技术源于 Sun Solaris 操作系统。在 DTrace 中,开发人员可以用 D 编程语言(C 语言的子集,但修改为支持跟踪行为)编写脚本。DTrace 脚本包含许多探针和相关联的操作,这些操作在探针 “触发” 时发生。例如,探针可以表示简单的系统调用,也可以表示更加复杂的交互,比如执行特定的代码行。清单 1 显示了 DTrace 脚本的一个简单例子,它计算每个进程发出的系统调用的数量(注意,使用字典将计数和进程关联起来)。该脚本的格式包含探针(在发出系统调用时触发)和操作(对应的操作脚本)。

阅读全文

xargs

xargs: echo: terminated by signal 13 注意xargs -i 后面没有管道 | ,否则报错

阅读全文

awk完成两个文件的关联Join 高级用法

先看看awk中的两个自身变量,NR和FNR。 awk可以指定同时读取多个文件,按照指定的先后顺序,逐个读取。

阅读全文

pollfd

int poll (struct pollfd *fds, size_t nfds , int timeout);

阅读全文

libevent 编译安装

http://libevent.org/ 下载源码:

阅读全文

fd_set

(一)fd_set 的实现

阅读全文

epoll_event

结构体epoll_event被用于注册所感兴趣的事件和回传所发生待处理的事件,定义如下: typedef union epoll_data { void ptr; int fd; __uint32_t u32; __uint64_t u64; } epoll_data_t;//保存触发事件的某个文件描述符相关的数据 struct epoll_event { __uint32_t events; / epoll event / epoll_data_t data; / User data variable */ };

阅读全文

configure.ac

编译源码目录下只有configure.ac文件和Makefile.am文件的工程 aclocal

阅读全文

Makefile/Makefile.am/Makefile.in三者关系

用make 命令来编译自己写的程序确实是很方便。一般情况下,大家都是手工写一个简单Makefile ,如果要想写出一个符合自由软件惯例的Makefile 就不那么容易了。 一、Makefile 介绍 Makefile 是用于自动编译和链接的,一个工程有很多文件组成,每一个文件的改变都会导致工程的重新链接,但是不是所有的文件都需要重新编译,Makefile 中纪录有文件的信息,在make 时会决定在链接的时候需要重新编译哪些文件。

阅读全文

eagain accept

在Linux环境下开发经常会碰到很多错误(设置errno),其中EAGAIN是其中比较常见的一个错误(比如用在非阻塞操作中)。

阅读全文

Socket 源码

Kernel提供了一组内核态的socket API,基本上在用户态的sockt API在内核中都有对应的API。 在net/socket.c中可以看到如下导出符号:

阅读全文

SIGIO

在TCP 连接中, SIGIO 信号将会在这个时候产生: l 在一个监听某个端口的套接字上成功的建立了一个新连接。 l 一个断线的请求被成功的初始化。 l 一个断线的请求成功的结束。 l 套接字的某一个通道(发送通道或是接收通道)被关闭。 l 套接字接收到新数据。 l 套接字将数据发送出去。 l 发生了一个异步I/O 的错误。 举例来说,如果一个正在进行读写操作的TCP 套接字处于信号驱动I/O 状态下,那么 每当新数据到达本地的时候,将会产生一个SIGIO 信号,每当本地套接字发出的数据被远 程确认后,也会产生一个SIGIO 信号。对于我们的程序来讲,是无法区分这两个SIGIO 有 什么区别的。在这种情况下使用SIGIO,TCP 套接字应当被设置为无阻塞模式来阻止一个 阻塞的read 和write(recv 和send)操作。我们可以考虑在一个只进行监听网络连接操作 的套接字上使用异步I/O,这样当有一个新的连接的时候,SIGIO 信号将会产生。

阅读全文

gdb_codesign

在 初次使用 gdb 时,可能会遇到这样的错误:

阅读全文

fcntl函数设置阻塞与非阻塞

用命令F_GETFL和F_SETFL设置文件标志,比如阻塞与非阻塞

阅读全文

swoole

https://github.com/swoole/swoole-src/ https://toxmc.github.io/swoole-cs.github.io/

阅读全文

进程池,线程池

由于服务器的硬件资源“充裕”,那么提高服务器性能的一个很直接的方法就是以空间换时间,即“浪费”服务器的硬件资源,以换取其运行效率。这就是池的概念。

阅读全文

getuid、geteuid和setuid函数

在linux中每个进程有三个[实际上有第4个]用户标识符. real uid : 真实用户ID. saved uid : 已保存用户ID , effective uid : 有效用户ID 真实用户ID(real uid)是login时的用户.而在运行过程中,用于所有的安全检查的是有效用户ID(effective uid). 一般情况下:real uid = saved uid = effective uid在某些场合下,使用用setuid,setruid函数可以改变effective uid,从而使得程序运行时具有特殊的权限.常见的例子是linux系统中的passwd命令,由于所有的用户信息包括用户密码都保存在/etc/passwd文件中,而/etc/passwd文件只有root权限可以读写,若想让每个用户都只可以修改自己的密码,就必须让普通用户暂时获得有限的读写/etc/passwd的权限.用setuid就可以解决这个 问题. Linux setuid(uid)函数: (1)如果由普通用户调用,将当前进程的有效ID设置为uid. (2)如果由有效用户ID符为0的进程调用,则将真实,有效和已保存用户ID都设 置为uid.

阅读全文

fpm 源码阅读

FastCGI是Web服务器(如:Nginx、Apache)和处理程序之间的一种通信协议,它是与Http类似的一种应用层通信协议,注意:它只是一种协议! PHP只是一个脚本解析器,你可以把它理解为一个普通的函数,输入是PHP脚本。输出是执行结果,假如我们想用PHP代替shell,在命令行中执行一个文件,那么就可以写一个程序来嵌入PHP解析器,这就是cli模式,这种模式下PHP就是普通的一个命令工具。接着我们又想:能不能让PHP处理http请求呢?这时就涉及到了网络处理,PHP需要接收请求、解析协议,然后处理完成返回请求。在网络应用场景下,PHP并没有像Golang那样实现http网络库,而是实现了FastCGI协议,然后与web服务器配合实现了http的处理,web服务器来处理http请求,然后将解析的结果再通过FastCGI协议转发给处理程序,处理程序处理完成后将结果返回给web服务器,web服务器再返回给用户,如下图所示。

阅读全文

umask umask(0)

当我们登录系统之后创建一个文件总是有一个默认权限的,那么这个权限是怎么来的呢?这就是umask干的事情。umask设置了用户创建文件的默认 权限,它与chmod的效果刚好相反,umask设置的是权限“补码”,而chmod设置的是文件权限码。一般在/etc/profile、$ [HOME]/.bash_profile或$[HOME]/.profile中设置umask值。

阅读全文

sigsuspend 进程阻塞 与 pause 区别

sigsuspend函数 : sigsuspend函数接受一个信号集指针,将信号屏蔽字设置为信号集中的值,在进程接受到一个信号之前,进程会挂起,当捕捉一个信

阅读全文

sigaction

sigaction函数的功能是检查或修改与指定信号相关联的处理动作(可同时两种操作) 其中,参数signo是要检测或修改其具体动作的信号编号。若act指针非空,则要修改其动作。如果oact指针非空,则系统经由oact指针返回该信号的上一个动作。此函数使用下列结构

阅读全文

信号(sigaction,sigaddset,sigprocmask)

sigaction(查询或设置信号处理方式) 相关函数 signal,sigprocmask,sigpending,sigsuspend 表头文件 #include 定义函数 int sigaction(int signum,const struct sigaction *act ,struct sigaction *oldact); 函数说明 sigaction()会依参数signum指定的信号编号来设置该信号的处理函数。参数signum可以指定SIGKILL和SIGSTOP以外的所有信号。 如参数结构sigaction定义如下

阅读全文

三种时间结构及定时器setitimer()

三种时间结构 time_t://seconds

阅读全文

setsid & 后台进程

当进程是会话的领头进程时setsid()调用失败并返回(-1)。 setsid()调用成功后,返回新的会话的ID,调用setsid函数的进程成为新的会话的领头进程,并与其父进程的会话组和进程组脱离。 由于会话对控制终端的独占性,进程同时与控制终端脱离。

阅读全文

getsockname getpeername

 这两个函数或者返回与某个套接字关联的本地协议地址(getsockname),或者返回与某个套接字关联的外地协议地址即得到对方的地址(getpeername)。 #include <sys/socket.h>

int getsockname(int sockfd,struct sockaddr* localaddr,socklen_t addrlen); int getpeername(int sockfd,struct sockaddr peeraddr,socklen_t *addrlen); 均返回:若成功则为0,失败则为-1                       getsockname可以获得一个与socket相关的地址,服务器端可以通过它得到相关客户端地址,而客户端也可以得到当前已连接成功的socket的ip和端口。

阅读全文

waitpid wait

【waitpid系统调用】
功能描述: 等待进程改变其状态。所有下面哪些调用都被用于等待子进程状态的改变,获取状态已改变的子进程信息。状态改变可被认为是:1.子进程已终止。2.信号导致子进程停止执行。3.信号恢复子进程的执行。在子进程终止的情况下,wait调用将允许系统释放与子进程关联的资源。如果不执行wait,终止了的子进程会停留在”zombie”状态。 如果发现子进程改变了状态,这些调用会立即返回。反之,调用会被阻塞直到子进程状态改变,或者由信号处理句柄所中断(假如系统调用没有通过sigaction的SA_RESTART标志重启动)。

阅读全文

socketpair popen

先说说我的理解:socketpair创建了一对无名的套接字描述符(只能在AF_UNIX域中使用),描述符存储于一个二元数组,eg. s[2] .这对套接字可以进行双工通信,每一个描述符既可以读也可以写。这个在同一个进程中也可以进行通信,向s[0]中写入,就可以从s[1]中读取(只能从s[1]中读取),也可以在s[1]中写入,然后从s[0]中读取;但是,若没有在0端写入,而从1端读取,则1端的读取操作会阻塞,即使在1端写入,也不能从1读取,仍然阻塞;反之亦然…… // // Created by didi on 2019-05-31. //

阅读全文

linux 信号系统调用

1、signal 系统调用  系统调用signal用来设定某个信号的处理方法。该调用声明的格式如下:  void (signal(int signum, void (handler)(int)))(int);  在使用该调用的进程中加入以下头文件:  #include 

阅读全文

sockt编程中的文件句柄 select poll epoll

socket编程的基本步骤: socket()函数返回监听的socket句柄 listen()监听socket句柄
bind() 句柄和端口绑定 while(1){ accept() 返回连接的句柄 fork()子程序处理 { dump2()将输入输出重定向到连接句柄 write(iConnSocket, szBuf, strlen(szBuf) + 1); while(1){ read() } } }

阅读全文

How TCP Sockets Work

https://eklitzke.org/how-tcp-sockets-work tcp 连接的可读可写有两种方式通知内核:轮询和中断 内核收到可读事件可写事件后分配连接队列(backlog),listen的时候设置;或者拒绝连接。 应用程序读写数据有两种方式:阻塞和非阻塞;阻塞方式是read/write系统调用后,一直等待,连接队列的接受/发送缓冲可读/写的时候,读/写数据,处理完了返回。非阻塞方式read/write系统调用后立即返回。连接队列的接受/发送缓冲可读/写的时候处罚读写事件,事件处理器(libevent)分发给处理函数处理。

阅读全文

io 模型

不要用操作磁盘文件的经验去看待网络IO 相比于传统的网络IO来说,一个普通的文件描述符的操作可以分为两部分。以read为例,我们利用read函数从socket中同步阻塞的读取数据,整个流程如下所示: 调用read后,该调用会转入内核调用 内核会等待该socket的可读事件,直到远程向socket发送了数据。可读事件成立(这里还需要满足TCP的低水位条件,但是不做太详细的讨论) 数据包到达内核,接着内核将数据拷贝到用户进程中,也就是read函数指定的buffer参数中。至此,read调用结束。 可以看到除了转入内核调用,与传统的磁盘IO不同的是,网络IO的读写大致可以分为两个阶段:

阅读全文

SO_REUSEPORT 惊群

单个进程监听多个端口 单个进程创建多个 socket 绑定不同的端口,TCP, UDP 都行

阅读全文

SO_REUSEPORT 多个server进程同时监听一个unix socket文件

nginx配置大致如下: upstream webserver { server unix:/var/3w/9101.sock server unix:/var/3w/9102.sock server unix:/var/3w/9103.sock server unix:/var/3w/9104.sock }

  • 多个server 进程,共同对应一个unix socket file,前面走nginx代理 启动配置大致如下: python webserver –app_sockfile=/var/3w/webserver.sock python webserver –app_sockfile=/var/3w/webserver.sock python webserver –app_sockfile=/var/3w/webserver.sock python webserver –app_sockfile=/var/3w/webserver.sock nginx配置大致如下: upstream webserver { server unix:/var/3w/webserver.sock } reuseaddr是用来去掉2MSL的,MSL请看tcp协议,简单来讲,当某个绑定了套接字127.0.0.1:8080的进程stop后,假如没有使用reuseaddr,该进程不能立刻重新运行,反之,则可以立刻running。 reuseport,也是setsockopt来设置套接字属性的
阅读全文

多个进程绑定相同端口的实现分析[Google Patch]

Google REUSEPORT 新特性,支持多个进程或者线程绑定到相同的 IP 和端口,以提高 server 的性能。

  1. 设计思路 该特性实现了 IPv4/IPv6 下 TCP/UDP 协议的支持, 已经集成到 kernel 3.9 中。
阅读全文

TCP_CORK tcp_push TCP_NODELAY 和 TCP_NOPUSH

用户层可通过setsockopt系统调用设置TCP套接口的TCP_CORK选项。开启时,内核将阻塞不完整的报文,当关闭此选项时,发送阻塞的报文。此处的不完整指的是应用层发送的数据长度不足一个MSS长度。使用场景是在调用sendfile发送文件内容之前,提前发送一个描述文件信息的头部数据段,并且阻塞住此头部数据,与之后的sendfile数据一同发送。或者用于优化吞吐性能。但是,TCP_CORK最多只能将数据阻塞200毫秒,如果超过此时间值,内核将自动发送阻塞的数据。

阅读全文

pid 文件作用

打开系统(Linux) 的 “/var/run/” 目录可以看到有很多已 “.pid” 为结尾的文件 这些文件只有一行,它记录的是相应进程的 pid,即进程号。所以通过 pid 文件可以很方便的得到一个进程的 pid,然后做相应的操作,比如检测、关闭。 重要的作用,那就是防止进程启动多个副本。通过文件锁,可以保证一时间内只有一个进程能持有这个文件的写权限,所以在程序启动的检测逻辑中加入获取pid 文件锁并写pid文件的逻辑就可以防止重复启动进程的多个副本了。 /var/run是干什么用的 根据linux的文件系统分层结构标准(FHS)中的定义:

阅读全文

libevent

libevent是一个事件通知库,适用于windows、linux、bsd等多种平台,内部使用select、epoll、kqueue、IOCP等系统调用管理事件机制。著名分布式缓存软件memcached也是基于libevent,而且libevent在使用上可以做到跨平台,而且根据libevent官方网站上公布的数据统计,似乎也有着非凡的性能。 libevent支持用户使用三种类型的事件,分别是网络IO、定时器、信号三种,最新版本在定时器的实现上使用了最小堆的数据结构,以达到高效查找、排序、删除定时器的目的,IO和信号的实现均使用了双向队列(用链表实现)。网络IO上,主要关注了一下linux上的epoll(因为目前的开发主要在linux平台),结果发现libevent的epoll居然用的EPOLLLT,水平触发的方式用起来比较方便,不容易出错,但是在效率上可能比EPOLLET要低一些。 libevent包括事件管理、缓存管理、DNS、HTTP、缓存事件几大部分。事件管理包括各种IO(socket)、定时器、信号等事件;缓存管理是指evbuffer功能;DNS是libevent提供的一个异步DNS查询功能;HTTP是libevent的一个轻量级http实现,包括服务器和客户端。libevent也支持ssl,这对于有安全需求的网络程序非常的重要,但是其支持不是很完善,比如http server的实现就不支持ssl。

阅读全文

backlog

TCP建立连接是要进行三次握手,但是否完成三次握手后,服务器就处理(accept)呢?

阅读全文

SYN-Cookie

预防半连接攻击,SYN-Cookie是一种有效的机制,它的基本原理非常简单,那就是“完成三次握手前不为任何一个连接分配任何资源 1.编码信息 将一些本应该在本地保存的信息编码到返回给客户端的SYN-ACK的初始化序列号或者时间戳里面。握手尚未完成不分配任何资源(Linux即不分配request结构体)。 2.解码信息 等到客户端的ACK最终到来的时候,再从ACK序列号里面解码出保存的信息。 3.建立连接 利用第2步解码出来的信息建立一个TCP连接,此时因为握手已经完成,可以分配资源了。 编码过程图示 解码过程图示 通过上面的编码解码过程中好像没有什么check/compare操作,一般而言,对于类似HASH或者摘要的算法,都需要对信息进行比对,比如对一段信息生产一个摘要,为了确保该信息没有被篡改,需要再次使用相同的算法生成摘要,如果两段摘要的值不同,说明信息被篡改了!对于上面的算法,在生产Cookie的时候,我们注意到使用hash算法对元组生产了一个值,但是对于解码的过程,它并没有再次计算这个值与原始携带的值做比对,这样合理吗?         这事实上是Linux的一个hack!Linux将一段data做了限定,比如它的值严格在0-7之间,将这个data一同参与运算,而不是仅仅将其编码到固定的某几个bit,算法寄希望于:如果数据是伪造的或者被篡改了,那么解码出来的data的值仍然处在规定的严格区间里的可能性微乎其微! 24比特数据的编码和解码的过程:

阅读全文

SO_REUSEADDR SO_REUSEPORT

每次kill掉该服务器进程并重新启动的时候,都会出现bind错误:error:98,Address already in use。然而再kill掉该进程,再次重新启动的时候,就bind成功了。 端口正在被使用,并处于TCP中的TIME_WAIT状态。再过两分钟,我再执行命令netstat -an|grep 9877

阅读全文

lsof netstat

lsof命令只能以root的权限执行 用法如下:

  1. 显示开启文件abc.txt的进程 lsof abc.txt
  2. 显示22端口现在被什么程序占用 lsof -i 22
  3. 显示abc进程现在正在打开的文件 lsof -c abc
  4. 显示归属gid的进程情况 lsof -g gid
  5. 显示指定目录下被进程开启的文件,不会遍历该目录下的所有子目录 lsof +d /usr/local/
  6. 显示指定目录下被进程开启的文件,会遍历该目录下得所有子目录 lsof +D /usr/local/
  7. 显示使用fd为4的进程 lsof -d 4
  8. 不进行域名解析,缺省会进行,比较慢 lsof -n
  9. 查看进程号为12的进程打开了哪些文件 lsof -p 12
  10. 让lsof重复执行,缺省15s刷新 lsof +|-r [t] -r, lsof会永远执行,直到被中断 +r, lsof会一直执行,直到没可显示的内容 Example: 查看目前ftp连接的情况:lsof -i tcp@test.com:ftp -r
  11. 列出打开文件的大小,如果大小为0,则空 lsof -s
  12. 以UID,列出打开的文件 lsof -u username
  13. 显示符合条件的进程情况 语法:lsof -i[46] [protocol][@hostname|hostaddr][:service|port] 46 – IPV4 or IPV6 protocol – TCP or UDP hostname – Internet host name hostaddr – IP地址 service – /etc/service中的service name(可多选) port – 端口号(可多选) Example: TCP:25 - TCP and port 25 @1.1.1.1 - IP 1.1.1.1 tcp@test.com - TCP protocol, ftp service netstat可以不在root权限下运行
  14. 查看进程占用的端口号 netstat -anp [root@nbatest ~]# netstat -anp | grep syslog //进程名 udp 0 0 0.0.0.0:514 0.0.0.0:* 31483/syslogd
    netstat -anp [root@nbatest ~]# netstat -anp | grep 514 //port口 udp 0 0 0.0.0.0:514 0.0.0.0:* 31483/syslogd
阅读全文

TCP序列号欺骗

为了确保端到端的可靠传输,TCP对所发送出的每个数据包都分配序列编号,当对方收到数据包后则向发送方进行确认,接收方利用序列号来确认数据包的先后顺序,并丢弃重复的数据包。TCP序列号在TCP数据包中占32位字节,有发送序列号SEQS和确认序列号SEQA两种,它们分别对应SYN和ACK两个标志。当SYN置1时,表示所发送的数据包的序列号为SEQS ;当ACK置1时,表示接收方准备接收的数据包的序列号为SEQA 。 ① Xser → Server:SYN(SEQS=ISNC);使用User的IP作为源地址 ② Server → User :SYN(SEQS=ISNS),ACK(SEQA=ISNC+1) ③ Xser → Server:ACK(SEQA=ISNS+1);使用User的IP作为源地址 在这里,Xser以User的身份向服务器发送初始序列号,并置SYN=1,请求与服务器建立连接;当服务器收到该请求后,向User返回应答序列号,如果此时User能正常工作,则认为这是一个非法数据包而终止连接,使攻击者的目的落空,否则,攻击者将继续以User的身份向服务器发送已推测出的确认序列号ISNS+1,并与服务器建立连接,进而可在服务器上行使User的权限,执行相应的操作。 使用这种攻击需要具备两个基本条件:一是能推测出序列号ISNS的值;二是所冒充的可信任主机不能正常工作。其中,最关键的是要推测出由服务器返回的序列号ISNS的值。由服务器返回的这个值可能是个随机数,它通常与被信任主机和服务器间的RTT时间有关,必须经过多次采样和统计分析,才可能推测到这个值。通常,可重复多次与被攻击主机的某个端口(如SMTP)建立正常连接,然后断开,并记录每次连接所设定的ISN值。另外,还需要多次测试可信任主机与服务器间的RTT时间

  1. 序列号和确认号的简介及作用
阅读全文

Tcp Keepalive和HTTP Keep-alive

Tcp Keepalive的起源 双方建立交互的连接,但是并不是一直存在数据交互,有些连接会在数据交互完毕后,主动释放连接,而有些不会,那么在长时间无数据交互的时间段内,交互双方都有可能出现掉电、死机、异常重启等各种意外,当这些意外发生之后,这些TCP连接并未来得及正常释放,那么,连接的另一方并不知道对端的情况,它会一直维护这个连接,长时间的积累会导致非常多的半打开连接,造成端系统资源的消耗和浪费,为了解决这个问题,在传输层可以利用TCP的保活报文来实现。

阅读全文

keepalive 连接池

1 连接种类      一般连接主要分为长连接,短连接和http的keepalive连接。

阅读全文

发送接收缓冲区滑动窗口

发送窗口与接收窗口关系

阅读全文

inode 与文件块 block 对应关系

Linux文件访问流程 inode是文件的唯一标识,文件名和inode的对应关系存放在上一级目录的block中;inode里有指向文件block的指针和文件的属性,从而通过block获得文件数据。

阅读全文

socket 虚拟文件系统

在内核中,对socket实现了一种虚拟的文件系统(VFS):socketfs。和其它一般文件系统不同,它不能被mount,没有挂载点,而是通过一个静态变量来引用: [ net/socket.c ] static struct vfsmount *sock_mnt __read_mostly; 在用户空间创建了一个socket后,返回值是一个文件描述符,下面分析一下创建socket时怎么和文件描述符联系的。在SYSCALL_DEFINE3(socket, int, family, int, type, int, protocol)最后调用sock_map_fd进行关联,其中返回的retval就是用户空间获取的文件描述符fd,sock就是调用sock_create创建成功的socket.

阅读全文

screen nohup session

nohup和&的缺点是,如果你要在一个shell会话里面执行多个命令和脚本,那么要每个命令和脚本都要加nohup和&非常麻烦,所以才有了screen和TMUX Screen 命令与 Tmux 命令 另一种思路是使用 terminal multiplexer (终端复用器:在同一个终端里面,管理多个session),典型的就是 Screen 命令和 Tmux 命令。 它们可以在当前 session 里面,新建另一个 session。这样的话,当前 session 一旦结束,不影响其他 session。而且,以后重新登录,还可以再连上早先新建的 session。 kill 调用的是 SIGTERM, 此信号可以被捕获和忽略。 kill -9 调用的是 SIGKILL, 杀掉进程,不能被捕获和忽略。 SIGHUP是在终端被断开时候调用,如果信号没有被处理,进程会终止。

阅读全文

fork cahe

题目:请问下面的程序一共输出多少个“-”? `#include #include <sys/types.h> #include

int main(void) { int i; for(i=0; i<2; i++){ fork(); printf(“-“); }

wait(NULL); wait(NULL);

return 0; }` 如果你对fork()的机制比较熟悉的话,这个题并不难,输出应该是6个“-”,但是,实际上这个程序会很tricky地输出8个“-”。

阅读全文

文件锁

两种经常使用的文件锁:

阅读全文

fd 文件描述符

每一个文件描述符会与一个打开文件相对应,同时,不同的文件描述符也会指向同一个文件。相同的文件可以被不同的进程打开也可以在同一个进程中被多次打开。系统为每一个进程维护了一个文件描述符表,该表的值都是从0开始的,所以在不同的进程中你会看到相同的文件描述符,这种情况下相同文件描述符有可能指向同一个文件,也有可能指向不同的文件。具体情况要具体分析,要理解具体其概况如何,需要查看由内核维护的3个数据结构。 1.进程级的文件描述符表; 2.系统级的打开文件描述符表; 3.文件系统的i-node表。 文件描述符在形式上是一个非负整数。实际上,它是一个索引值,指向内核为每一个进程所维护的该进程打开文件的记录表。当程序打开一个现有文件或者创建一个新文件时,内核向进程返回一个文件描述符。在程序设计中,一些涉及底层的程序编写往往会围绕着文件描述符展开。但是文件描述符这一概念往往只适用于UNIX、Linux这样的操作系统。 在编写文件操作的或者网络通信的软件时,初学者一般可能会遇到“Too many open files”的问题。这主要是因为文件描述符是系统的一个重要资源,虽然说系统内存有多少就可以打开多少的文件描述符,但是在实际实现过程中内核是会做相应的处理的,一般最大打开文件数会是系统内存的10%(以KB来计算)(称之为系统级限制),查看系统级别的最大打开文件数可以使用sysctl -a | grep fs.file-max命令查看。与此同时,内核为了不让某一个进程消耗掉所有的文件资源,其也会对单个进程最大打开文件数做默认值处理(称之为用户级限制),默认值一般是1024,使用ulimit -n命令可以查看。在Web服务器中,通过更改系统默认值文件描述符的最大值来优化服务器是最常见的方式之一 ## 查看默认文件描述符的大小 [root@poe ~]# ulimit -n 1024 临时修改文件描述符的大小 [root@Gin scripts]# ulimit -SHn 65535 [root@Gin scripts]# ulimit -n 65535 永久修改文件描述符的大小: [root@Gin ~]# echo ‘* - nofile 65535’ »/etc/security/limits.conf [root@Gin ~]# tail -n1 /etc/security/limits.conf

    • nofile 65535
阅读全文

fork() execve()

①fork()系统调用: 进程调用fork()创建一个新的进程,新进程复制了父进程的task_struct(PCB,process control block,进程控制块),以及task_struct中的各个子模块,比如内核堆栈等,然后对各个子模块做了修改。系统调用通过eax寄存器保存返回值,fork()系统调用结束后从内核态返回两次,一次是父进程返回,一次是子进程返回,区分父子进程的方法就是看返回值是否为0,若为0,说明返回的是新进程,不为0返回的是父进程。

阅读全文

页、磁盘块与扇区

扇区:磁盘的最小存储单位; 磁盘块:文件系统读写数据的最小单位; 页:内存的最小存储单位;

阅读全文

Bash 带有特殊含义的退出码

exit命令用于退出当前shell,在shell脚本中可以终止当前脚本执行。

阅读全文

FUNCNAME

C/C++中,__FUNCTION__常量记录当前函数的名称。有时候,在日志输出的时候包含这些信息是非常有用的。而在Bash中,同样有这样一个常量FUNCNAME,但是有一点区别是,它是一个数组而非字符串,其中数组的第一个元素为当前函数的名称。

阅读全文

乐观锁悲观锁

乐观锁 在关系数据库管理系统里,乐观并发控制(又名”乐观锁”,Optimistic Concurrency Control,缩写”OCC”)是一种并发控制的方法。它假设多用户并发的事务在处理时不会彼此互相影响,各事务能够在不产生锁的情况下处理各自影响的 那部分数据。在提交数据更新之前,每个事务会先检查在该事务读取数据后,有没有其他事务又修改了该数据。如果其他事务有更新的话,正在提交的事务会进行回 滚。乐观事务控制最早是由孔祥重(H.T.Kung)教授提出。

阅读全文

strace

strace是用来跟踪用户空间进程的系统调用和信号的 系统调用提供用户程序与操作系统之间的接口。操作系统的进程空间分为用户空间和内核空间:

阅读全文

pstack 原理

注意和ptrace(ptrace()系统调用提供了一个方法,该方法使一个程序(追踪者)可以观察和控制另外一个程序(被追踪者)的执行,并检查和改变被追踪者的内存及寄存器。它主要用于实现断点调试和追踪系统调用。GDB的工作机制)区分

和jstack一样, pstack亦能展现进程的线程堆栈快照, 非常方便验证和性能评估. pstack的作用, 大致可以归纳如下:   1). 查看线程数(比pstree, 包含了详细的堆栈信息)   2). 能简单验证是否按照预定的调用顺序/调用栈执行   3). 采用高频率多次采样使用时, 能发现程序当前的阻塞在哪里, 以及性能消耗点在哪里?   4). 能反映出疑似的死锁现象(多个线程同时在wait lock, 具体需要进一步验证)   当然还能举例更多的作用, 相信使用过jstack的coder, 必然深以为然.

阅读全文

Linux进程间通信-消息队列(mqueue)

消息队列的实现分为两种,一种为System V的消息队列,一种是Posix消息队列; 消息队列可以认为是一个消息链表,某个进程往一个消息队列中写入消息之前,不需要另外某个进程在该队列上等待消息的达到,这一点与管道和FIFO相反。Posix消息队列与System V消息队列的区别如下:

阅读全文

kill

  1. kill与signals 我们这里所说的kill是指作为shell command的那个kill(相对地,linux系统中还有个叫做kill的system call, man 2 kill可查看其功能及用法),shell终端中输入man kill可以看到,kill的作用是向某个指定的进程或进程组发送指定信号,从而结束该进程/进程组。-s选项可以指定要发送的具体信号,如果没有指定,则默认发送SIGTERM信号至指定进程/进程组,若进程没有捕获该信号的逻辑,则SIGTERM的作用是终止进程。

     kill支持发送的信号列表可以通过kill -l查看,而这些信号的具体含义可以通过man 7 signal查看。在我的机器上,man 7 signal输出的POSIX标准信号如下所示(kill支持的信号还有POSIX没有定义的非标准信号,这里没有摘出,感兴趣的同学可以通过man查看)。
    
阅读全文

火焰图(flame graph)

Brendan D. Gregg 发明了火焰图,可以一针见血的指出程序的性能瓶颈,坏消息是除了OpenResty 社区,很少看到还有其他人使用火焰图。

阅读全文

core dump

有的程序可以通过编译, 但在运行时会出现Segment fault(段错误). 这通常都是指针错误引起的. 但这不像编译错误一样会提示到文件某一行, 而是没有任何信息, 使得我们的调试变得困难起来.

阅读全文

thread apply all bt

Core Dump又叫核心转储, 当程序没有core文件生成怎么办呢?

阅读全文

通配符与正则表达式

通配符适用的地方:shell命令行或者shell脚本中

阅读全文

awk 用法总结

1,awk 大小写敏感

  1. awk命令格式和选项 2.1. awk的语法有两种形式 awk [options] ‘script’ var=value file(s)
阅读全文

futex 快速用户空间互斥体

版内核中出现。

阅读全文

Test And Set Lock

1禁止中断、 2锁内存总线 3Test-and-Set指令 Swap指令 同步互斥 . 互斥与同步的概念 互斥和同步是两个紧密相关而又容易混淆的概念。

阅读全文

timer

内核定时器是内核用来控制在未来某个时间点(基于jiffies)调度执行某个函数的一种机制,其实现位于 <linux/timer.h> 和 kernel/timer.c 文件中。 被调度的函数肯定是异步执行的,它类似于一种“软件中断”,而且是处于非进程的上下文中,所以调度函数必须遵守以下规则: 1) 没有 current 指针、不允许访问用户空间。因为没有进程上下文,相关代码和被中断的进程没有任何联系。 2) 不能执行休眠(或可能引起休眠的函数)和调度。 3) 任何被访问的数据结构都应该针对并发访问进行保护,以防止竞争条件。 内核定时器的调度函数运行过一次后就不会再被运行了(相当于自动注销),但可以通过在被调度的函数中重新调度自己来周期运行。 在SMP系统中,调度函数总是在注册它的同一CPU上运行,以尽可能获得缓存的局域性。 struct timer_list {

阅读全文

timekeeper

  1.  时间的种类 内核管理着多种时间,它们分别是:
阅读全文

jiffies

HZ Linux核心每隔固定周期会发出timer interrupt (IRQ 0),HZ是用来定义每一秒有几次timer interrupts。举例来说,HZ为1000,代表每秒有1000次timer interrupts。 HZ可在编译核心时设定,如下所示(以核心版本2.6.20-15为例): :~$ cd /usr/src/linux :/usr/src/linux$ make menuconfig Processor type and features —> Timer frequency (250 HZ) —> Tick Tick是HZ的倒数,意即timer interrupt每发生一次中断的时间。如HZ为250时,tick为4毫秒(millisecond)。

阅读全文

clock_event_device

早期的内核版本中,进程的调度基于一个称之为tick的时钟滴答,通常使用时钟中断来定时地产生tick信号,每次tick定时中断都会进行进程的统计和调度,并对tick进行计数,记录在一个jiffies变量中,定时器的设计也是基于jiffies。这时候的内核代码中,几乎所有关于时钟的操作都是在machine级的代码中实现,很多公共的代码要在每个平台上重复实现。随后,随着通用时钟框架的引入,内核需要支持高精度的定时器,为此,通用时间框架为定时器硬件定义了一个标准的接口:clock_event_device,machine级的代码只要按这个标准接口实现相应的硬件控制功能,剩下的与平台无关的特性则统一由通用时间框架层来实现。

  1.  时钟事件软件架构 本系列文章的第一节中,我们曾经讨论了时钟源设备:clocksource,现在又来一个时钟事件设备:clock_event_device,它们有何区别?看名字,好像都是给系统提供时钟的设备,实际上,clocksource不能被编程,没有产生事件的能力,它主要被用于timekeeper来实现对真实时间进行精确的统计,而clock_event_device则是可编程的,它可以工作在周期触发或单次触发模式,系统可以对它进行编程,以确定下一次事件触发的时间,clock_event_device主要用于实现普通定时器和高精度定时器,同时也用于产生tick事件,供给进程调度子系统使用。时钟事件设备与通用时间框架中的其他模块的关系如下图所示: 与clocksource一样,系统中可以存在多个clock_event_device,系统会根据它们的精度和能力,选择合适的clock_event_device对系统提供时钟事件服务。在smp系统中,为了减少处理器间的通信开销,基本上每个cpu都会具备一个属于自己的本地clock_event_device,独立地为该cpu提供时钟事件服务,smp中的每个cpu基于本地的clock_event_device,建立自己的tick_device,普通定时器和高精度定时器。 在软件架构上看,clock_event_device被分为了两层,与硬件相关的被放在了machine层,而与硬件无关的通用代码则被集中到了通用时间框架层,这符合内核对软件的设计需求,平台的开发者只需实现平台相关的接口即可,无需关注复杂的上层时间框架。 tick_device是基于clock_event_device的进一步封装,用于代替原有的时钟滴答中断,给内核提供tick事件,以完成进程的调度和进程信息统计,负载平衡和时间更新等操作。 struct clock_event_device 时钟事件设备的核心数据结构是clock_event_device结构,它代表着一个时钟硬件设备,该设备就好像是一个具有事件触发能力(通常就是指中断)的clocksource,它不停地计数,当计数值达到预先编程设定的数值那一刻,会引发一个时钟事件中断,继而触发该设备的事件处理回调函数,以完成对时钟事件的处理。clock_event_device结构的定义如下:
阅读全文

时间轮算法

内核自身的正常运行也依赖于时钟系统。Linux 是一个典型的分时系统,CPU 时间被分成多个时间片,这是多任务实现的基础。Linux 内核依赖 tick,即时钟中断来进行分时。

阅读全文

Shell 脚本中调用另一个 Shell 脚本

fork: 如果脚本有执行权限的话,path/to/foo.sh。如果没有,sh path/to/foo.sh。 exec: exec path/to/foo.sh source: source path/to/foo.sh fork fork 是最普通的, 就是直接在脚本里面用 path/to/foo.sh 来调用 foo.sh 这个脚本,比如如果是 foo.sh 在当前目录下,就是 ./foo.sh。运行的时候 terminal 会新开一个子 Shell 执行脚本 foo.sh,子 Shell 执行的时候, 父 Shell 还在。子 Shell 执行完毕后返回父 Shell。 子 Shell 从父 Shell 继承环境变量,但是子 Shell 中的环境变量不会带回父 Shell。

阅读全文

文件读取流程

使用df -aT命令,发现根目录/对应的文件系统为ext3 Ext3文件系统结构与Ext2相似。用工具格式化磁盘或分区时, 会选择使用什么文件系统来格式化。若选用Ext2来格式化磁盘或分区, 则磁盘或分区的大部分空间被格式化成了许多个Inode和block两个数据结构。block有3种大小选项1K,2K,4K,可在格式化之前选择。Inode的大小固定为128bytes。每个文件都仅会占用一个Inode,Inode主要用来记录文件相关属性,比如权限、文件大小、修改时间等。block用来记录文件的数据,一个Inode可以关联多个block,且Inode记录一个block号码需要4byte。如果文件非常大的话,则Inode无法直接记录文件所包含的所有block号码。所以Ext2采用12个直接、1个间接、1个双间接、和1个三间接记录区,共需60bytes。 12个直接记录区直接指向带有文件数据的block。1个间接记录区指向一个无真实文件数据的block,此block充当Inode的扩展记录区,此block直接指向带有数据的block。若此扩展block为1K,那么它可记录256个block号码。双间接和三间接类似。

文件读取流程 1)通过挂载点信息找到/dev/sda2的inode号码为2,对应根目录/ 2) 经过上个步骤,由于owner root有r,w,x权限,可从inode取得根目录/的block,然后再从block中取得etc/目录的inode为3303105。这里etc/相当于根目录/的数据。

阅读全文

autoconf和automake

使用autoconf和automake两个工具来帮助我们自动地生成符合自由软件惯例的Makefile,这样就可以象常见的GNU程序一样,只要使用“./configure”,“make”,“make instal”就可以把程序安装到Linux系统中去了。这将特别适合想做开放源代码软件的程序开发人员,又或如果你只是自己写些小的Toy程序,那么这个文章对你也会有很大的帮助。   一、Makefile介绍   Makefile是用于自动编译和链接的,一个工程有很多文件组成,每一个文件的改变都会导致工程的重新链接,但是不是所有的文件都需要重新编译,Makefile中纪录有文件的信息,在make时会决定在链接的时候需要重新编译哪些文件。   Makefile的宗旨就是:让编译器知道要编译一个文件需要依赖其他的哪些文件。当那些依赖文件有了改变,编译器会自动的发现最终的生成文件已经过时,而重新编译相应的模块。   Makefile的基本结构不是很复杂,但当一个程序开发人员开始写Makefile时,经常会怀疑自己写的是否符合惯例,而且自己写的Makefile经常和自己的开发环境相关联,当系统环境变量或路径发生了变化后,Makefile可能还要跟着修改。这样就造成了手工书写Makefile的诸多问题,automake恰好能很好地帮助我们解决这些问题。   使用automake,程序开发人员只需要写一些简单的含有预定义宏的文件,由autoconf根据一个宏文件生成configure,由automake根据另一个宏文件生成Makefile.in,再使用configure依据Makefile.in来生成一个符合惯例的Makefile。下面我们将详细介绍Makefile的automake生成方法。   二、使用的环境   本文所提到的程序是基于Linux发行版本:Fedora Core release 1,它包含了我们要用到的autoconf,automake。   三、从helloworld入手   我们从大家最常使用的例子程序helloworld开始。   下面的过程如果简单地说来就是:   新建三个文件:  helloworld.c  configure.in  Makefile.am   然后执行:autoscan; aclocal; autoconf; automake –add-missing; ./configure; make; ./helloworld;   就可以看到Makefile被产生出来,而且可以将helloworld.c编译通过。很简单吧,几条命令就可以做出一个符合惯例的Makefile,感觉如何呀。现在开始介绍详细的过程:   1、建目录   在你的工作目录下建一个helloworld目录,我们用它来存放helloworld程序及相关文件,如在/home/my/build下: $ mkdir helloword $ cd helloworld

阅读全文

bash 2>&1

“命令>/dev/null 2>&1 ”等价于“命令 &>/dev/null ” 我们在Linux下经常会碰到nohup command>/dev/null 2>&1 &这样形式的命令。首先我们把这条命令大概分解下首先就是一个nohup表示当前用户和系统的回话下的进城忽略响应HUP消息。&是把该命令以后台的job的形式运行。那么就剩下command>/dev/null 2>&1,command>/dev/null较好理解,/dev/null表示一个空设备,就是说吧command的执行结果重定向到空设备中,说白了就是不显示任何信息。那么2>&1又是什么含义? 2>&1 几个基本符号及其含义 /dev/null 表示空设备文件0 表示stdin标准输入1 表示stdout标准输出2 表示stderr标准错误从command>/dev/null说起 其实这条命令是一个缩写版,对于一个重定向命令,肯定是a > b这种形式,那么command > /dev/null难道是command充当a的角色,/dev/null充当b的角色。这样看起来比较合理,其实一条命令肯定是充当不了a,肯定是command执行产生的输出来充当a,其实就是标准输出stdout。所以command > /dev/null相当于执行了command 1 > /dev/null。执行command产生了标准输出stdout(用1表示),重定向到/dev/null的设备文件中。 说说2>&1 通过上面command > /dev/null等价于command 1 > /dev/null,那么对于2>&1也就好理解了,2就是标准错误,1是标准输出,那么这条命令不就是相当于把标准错误重定向到标准输出么。等等是&1而不是1,这里&是什么?这里&相当于等效于标准输出。这里有点不好理解,先看下面。 command>a 2>a 与 command>a 2>&1的区别 通过上面的分析,对于command>a 2>&1这条命令,等价于command 1>a 2>&1可以理解为执行command产生的标准输入重定向到文件a中,标准错误也重定向到文件a中。那么是否就说command 1>a 2>&1等价于command 1>a 2>a呢。其实不是,command 1>a 2>&1与command 1>a 2>a还是有区别的,区别就在于前者只打开一次文件a,后者会打开文件两次,并导致stdout被stderr覆盖。&1的含义就可以理解为用标准输出的引用,引用的就是重定向标准输出产生打开的a。从IO效率上来讲,command 1>a 2>&1比command 1>a 2>a的效率更高。 举个栗子 来个shell //test.sh #!/bin/sh t date chmod +x test.sh为test.sh增加执行权限。这里我们弄了两条命令,其中t指令并不存在,执行会报错,会输出到stderr。date能正常执行,执行会输出当前时间,会输出到stdout。 执行./test.sh > res1.log结果为 我们发现stderr并没有被重定向到res1.log中,stderr被打印到了屏幕上。这也进一步证明了上面说的./test.sh > res1.log等价于./test.sh 1>res1.log 执行./test.sh>res2.log 2>&1结果为 这次我们发现stdout和stderr都被重定向到了res2.log中了。上面我们未对stderr也就是2说明如何输出,stderr就输出到了屏 幕上,这里我们不仅对stdout进行说明,重定向到res2.log中,对标准错误也进行了说明,让其重定向到res2.log的引用即 res2.log的文件描述符中。 再思考一下 为何2>&1要写在command>1的后面,直接用2可以么。比如ls 2>a。其实这种用法也是可以的,ls命令列出当前的目录,用stdout(1)表示,由于这个时候没有stderr(2),这个时候执行ls 2>a也会正常产生一个a的文件,但是a的文件中是空的,因为这时候执行ls并没有产生stderr(2)。

阅读全文

uninterruptible D 状态

D状态即无法中断的休眠进程,是由于在等待IO,比如磁盘IO,网络IO,其他外设IO,如果进程正在等待的IO在较长的时间内都没有响应,那么就被ps看到了,同时也就意味着很有可能有IO出了问题,可能是外设本身出了故障,也可能是比如挂载的远程文件系统已经不可访问等操作时出现的问题。

阅读全文

LSF、BPF、eBPF

概览LSF(Linuxsocketfilter)起源于BPF(BerkeleyPacketFilter),基础从架构一致,但使用更简单。LSF内部的BPF最早是cBPF(classic),后来x86平台首先切换到eBPF(extended),但由于很多上层应用程序仍然使用cBPF(tcpdump、iptables),并且eBPF还没有支持很多平台,所以内核提供了从cBPF向eBPF转换的逻辑,并且eBPF在设计的时候也是沿用了很多cBPF的指令编码。但是在指令集合寄存器,还有 概览 LSF(Linux socket filter)起源于BPF(Berkeley Packet Filter),基础从架构一致,但使用更简单。LSF内部的BPF最早是cBPF(classic),后来x86平台首先切换到eBPF(extended),但由于很多上层应用程序仍然使用cBPF(tcpdump、iptables),并且eBPF还没有支持很多平台,所以内核提供了从cBPF向eBPF转换的逻辑,并且eBPF在设计的时候也是沿用了很多cBPF的指令编码。但是在指令集合寄存器,还有架构设计上有很大不同(例如eBPF已经可以调用C函数,并且可以跳转到另外的eBPF程序)。 但是新的eBPF一出来就被玩坏了,人们很快发现了它在内核trace方面的意义,它可以保证绝对安全的获取内核执行信息。是内核调试和开发者的不二选择,所以针对这个方面,例如kprobe、ktap、perf eBPF等优秀的工作逐渐产生。反而包过滤部门关注的人不够多。tc(traffic controll)是使用eBPF的一角优秀的用户端程序,它允许不用重新编译模块就可以动态添加删除新的流量控制算法。netfilter的xtable模块,配合xt_bpf模块,就可以实现将eBPF程序添加到hook点,来实现过滤。当然,内核中提供了从cBPF到eBPF编译的函数,所以,任何情况下想要使用cBPF都可以,内核会自动检测和编译。

阅读全文

UNIX Domain Socket

UNIX Domain Socket, 简称UDS, UDS的优势:

阅读全文

ptrace

引子: 1.在Linux系统中,进程状态除了我们所熟知的TASK_RUNNING,TASK_INTERRUPTIBLE,TASK_STOPPED等,还有一个TASK_TRACED。这表明这个进程处于什么状态? 2.strace可以方便的帮助我们记录进程所执行的系统调用,它是如何跟踪到进程执行的? 3.gdb是我们调试程序的利器,可以设置断点,单步跟踪程序。它的实现原理又是什么?

阅读全文

LD_PRELOAD/DYLD_INSERT_LIBRARIES libc hook

一、LD_PRELOAD是什么 LD_PRELOAD是Linux系统的一个环境变量,它可以影响程序的运行时的链接(Runtime linker),它允许你定义在程序运行前优先加载的动态链接库。这个功能主要就是用来有选择性的载入不同动态链接库中的相同函数。通过这个环境变量,我们可以在主程序和其动态链接库的中间加载别的动态链接库,甚至覆盖正常的函数库。一方面,我们可以以此功能来使用自己的或是更好的函数(无需别人的源码),而另一方面,我们也可以以向别人的程序注入程序,从而达到特定的目的。 编译、设置指令 gcc mystrcmp.c -fPIC -shared -o libmystrcmp.so #编译动态链接库 gcc myverifypasswd.c -L. -lmystrcmp -o myverifypasswd #编译主程序 export LD_LIBRARY_PATH=/home/LD_PRELOAD #指定动态链接库所在目录位置 ldd myverifypasswd #显示、确认依赖关系 ./myverifypasswd #运行主程序myverifypasswd 二、LD_PRELOAD运用总结 定义与目标函数完全一样的函数,包括名称、变量及类型、返回值及类型等 将包含替换函数的源码编译为动态链接库 通过命令 export LD_PRELOAD=”库文件路径”,设置要优先替换动态链接库 如果找不替换库,可以通过 export LD_LIBRARY_PATH=库文件所在目录路径,设置系统查找库的目录 替换结束,要还原函数调用关系,用命令unset LD_PRELOAD 解除 想查询依赖关系,可以用ldd 程序名称 LD_PRELOAD: 在Unix操作系统的动态链接库的世界中,LD_PRELOAD就是这样一个环境变量 用以指定预先装载的一些共享库或目标文件,且无论程序是否依赖这些共享库或者文件,LD_PRELOAD指定的这些文件都会被装载 其优先级比LD_LIBRARY_PATH自定义的进程的共享库查找路径的执行还要早 全局符号介入 指在不同的共享库(对象)中存在同名符号时,一个共享对象中的全局符号被另一个共享对象的同名全局符号覆盖 因为LD_PRELOAD指定的共享库或者目标文件的装载顺序十分靠前,几乎是程序运行最先装载的,所以其中的全局符号如果和后面的库中的全局符号重名的话,就会覆盖后面装载的共享库或者目标文件中的全局符号。 因为装载顺序和全局符号介入的原理 它可以影响程序的运行时的链接(Runtime linker),它允许你定义在程序运行前优先加载的动态链接库。 这个功能主要就是用来有选择性的载入Unix操作系统不同动态链接库中的相同函数。通过这个环境变量,我们可以在主程序和其动态链接库的中间加载别的动态链接库,甚至覆盖正常的函数库。

阅读全文

futex

futex 设计成用户空间快速锁操作,由用户空间实现fastpath,以及内核提供锁竞争排队仲裁服务,由用户空间使用futex系统调用来实现slowpath。futex系统调用提供了三种配对的调用接口,满足不同使用场合的,分别为noraml futex,pi-futex,以及 requeue-pi。

阅读全文

strace 原理

1.在Linux系统中,进程状态除了我们所熟知的TASK_RUNNING,TASK_INTERRUPTIBLE,TASK_STOPPED等,还有一个TASK_TRACED。这表明这个进程处于什么状态? 2.strace可以方便的帮助我们记录进程所执行的系统调用,它是如何跟踪到进程执行的? 3.gdb是我们调试程序的利器,可以设置断点,单步跟踪程序。它的实现原理又是什么?

阅读全文

libevent

Memcached中的网络部分就是基于libevent完成的,其中的多线程模型就是典型的消息通知+同步层机制。 libevent是一个轻量级的基于事件驱动的高性能的开源网络库,并且支持多个平台,对多个平台的I/O复用技术进行了封装,当我们编译库的代码时,编译的脚本将会根据OS支持的处理事件机制,来编译相应的代码,从而在libevent接口上保持一致。

阅读全文

c-ares dns 异步请求库

https://github.com/c-ares/c-ares 是一个C语言实现的异步请求DNS的实现。很多知名 软件(curl、seastar、gevent、Nodejs等等)都使用了该软件。

阅读全文

makefile 及其工作原理

Make程序最初设计是为了维护C程序文件防止不必要的重新编译。在使用命令行编译器的时候,修改了一个工程中的头文件,如何确保包含这个头文件的所有文件都得到编译?现在10机的版本生成是使用批处理程序,编译那些文件依赖于程序的维护者,在模块之间相互引用头文件的情况下,要将所有需要重新编译的文件找出来是一件痛苦的事情;在找到这些文件之后,修改批处理进行编译。实际上这些工作可以让make程序来自动完成,make工具对于维护一些具有相互依赖关系的文件特别有用,它对文件和命令的联系(在文件改变时调用来更新其它文件的程序)提供一套编码方法。Make工具的基本概念类似于Proglog语言,你告诉make需要做什么,提供一些规则,make来完成剩下的工作。 make工作自动确定工程的哪部分需要重新编译,执行命令去编译它们。虽然make多用于C程序,然而只要提供命令行的编译器,你可以将其用于任何语言。实际上,make工具的应用范围不仅于编程,你可以描述任和一些文件改变需要自动更新另一些文件的任务来使用它。

规则简介 makefile中的规则是这样的: TARGET … : DEPENDENCIES … COMMAND … 目标(TARGET)程序产生的文件,如可执行文件和目标文件;目标也可以是要执行的动作,如“clean”。 依赖(DEPENDENCIES)是用来产生目标的输入文件,一个目标通常依赖于多个文件。 命令(COMMAND)是make执行的动作,一个可以有多个命令,每个占一行。注意:每个命令行的起始字符必须为TAB字符! 有依赖关系规则中的命令通常在依赖文件变化时负责产生target文件,make执行这些命令更新或产生target。规则可以没有依赖关系,如包含target “clean”的规则。 规则解释如何和何时重做该规则中的文件,make根据依赖关系执行产生或更新目标;规则也说明如何和何时执行动作。有的规则看起来很复杂,但都符合上述模式。

阅读全文

spinlock 自旋锁

自旋锁是专为防止多处理器并发而引入的一种锁,它在内核中大量应用于中断处理等部分(对于单处理器来说,防止中断处理中的并发可简单采用关闭中断的方式,即在标志寄存器中关闭/打开中断标志位,不需要自旋锁)。 何谓自旋锁?它是为实现保护共享资源而提出一种锁机制。其实,自旋锁与互斥锁比较类似,它们都是为了解决对某项资源的互斥使用。无论是互斥锁,还是自旋锁,在任何时刻,最多只能有一个保持者,也就说,在任何时刻最多只能有一个执行单元获得锁。但是两者在调度机制上略有不同。对于互斥锁,如果资源已经被占用,资源申请者只能进入睡眠状态。但是自旋锁不会引起调用者睡眠,如果自旋锁已经被别的执行单元保持,调用者就一直循环在那里看是否该自旋锁的保持者已经释放了锁,”自旋”一词就是因此而得名。 跟互斥锁一样,一个执行单元要想访问被自旋锁保护的共享资源,必须先得到锁,在访问完共享资源后,必须释放锁。如果在获取自旋锁时,没有任何执行单元保持该锁,那么将立即得到锁;如果在获取自旋锁时锁已经有保持者,那么获取锁操作将自旋在那里,直到该自旋锁的保持者释放了锁。由此我们可以看出,自旋锁是一种比较低级的保护数据结构或代码片段的原始方式,这种锁可能存在两个问题: 死锁。试图递归地获得自旋锁必然会引起死锁:递归程序的持有实例在第二个实例循环,以试图获得相同自旋锁时,不会释放此自旋锁。在递归程序中使用自旋锁应遵守下列策略:递归程序决不能在持有自旋锁时调用它自己,也决不能在递归调用时试图获得相同的自旋锁。此外如果一个进程已经将资源锁定,那么,即使其它申请这个资源的进程不停地疯狂“自旋”,也无法获得资源,从而进入死循环。 过多占用cpu资源。如果不加限制,由于申请者一直在循环等待,因此自旋锁在锁定的时候,如果不成功,不会睡眠,会持续的尝试,单cpu的时候自旋锁会让其它process动不了. 因此,一般自旋锁实现会有一个参数限定最多持续尝试次数. 超出后, 自旋锁放弃当前time slice. 等下一次机会。 由此可见,自旋锁比较适用于锁使用者保持锁时间比较短的情况。正是由于自旋锁使用者一般保持锁时间非常短,因此选择自旋而不是睡眠是非常必要的,自旋锁的效率远高于互斥锁。信号量和读写信号量适合于保持时间较长的情况,它们会导致调用者睡眠,因此只能在进程上下文使用,而自旋锁适合于保持时间非常短的情况,它可以在任何上下文使用。如果被保护的共享资源只在进程上下文访问,使用信号量保护该共享资源非常合适,如果对共享资源的访问时间非常短,自旋锁也可以。但是如果被保护的共享资源需要在中断上下文访问(包括底半部即中断处理句柄和顶半部即软中断),就必须使用自旋锁。自旋锁保持期间是抢占失效的,而信号量和读写信号量保持期间是可以被抢占的。自旋锁只有在内核可抢占或SMP(多处理器)的情况下才真正需要,在单CPU且不可抢占的内核下,自旋锁的所有操作都是空操作。 上面简要介绍了自旋锁的基本原理,以下将给出具体的例子,进一步阐释自旋锁在实际系统中的应用。上面我们已经讲过自旋锁只有在内核可抢占或SMP(多处理器)的情况下才真正需要,下面我们就以SMP为例,来说明为什么要使用自旋锁,以及自旋锁实现的基本算法。 实现编辑 在单处理机环境中可以使用特定的原子级汇编指令swap和test_and_set实现进程互斥,(Swap指令:交换两个内存单元的内容;test_and_set指令取出内存某一单元(位)的值,然后再给该单元(位)赋一个新值,关于为何这两条指令能实现互斥我们不在赘述,读者可以了解其算法) 这些指令涉及对同一存储单元的两次或两次以上操作,这些操作将在几个指令周期内完成,但由于中断只能发生在两条机器指令之间,而同一指令内的多个指令周期不可中断,从而保证swap指令或test_and_set指令的执行不会交叉进行. 但在多处理机环境中情况有所不同,例如test_and_set指令包括“取”、“送”两个指令周期,两个CPU执行test_and_set(lock)可能发生指令周期上的交叉,假如lock初始为0, CPU1和CPU2可能分别执行完前一个指令周期并通过检测(均为0),然后分别执行后一个指令周期将lock设置为1,结果都取回0作为判断临界区空闲的依据,从而不能实现互斥. 如图4-3所示. 为在多CPU环境中利用test_and_set指令实现进程互斥,硬件需要提供进一步的支持,以保证test_and_set指令执行的原子性. 这种支持目前多以“锁总线”(bus locking)的形式提供的,由于test_and_set指令对内存的两次操作都需要经过总线,在执行test_and_set指令之前锁住总线,在执行test_and_set指令后开放总线,即可保证test_and_set指令执行的原子性,用法如下: 算法4-6:多处理机互斥算法(自旋锁算法) do{ b=1; while(b){ lock(bus); b = test_and_set(&lock); unlock(bus); } 临界区 lock = 0; 其余部分 }while(1) 总之,自旋锁是一种对多处理器相当有效的机制,而在单处理器非抢占式的系统中基本上没有作用。自旋锁在SMP系统中应用得相当普遍。在许多SMP系统中,允许多个处理机同时执行目态程序,而一次只允许一个处理机执行操作系统代码,利用一个自旋锁可以很容易实现这种控制.一次只允许一个CPU执行核心代码并发性不够高,若期望核心程序在多CPU之间的并行执行,将核心分为若干相对独立的部分,不同的CPU可以同时进入和执行核心中的不同部分,实现时可以为每个相对独立的区域设置一个自旋锁. 初衷编辑 事实上,自旋锁的初衷就是:在短期间内进行轻量级的锁定。一个被争用的自旋锁使得请求它的线程在等待锁重新可用的期间进行自旋(特别浪费处理器时间),所以自旋锁不应该被持有时间过长。如果需要长时间锁定的话, 最好使用信号量。 1自旋锁实际上是忙等锁 当锁不可用时,CPU一直循环执行“测试并设置”该锁直到可用而取得该锁,CPU在等待自旋锁时不做任何有用的工作,仅仅是等待。因此,只有在占用锁的时间极短的情况下,使用自旋锁才是合理的。当临界区很大或有共享设备的时候,需要较长时间占用锁,使用自旋锁会降低系统的性能。 自旋锁可能导致系统死锁 引发这个问题最常见的情况是递归使用一个自旋锁,即如果一个已经拥有某个自旋锁的CPU 想第二次获得这个自旋锁,则该CPU 将死锁。此外,如果进程获得自旋锁之后再阻塞,也有可能导致死锁的发生。copy_from_user()、copy_to_user()和kmalloc()等函数都有可能引起阻塞,因此在自旋锁的占用期间不能调用这些函数。代码清单7.2 给出了自旋锁的使用实例,它被用于实现使得设备只能被最多一个进程打开。 基本形式编辑 自旋锁的基本形式如下: spin_lock(&mr_lock); //临界区 spin_unlock(&mr_lock); 因为自旋锁在同一时刻只能被最多一个内核任务持有,所以一个时刻只有一个线程允许存在于临界区中。这点很好地满足了对称多处理机器需要的锁定服务。在单处理器上,自旋锁仅仅当作一个设置内核抢占的开关。如果内核抢占也不存在,那么自旋锁会在编译时被完全剔除出内核。 简单的说,自旋锁在内核中主要用来防止多处理器中并发访问临界区,防止内核抢占造成的竞争。另外自旋锁不允许任务睡眠(持有自旋锁的任务睡眠会造成自死锁——因为睡眠有可能造成持有锁的内核任务被重新调度,而再次申请自己已持有的锁),它能够在中断上下文中使用。 死锁:假设有一个或多个内核任务和一个或多个资源,每个内核都在等待其中的一个资源,但所有的资源都已经被占用了。这便会发生所有内核任务都在相互等待,但它们永远不会释放已经占有的资源,于是任何内核任务都无法获得所需要的资源,无法继续运行,这便意味着死锁发生了。自死琐是说自己占有了某个资源,然后自己又申请自己已占有的资源,显然不可能再获得该资源,因此就自缚手脚了。 自旋锁 Linux的的内核最常见的锁是自旋锁。自旋锁最多只能被一个可执行线程持有。如果一个执行线程试图获得一个被已经持有(争用)的自旋锁,那么该线程就会一直进行忙循环-旋转-等待锁重新可用要是锁未被争用,请求锁的执行线程就可以立即得到它,继续执行。

阅读全文

异步io

linux下主要有两套异步IO,一套是由glibc实现的(以下称之为glibc版本)、一套是由linux内核实现,并由libaio来封装调用接口(以下称之为linux版本)。

阅读全文

popen

popen()可以执行shell命令,并读取此命令的返回值;  

阅读全文

零拷贝之splice( )函数和tee( )函数

splice( )函数 在两个文件描述符之间移动数据,同sendfile( )函数一样,也是零拷贝。 函数原型:

阅读全文

splice_sendfile

Linux传统I/O操作是一种缓冲I/O,在数据传输中,操作系统会将 I/O 的数据缓存在文件系统的页缓存中,即操作系统内核缓冲区中。 比如:在网络中传输一个文件时,发送端应用程序会先检查内核缓冲区中有没有需要发送的这个文件的数据,如果没有,则会将这个文件从磁盘拷贝到内核缓冲区中,然后再从内核缓冲区拷贝到应用程序的用户缓冲区,如果应用程序不对数据进行处理或处理完毕之后,再将文件拷贝到内核中的socket发送缓冲区(比如TCP发送缓冲区),待内核socket缓冲区中有足够的数据时,就会把数据发送到网卡上,然后在网络上进行传输 其过程至少发生了四次数据的拷贝,其频繁的读写对CPU的使用和内存的带宽开销是非常大的。

阅读全文

splice

  1. splice函数 #include ssize_t splice(int fd_in, loff_t *off_in, int fd_out, loff_t *off_out, size_t len, unsigned int flags);
阅读全文

sendfile

如今几乎每个人都听说过Linux中所谓的”零拷贝”特性,然而我经常碰到没有充分理解这个问题的人们。因此,我决定写一些文章略微深入的讲述这个问题,希望能将这个有用的特性解释清楚。在本文中,将从用户空间应用程序的角度来阐述这个问题,因此有意忽略了复杂的内核实现。

阅读全文

cgroup

1 什么是cgroup?

阅读全文

Linux系统调用--access

【access系统调用】

阅读全文

ifconfig

阅读全文

time_wait

在服务器的日常维护过程中,会经常用到下面的命令: netstat -n | awk ‘/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}’
它会显示例如下面的信息: TIME_WAIT 814 CLOSE_WAIT 1 FIN_WAIT1 1 ESTABLISHED 634 SYN_RECV 2 LAST_ACK 1

阅读全文

nginx_redis 高并发

Apache与Nginx: Apache与Nginx的性能谁更高效,取决于其服务器的并发策略以及其面对的场景:

阅读全文

multiplexing IO多路复用

1 基础知识回顾 注意:咱们下面说的都是Linux环境下,跟Windows不一样哈~~~

阅读全文

select,poll,epoll,kqueue模型

概况: select() select()和poll()的工作方式非常类似。让我们先快速看一下select()函数

阅读全文

epoll_server 服务端代码

#include #include <sys/socket.h> #include <sys/epoll.h> #include <netinet/in.h> #include <arpa/inet.h> #include #include #include #include

阅读全文

epoll

epoll的核心是3个API,核心数据结构是:1个红黑树 和 1个链表

阅读全文

sysfs

sysfs 文件系统总是被挂载在 /sys 挂载点上。虽然在较早期的2.6内核系统上并没有规定 sysfs 的标准挂载位置,可以把 sysfs 挂载在任何位置,但较近的2.6内核修正了这一规则,要求 sysfs 总是挂载在 /sys 目录上;针对以前的 sysfs 挂载位置不固定或没有标准被挂载,有些程序从 /proc/mounts 中解析出 sysfs 是否被挂载以及具体的挂载点,这个步骤现在已经不需要了。请参考附录给出的 sysfs-rules.txt 文件链接。

阅读全文

proc文件系统

Linux系统上的/proc目录是一种文件系统,即proc文件系统。与其它常见的文件系统不同的是,/proc是一种伪文件系统(也即虚拟文件系统),存储的是当前内核运行状态的一系列特殊文件,用户可以通过这些文件查看有关系统硬件及当前正在运行进程的信息,甚至可以通过更改其中某些文件来改变内核的运行状态。

阅读全文

lkm 可加载内核模块

最初开发 /proc 文件系统是为了提供有关系统中进程的信息。但是由于这个文件系统非常有用,因此内核中的很多元素也开始使用它来报告信息,或启用动态运行时配置。

阅读全文

kobject

<img src="https://xiazemin.github.io/MyBlog/img/kobject.jpeg"/> <!-- more --> 1. kobject
阅读全文

Linux各目录及每个目录的详细介绍

阅读全文

devfs sysfs

linux下有专门的文件系统用来对设备进行管理,devfs和sysfs就是其中两种。

阅读全文

Linux中pam模块

Linux-PAM(linux可插入认证模块)是一套共享库,使本地系统管理员可以随意选择程序的认证方式。换句话说,不用重新编译一个包含PAM功能的应用程序,就可以改变它使用的认证机制。这种方式下,就算升级本地认证机制,也不用修改程序. PAM使用配置/etc/pam.d/下的文件,来管理对程序的认证方式.应用程序 调用相应的配置文件,从而调用本地的认证模块.模块放置在/lib/security下,以加载动态库的形式进,像我们使用su命令时,系统会提示你输入root用户的密码.这就是su命令通过调用PAM模块实现的。

阅读全文

issue 设置linux登录前后的欢迎信息

实现登录消息的功能,可以修改3个文件。

阅读全文

bash_profile等几个文件的执行过程

利用~/.bash_profile: 每个用户都可使用该文件输入专用于自己使用的shell信息,当用户登录时,该文件仅仅执行一次可以实现登录公告 $~/.bash_profile if [ -f ~/.greeting ]; then . ~/.greeting; fi $vi ~/.greeting echo -e “^[[1m^[[4;37;32m公 告:^[[0m”

  1. 在登录Linux时要执行文件的过程如下:
阅读全文

python中os.system、os.popen、subprocess.popen的区别

1.os.system 该函数返回命令执行结果的返回值,system()函数在执行过程中进行了以下三步操作: 1.fork一个子进程; 2.在子进程中调用exec函数去执行命令; 3.在父进程中调用wait(阻塞)去等待子进程结束。 对于fork失败,system()函数返回-1。 由于使用该函数经常会莫名其妙地出现错误,但是直接执行命令并没有问题,所以一般建议不要使用。

阅读全文

sshpass

 在使用ssh登录远程服务器的时候,在执行完ssh user@ip后,要输入登录密码,有时候登录密码记不住,这样以来Ian带来的很多的麻烦,有没有一种在ssh的参数中直接加入密码的方法呢?查看ssh的帮助我们发现ssh命令并不能在参数中制定密码。 usage: ssh [-1246AaCfGgKkMNnqsTtVvXxYy] [-b bind_address] [-c cipher_spec] [-D [bind_address:]port] [-E log_file] [-e escape_char] [-F configfile] [-I pkcs11] [-i identity_file] [-L address] [-l login_name] [-m mac_spec] [-O ctl_cmd] [-o option] [-p port] [-Q query_option] [-R address] [-S ctl_path] [-W host:port] [-w local_tun[:remote_tun]] [user@]hostname

阅读全文

samba

利用samba解决。 在mac机上直接远程连接这个开发机(可以略过跳板机的限制):Finder中,command+k,添加服务器地址,协议使用smb://hostname。 然后在phpstorm上,打开这个远程开发机上的项目。 直接在mac的IDE上搞运行环境上的代码,不用vi,不用再手动上传到开发机,甚至不用保存(ctrl+s等操作不必,因为更改的直接就是远程机器的代码),效率很不错。 分享一段samba配置文件中,怎么分享某个文件夹。 [myshare] ;comment = my share directory path = /home/work/xxx valid users = work write list = work force user = work force group = work public = yes writable = yes printable = no create mask = 0644 当然可以配置多个分享文件夹[xxx] ,只要复制这段配置即可。 值得一提的是,force user 和 force group这个选项比较重要,如果你很在意更改文件后,这个文件的owner和group的话。

阅读全文

pyopen Python subprocess

shkex 模块最常见的用法就是其中的split 函数,split 函数提供了和shell 处理命令行参数时一致的分隔方式

阅读全文

dns

114.114.114.114和8.8.8.8,这两个IP地址都属于公共域名解析服务DNS其中的一部分,而且由于不是用于商业用途的,这两个DNS都很纯净,不用担心因ISP运营商导致的DNS劫持等问题,而且都是免费提供给用户使用的。

阅读全文

bind

你可以考虑使用bind,使用BIND作为服务器软件的DNS服务器约占所有DNS服务器的九成。这是官网:https://www.isc.org/downloads/bind/

阅读全文

supervisor

后台需要持续运行这个二进制文件,保证服务的持续运行。 方案 1: 直接采用 nohup ./app_admin &后台运行方式,该方式存在一个缺点,如果服务器重启后,或者程序运行出错的话,服务就会终止,这种方式不稳定。 方案 2: 采用supervisor进程管理方式守护go语言的二进制文件运行,保证程序的持续运行。 Supervisor(http://supervisord.org/)是用Python开发的一个client/server服务,是Linux/Unix系统下的一个进程管理工具,不支持Windows系统。它可以很方便的监听、启动、停止、重启一个或多个进程。用Supervisor管理的进程,当一个进程意外被杀死,supervisort监听到进程死后,会自动将它重新拉起,很方便的做到进程自动恢复的功能,不再需要自己写shell脚本来控制。 因为Supervisor是Python开发的,安装前先检查一下系统否安装了Python2.4以上版本 superviosr是一个Linux/Unix系统上的进程监控工具,他/她upervisor是一个Python开发的通用的进程管理程序,可以管理和监控Linux上面的进程,能将一个普通的命令行进程变为后台daemon,并监控进程状态,异常退出时能自动重启。不过同daemontools一样,它不能监控daemon进程 supervisor管理进程,是通过fork/exec的方式将这些被管理的进程当作supervisor的子进程来启动,所以我们只需要将要管理进程的可执行文件的路径添加到supervisor的配置文件中就好了。此时被管理进程被视为supervisor的子进程,若该子进程异常中断,则父进程可以准确的获取子进程异常中断的信息,通过在配置文件中设置autostart=ture,可以实现对异常中断的子进程的自动重启。 安装supervisor $ sudo apt-get install supervisor 配置文件 安装完supervisor后,输入以下命令可得到配置文件: $ echo supervisord.conf 或者: $ cat /etc/supervisord/supervisord.conf 配置文件用到几个部分:

阅读全文

dtrace

我们在分析各种系统异常和故障的时候,通常会用到 pstack(jstack) /pldd/ lsof/ tcpdump/ gdb(jdb)/ netstat/vmstat/ mpstat/truss(strace)/iostat/sar/nmon(top)等系列工具,这些工具从某个方面为我们提供了诊断信息。但这些工具常常带有各类“副作用”,比如 truss(见于 AIX/Solaris) 或者 strace(见于 Linux) 能够让我们检测我们应用的系统调用情况,包括调用参数和返回值,但是却会导致应用程序的性能下降;这对于诊断毫秒级响应的计费生产系统来说,影响巨大。 有没有一个工具,能够兼得上述所有工具的优点,又没有副作用呢?答案是有!对于 Solaris/BSD/OS X 系统来说,那就是 DTrace 工具(后来,Linux 也终于有了自己类似的工具,stap)。

阅读全文

zebra

Zebra是一个路由软件包,提供基于TCP/IP路由服务,支持RIPv1, RIPv2, RIPng, OSPFv2, OSPFv3, BGP- 4,

阅读全文

rawip

一、INET协议族说明

阅读全文

ospf

OSPF(Open Shortest Path First开放式最短路径优先)是一个内部网关协议(Interior Gateway Protocol,简称IGP),用于在单一自治系统(autonomous system,AS)内决策路由。是对链路状态路由协议的一种实现,隶属内部网关协议(IGP),故运作于自治系统内部。著名的迪克斯加算法(Dijkstra)被用来计算最短路径树。OSPF分为OSPFv2和OSPFv3两个版本,其中OSPFv2用在IPv4网络,OSPFv3用在IPv6网络。OSPFv2是由RFC 2328定义的,OSPFv3是由RFC 5340定义的。与RIP相比,OSPF是链路状态协议,而RIP是距离矢量协议。 1.OSPF协议的基本原理:

阅读全文

邻居子系统

一:邻居子系统概述 邻居子系统是从物理来说是指在同一个局域网内的终端。从网络拓扑的结构来说,是指他们之间相隔的距离仅为一跳,他们属于同一个突冲域 邻居子系统的作用:它为第三层协议与第二层协议提供地址映射关系。提供邻居头部缓存,加速发包的速度 二:邻居子系统在整个协议栈的地位 发送数据的时候,要在本机进行路由查找,如果有到目的地地址的路径,查看arp缓存中是否存在相应的映射关系,如果没有,则新建邻居项。判断邻居项是否为可用状态。如果不可用。把skb 存至邻居发送对列中,然后将发送arp请求。如果接收到arp应答。则将对应邻居项置为可用。如果在指定时间内末收到响应包,则将对应邻居项置为无效状态。如果邻居更改为可用状态,则把邻居项对应的skb对列中的数据包发送出去 三:流程概述; 发包流程。下面以包送udp数据为例,看看是怎么与邻居子系统相关联的Sendmsg() à ip_route_output()(到路由缓存中查找目的出口)à ip_route_output_slow( 如果缓存中不存在目的项,则到路由表中查找) à ip_build_xmit() à output_maybe_reroute à skb->dst->output()如果至时找到了路由,则根据路由信息分配个dst_entry,并调用arp_bind_neighbour为之绑定邻居 output指针赋值为ip_output 转到执行ip_output ip_output à __ip_finish_output() -à ip_finish_output2() à dst->neighbour->output()现在就转至邻居项的出口函数了。

阅读全文

General Purpose Input Output

控制GPIO的目录/sys/class/gpio /sys/class/gpio/export文件用于通知系统需要导出控制的GPIO引脚编号 /sys/class/gpio/unexport 用于通知系统取消导出 /sys/class/gpio/gpiochipX目录保存系统中GPIO寄存器的信息,包括每个寄存器控制引脚的起始编号base,寄存器名称,引脚总数 导出一个引脚的操作步骤 direction文件,定义输入输入方向,可以通过下面命令定义为输出。direction接受的参数:in, out, high, low。high/low同时设置方向为输出,并将value设置为相应的1/0 value文件是端口的数值,为1或0

阅读全文

流量控制

Traffic Control ,简称TC,主要是在输出端口处建立一个队列进行流量控制,控制的方式是基于路由,亦即基于目的IP地址或目的子网的网络号的流量控制。流量控制器TC, 其基本的功能模块为队列、分类和过滤器。Linux内核中支持的队列有,Class Based Queue ,Token Bucket Flow ,CSZ ,First In First Out ,Priority ,TEQL ,SFQ ,ATM ,RED。这里我们讨论的队列与分类都是基于CBQ(Class Based Queue)的,而过滤器是基于路由(Route)的。

阅读全文

透明网桥

透明网桥是一种即插即用设备,只要把网桥接入局域网,不需要改动硬件和软件,无需设置地址开关,无需装入路由表或参数,网桥就能工作。 透明网桥以混杂方式工作,它接收与之连接的所有LAN传送的每一帧。当一帧到达时,网桥必须决定将其丢弃还是转发。如果要转发,则必须决定发往哪个LAN。这需要通过查询网桥中一张大型散列表里的目的地址而作出决定。该表可列出每个可能的目的地,以及它属于哪一条输出线路(LAN)。在插入网桥之初,所有的散列表均为空。这时若网桥受到一个帧,会采用自学习(self-learning)算法处理收到的帧(这样就逐渐建立起转发表),并且按照转发表把帧转发出去。这种自学习算法的原理并不复杂,若从某个站A发出的帧从接口x进入了某网桥,那么从这个接口出发沿相反方向一定可把一个帧传送到A。所以网桥只要每收到一个帧,就记下其源地址和进入网桥的接口,作为转发表中的一个项目。在建立转发表时是把帧首部中的源地址写在“地址”这一栏的下面。在转发帧时,则是根据收到的帧首部中的目的地址来转发的。这时就把在“地址”栏下面已经记下的源地址当作目的地址,而把记下的进入接口当作转发接口。 如果网络上的每一个站都发送过帧,那么每一个帧的地址最终都会记录在两个网桥的转发表上。 实际上,在网桥的转发表中写入的信息除了地址和接口外,还有帧进入该网桥的时间要登记进入网桥的时间是因为以太网的拓扑可能会经常发生变化,站点也会更换适配器。另外,以太网上的工作站并非总是接通电源的。把每个帧到达网桥的时间登记下来,就可以在转发表中只保留网络拓扑的最新状态信息。具体方法是,网桥中的接口管理软件周期性地扫描转发表中的项目。只要在一定时间以前登记的都要删除。这样就使得网桥中的转发表能反映当前网络的最新拓扑状态。 由此可见,网桥中的转发信息表并非总是包含所有站点的信息。只要某个站点从来都不发送数据,那么在网桥的转发表中就没有这个站点的项目。如果站点A在一段时间内不发送数据,那么在转发表中地址为A的项目就被删除了。 下面是网桥的自学习和转发帧的一般步骤。 网桥收到一帧后先进行自学习。查找转发表中与收到帧的源地址有无相匹配的项目。如果没有,就在转发表中增加一个项目。如果有,则把原有的项目进行更新。 转发帧。查找转发表中与收到帧的源地址有无相匹配的项目。如果没有,则通过所有其他接口进行转发。如果有,则按转发表中给出的接口进行转发。但应注意,若转发表中给出的接口就是该帧进入网桥的接口,则应丢弃这个帧。 逆向学习法 透明网桥采用的算法是逆向学习法(backward learning)。网桥按混杂的方式工作,故它能看见所连接的任一LAN上传送的帧。查看源地址即可知道在哪个LAN上可访问哪台机器,于是在散列表中添上一项。 当计算机和网桥加电、断电或迁移时,网络的拓扑结构会随之改变。为了处理动态拓扑问题,每当增加散列表项时,均在该项中注明帧的到达时间。每当目的地已在表中的帧到达时,将以当前时间更新该项。这样,从表中每项的时间即可知道该机器最后帧到来的时间。网桥中有一个进程定期地扫描散列表,清除时间早于当前时间若干分钟的全部表项。于是,如果从LAN上取下一台计算机,并在别处重新连到LAN上的话,那么在几分钟内,它即可重新开始正常工作而无须人工干预。这个算法同时也意味着,如果机器在几分钟内无动作,那么发给它的帧将不得不散发,一直到它自己发送出一帧为止。 到达帧的路由选择过程取决于发送的LAN(源LAN)和目的地所在的LAN(目的LAN),如下所示: 1、如果源LAN和目的LAN相同,则丢弃该帧。 2、如果源LAN和目的LAN不同,则转发该帧。 3、如果目的LAN未知,则进行扩散。 为了提高可靠性,有人在LAN之间设置了并行的两个或多个网桥,但是,这种配置引起了另外一些问题,因为在拓扑结构中产生了回路,可能引发无限循环。 生成树算法 透明网桥还使用了一个生成树(spanning tree)算法,即互连在一起的网桥在进行彼此通信后,就能找出原来的网络拓扑的一个子集。在这个子集里,整个连通的网络中不存在回路,即在任何两个站之间只有一条路径。 为了得能够反映网络拓扑发生变化时的生成树,在生成树上的根网桥每隔一段时间还要对生成树的拓扑进行更新。 透明网桥的路径选择算法归纳 (1)若目的局域网和源局域网一样,则网桥将该帧删除。 (2)若源局域网和目的局域网是不同的网,则将该帧转发到目的局域网。 (3)若目的局域网不知道,则采用扩散法处理。

阅读全文

tcpdump

tcpdump采用命令行方式对接口的数据包进行筛选抓取,其丰富特性表现在灵活的表达式上。 不带任何选项的tcpdump,默认会抓取第一个网络接口,且只有将tcpdump进程终止才会停止抓包。 例如: shell> tcpdump -nn -i eth0 icmp 下面是详细的tcpdump用法。 1.1 tcpdump选项 它的命令格式为: tcpdump [ -DenNqvX ] [ -c count ] [ -F file ] [ -i interface ] [ -r file ] [ -s snaplen ] [ -w file ] [ expression ]

阅读全文

stp 生成树协议

生成树协议是一种二层管理协议,它通过有选择性地阻塞网络冗余链路来达到消除网络二层环路的目的,同时具备链路的备份功能。 由于生成树协议本身比较小,所以并不像路由协议那样广为人知。但是它却掌管着端口的转发大权—“小树枝抖一抖,上层协议就得另谋生路”。真实情况也确实如此,特别是在和别的协议一起运行的时候,生成树就有可能断了其他协议的报文通路,造成种种奇怪的现象。 生成树协议和其他协议一样,是随着网络的不断发展而不断更新换代的。本文标题中的“生成树协议”是一个广义的概念,并不是特指IEEE 802.1D中定义的STP协议,而是包括STP以及各种在STP基础上经过改进了的生成树协议。 在生成树协议发展过程中,老的缺陷不断被克服,新的特性不断被开发出来。按照大功能点的改进情况,我们可以粗略地把生成树协议的发展过程划分成三代,下面一一道来。 开天辟地的第一代生成树协议: STP/RSTP 在网络发展初期,透明网桥是一个不得不提的重要角色。它比只会放大和广播信号的集线器聪明得多。它会悄悄把发向它的数据帧的源MAC地址和端口号记录下来,下次碰到这个目的MAC地址的报文就只从记录中的端口号发送出去,除非目的MAC地址没有记录在案或者目的MAC地址本身就是多播地址才会向所有端口发送。通过透明网桥,不同的局域网之间可以实现互通,网络可操作的范围得以扩大,而且由于透明网桥具备MAC地址学习功能而不会像Hub那样造成网络报文冲撞泛滥。

阅读全文

NAPI 技术在 Linux 网络驱动上的应用

NAPI是Linux新的网卡数据处理API,据说是由于找不到更好的名字,所以就叫NAPI(New API),在2.5之后引入。简单来说,NAPI是综合中断方式与轮询方式的技术。中断的好处是响应及时,如果数据量较小,则不会占用太多的CPU事件;缺点是数据量大时,会产生过多中断,而每个中断都要消耗不少的CPU时间,从而导致效率反而不如轮询高。轮询方式与中断方式相反,它更适合处理大量数据,因为每次轮询不需要消耗过多的CPU时间;缺点是即使只接收很少数据或不接收数据时,也要占用CPU时间。 NAPI是两者的结合,数据量低时采用中断,数据量高时采用轮询。平时是中断方式,当有数据到达时,会触发中断 处理函数执行,中断处理函数关闭中断开始处理。如果此时有数据到达,则没必要再触发中断了,因为中断处理函 数中会轮询处理数据,直到没有新数据时才打开中断。 很明显,数据量很低与很高时,NAPI可以发挥中断与轮询方式的优点,性能较好。如果数据量不稳定,且说高不高 说低不低,则NAPI则会在两种方式切换上消耗不少时间,效率反而较低一些。 来看下NAPI和非NAPI的区别: (1) 支持NAPI的网卡驱动必须提供轮询方法poll()。 (2) 非NAPI的内核接口为netif_rx(),NAPI的内核接口为napi_schedule()。 (3) 非NAPI使用共享的CPU队列softnet_data->input_pkt_queue,NAPI使用设备内存(或者 设备驱动程序的接收环)。 (1) NAPI设备结构 /* Structure for NAPI scheduling similar to tasklet but with weighting */

struct napi_struct {
/* The poll_list must only be managed by the entity which changes the * state of the NAPI_STATE_SCHED bit. This means whoever atomically * sets that bit can add this napi_struct to the per-cpu poll_list, and * whoever clears that bit can remove from the list right before clearing the bit. /
struct list_head poll_list; /
用于加入处于轮询状态的设备队列 /
unsigned long state; /
设备的状态 /
int weight; /
每次处理的最大数量,非NAPI默认为64 /
int (
poll) (struct napi_struct , int); / 此设备的轮询方法,非NAPI为process_backlog() /
#ifdef CONFIG_NETPOLL

#endif
unsigned int gro_count;
struct net_device *dev;
struct list_head dev_list;
struct sk_buff *gro_list;
struct sk_buff *skb;
};
(2) 初始化 初始napi_struct实例。 void netif_napi_add(struct net_device *dev, struct napi_struct *napi,
int (
poll) (struct napi_struct , int), int weight)
{
INIT_LIST_HEAD(&napi->poll_list);
napi->gro_count = 0;
napi->gro_list = NULL;
napi->skb = NULL;
napi->poll = poll; /
设备的poll函数 /
napi->weight = weight; /
设备每次poll能处理的数据包个数上限 */

list_add(&napi->dev_list, &dev->napi_list); /* 加入设备的napi_list */  
napi->dev = dev; /* 所属设备 */     #ifdef CONFIG_NETPOLL  
spin_lock_init(&napi->poll_lock);  
napi->poll_owner = -1;    #endif  
set_bit(NAPI_STATE_SCHED, &napi->state); /* 设置NAPI标志位 */   }   (3) 调度 在网卡驱动的中断处理函数中调用napi_schedule()来使用NAPI。 /**   * napi_schedule - schedule NAPI poll   * @n: napi context   * Schedule NAPI poll routine to be called if it is not already running.   */    static inline void napi_schedule(struct napi_struct *n)   {  
/* 判断是否可以调度NAPI */  
if (napi_schedule_prep(n))  
    __napi_schedule(n);   }   判断NAPI是否可以调度。如果NAPI没有被禁止,且不存在已被调度的NAPI, 则允许调度NAPI,因为同一时刻只允许有一个NAPI poll instance。 /**   * napi_schedule_prep - check if napi can be scheduled   * @n: napi context   * Test if NAPI routine is already running, and if not mark it as running.   * This is used as a condition variable insure only one NAPI poll instance runs.   * We also make sure there is no pending NAPI disable.   */  

static inline int napi_schedule_prep(struct napi_struct *n)
{
return !napi_disable_pending(n) && !test_and_set_bit(NAPI_STATE_SCHED, &n->state);
}

static inline int napi_disable_pending(struct napi_struct *n)
{
return test_bit(NAPI_STATE_DISABLE, &n->state);
}

enum {
NAPI_STATE_SCHED, /* Poll is scheduled /
NAPI_STATE_DISABLE, /
Disable pending /
NAPI_STATE_NPSVC, /
Netpoll - don’t dequeue from poll_list */
};
NAPI的调度函数。把设备的napi_struct实例添加到当前CPU的softnet_data的poll_list中,

阅读全文

ethereal

Ethereal (Ethereal:A Network Packet Sniffing Tool)是当前较为流行的一种计算机网络调试和数据包嗅探软件。Ethereal 基本类似于tcpdump,但Ethereal 还具有设计完美的 GUI 和众多分类信息及过滤选项。用户通过 Ethereal,同时将网卡插入混合模式,可以查看到网络中发送的所有通信流量。 Ethereal 应用于故障修复、分析、软件和协议开发以及教育领域。它具有用户对协议分析器所期望的所有标准特征,并具有其它同类产品所不具备的有关特征。

阅读全文

inotify 监控 Linux 文件系统事件

从文件管理器到安全工具,文件系统监控对于的许多程序来说都是必不可少的。从 Linux 2.6.13 内核开始,Linux 就推出了 inotify,允许监控程序打开一个独立文件描述符,并针对事件集监控一个或者多个文件,例如打开、关闭、移动/重命名、删除、创建或者改变属性。在后期的内核中有了很多增强,因此在依赖这些特性之前,请先检查您的内核版本。

阅读全文

fsnotify

linux的2.6.11内核之后有了inotify,这个特性确实很不错,使得很多用户策略得以实现,但是这个特性的代码实现却不是那么好,说实话很乱,很难扩展,很多链表,抽象出的dev结构也不是那么符合逻辑,只不过是为了将各种杂乱的数据结合在一起的勉强罢了。近期由于换工作一直没有关注kernel方面的更新,今天在新工作敲定以后终于有时间看看kernel最新的进展了,发现2.6.31版本的内核中对notify进行了一番大的改动,将原来的inotify和dnotify这两个不想关的特性进行了抽象,将它们的共同的点抽象成了一个基础设施,这个基础设施就是fsnotify.

阅读全文

iptables

ptables是linux自带的一款开源的内核级基于数据包过滤的防火墙。利用iptables可以实现诸如数据包过滤、转发等功能。 iptables包含表,表中包含链,链中包含规则。(规则指的是一条条用于过滤数据包的语句) iptables共有四个表五个链,简称四表五链。 四表指的是filter, nat, mangle, raw。其中前2个是用的最多的,后面2个用的较少,raw可以说基本不怎么使用。 五链指的是INPUT, OUTPUT, FORWARD, PREROUTING, POSTROUTING。(注意,链名用大写。) filter表 过滤数据包,用于本机防火墙,这是默认表。 包含的三个链, INPUT 链:用于过滤所有目标地址是本机的数据包 OUTPUT 链:用于过滤所有本机产生的数据包 FORWARD链:用于过滤所有路过本机的数据包

阅读全文

veth

Linux container 中用到一个叫做veth的东西,这是一种新的设备,专门为 container 所建。veth 从名字上来看是 Virtual ETHernet 的缩写,它的作用很简单,就是要把从一个 network namespace 发出的数据包转发到另一个 namespace。veth 设备是成对的,一个是 container 之中,另一个在 container 之外,即在真实机器上能看到的。 VETH设备总是成对出现,送到一端请求发送的数据总是从另一端以请求接受的形式出现。创建并配置正确后,向其一端输入数据,VETH会改变数据的方向并将其送入内核网络子系统,完成数据的注入,而在另一端则能读到此数据。(Namespace,其中往veth设备上任意一端上RX到的数据,都会在另一端上以TX的方式发送出去)veth工作在L2数据链路层,veth-pair设备在转发数据包过程中并不串改数据包内容。 显然,仅有veth-pair设备,容器是无法访问网络的。因为容器发出的数据包,实质上直接进入了veth1设备的协议栈里。如果容器需要访问网络,需要使用bridge等技术,将veth1接收到的数据包通过某种方式转发出去 创建veth的命令如下: ip link add name veth0 type veth0 peer name veth1 veth设备特点 veth和其它的网络设备都一样,一端连接的是内核协议栈 veth设备是成对出现的,另一端两个设备彼此相连 一个设备收到协议栈的数据发送请求后,会将数据发送到另一个设备上去 常用命令 创建network namespace # ip netns add sunldnamespace01 # ip netns list sunldnamespace01 创建veth # ip link add sunldveth01 type veth peer name sunldveth02 # ip link list 显示信息如下: 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000 link/ether 00:0c:29:2d:d4:23 brd ff:ff:ff:ff:ff:ff 20: sunldveth02: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000 link/ether c6:bb:c0:d0:54:71 brd ff:ff:ff:ff:ff:ff 21: sunldveth01: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000 link/ether da:a1:36:d1:3b:36 brd ff:ff:ff:ff:ff:ff 添加网卡到namespace # ip link set sunldveth01 netns sunldnamespace01 查看当前namespace中的veth,只有sunldveth02 # ip link list 显示信息如下 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000 link/ether 00:0c:29:2d:d4:23 brd ff:ff:ff:ff:ff:ff 3: virbr0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN mode DEFAULT group default link/ether ba:d9:d4:48:55:65 brd ff:ff:ff:ff:ff:ff 20: sunldveth02: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000 link/ether c6:bb:c0:d0:54:71 brd ff:ff:ff:ff:ff:ff 通过命令查看sunldnamespace01中的veth # ip netns exec sunldnamespace01 ip link list 显示信息如下 1: lo: mtu 65536 qdisc noop state DOWN mode DEFAULT group default link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 21: sunldveth01: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000 link/ether da:a1:36:d1:3b:36 brd ff:ff:ff:ff:ff:ff 配置network namespace的网口 # ip netns exec sunldnamespace01 ifconfig sunldveth01 100.2.96.2/16 up # ip netns exec sunldnamespace01 ip addr list 显示信息如下 1: lo: mtu 65536 qdisc noop state DOWN group default link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 21: sunldveth01: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc pfifo_fast state DOWN group default qlen 1000 link/ether da:a1:36:d1:3b:36 brd ff:ff:ff:ff:ff:ff inet 100.2.96.2/16 brd 100.2.255.255 scope global sunldveth01 valid_lft forever preferred_lft forever 开启空间脚本 #可以使用这条命令开启一个 ns0 的 shell ip netns exec ns0 sh network namespace 创建network namespace # ip netns add blue # ip netns list blue 添加网口到namespace 先创建veth # ip link add veth0 type veth peer name veth1 在当前namespace可以看到veth0和veth1 # ip link list 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000 link/ether 00:0c:29:b2:cf:72 brd ff:ff:ff:ff:ff:ff 3: veth1: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN qlen 1000 link/ether ae:0d:00:e1:11:38 brd ff:ff:ff:ff:ff:ff 4: veth0: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN qlen 1000 link/ether 42:e7:50:d4:bb:c5 brd ff:ff:ff:ff:ff:ff 将veth1加到namespace “blue” # ip link set veth1 netns blue 此时,当前namepapce只能看到veth0。 通过如下命令可以查看blue namespace的网口 # ip netns exec blue ip link list 配置network namespace的网口 通过ip netns exec可以配置namespace的网口 # ip netns exec blue ifconfig veth1 172.17.42.2/16 up network namespace的网口与物理网卡的通信通过bridge来实现。 # add the namespaces ip netns add ns1 ip netns add ns2 # create the veth pair ip link add tap1 type veth peer name tap2 # move the interfaces to the namespaces ip link set tap1 netns ns1 ip link set tap2 netns ns2 # bring up the links ip netns exec ns1 ip link set dev tap1 up ip netns exec ns2 ip link set dev tap2 up

阅读全文

linux namespace

Linux Namespace是Linux提供的一种内核级别环境隔离的方法。不知道你是否还记得很早以前的Unix有一个叫chroot的系统调用(通过修改根目录把用户jail到一个特定目录下),chroot提供了一种简单的隔离模式:chroot内部的文件系统无法访问外部的内容。Linux Namespace在此基础上,提供了对UTS、IPC、mount、PID、network、User等的隔离机制。 举个例子,我们都知道,Linux下的超级父亲进程的PID是1,所以,同chroot一样,如果我们可以把用户的进程空间jail到某个进程分支下,并像chroot那样让其下面的进程 看到的那个超级父进程的PID为1,于是就可以达到资源隔离的效果了(不同的PID namespace中的进程无法看到彼此) šLinux Namespace 有如下种类,官方文档在这里《Namespace in Operation》 分类 系统调用参数 相关内核版本 Mount namespaces CLONE_NEWNS Linux 2.4.19 šUTS namespaces CLONE_NEWUTS Linux 2.6.19 IPC namespaces CLONE_NEWIPC Linux 2.6.19 PID namespaces CLONE_NEWPID Linux 2.6.24 Network namespaces CLONE_NEWNET 始于Linux 2.6.24 完成于 Linux 2.6.29 User namespaces CLONE_NEWUSER 始于 Linux 2.6.23 完成于 Linux 3.8) 主要是š三个系统调用 šclone() – 实现线程的系统调用,用来创建一个新的进程,并可以通过设计上述参数达到隔离。 šunshare() – 使某进程脱离某个namespace šsetns() – 把某进程加入到某个namespace User Namespace主要是用了CLONE_NEWUSER的参数。使用了这个参数后,内部看到的UID和GID已经与外部不同了,默认显示为65534。那是因为容器找不到其真正的UID所以,设置上了最大的UID(其设置定义在/proc/sys/kernel/overflowuid)。 要把容器中的uid和真实系统的uid给映射在一起,需要修改 /proc//uid_map 和 /proc//gid_map 这两个文件。这两个文件的格式为: ID-inside-ns ID-outside-ns length 其中: 第一个字段ID-inside-ns表示在容器显示的UID或GID, 第二个字段ID-outside-ns表示容器外映射的真实的UID或GID。 第三个字段表示映射的范围,一般填1,表示一一对应。 比如,把真实的uid=1000映射成容器内的uid=0 $ cat /proc/2465/uid_map 0 1000 1 再比如下面的示例:表示把namespace内部从0开始的uid映射到外部从0开始的uid,其最大范围是无符号32位整形 $ cat /proc/$$/uid_map 0 0 4294967295 另外,需要注意的是: 写这两个文件的进程需要这个namespace中的CAP_SETUID (CAP_SETGID)权限(可参看Capabilities) 写入的进程必须是此user namespace的父或子的user namespace进程。 另外需要满如下条件之一:1)父进程将effective uid/gid映射到子进程的user namespace中,2)父进程如果有CAP_SETUID/CAP_SETGID权限,那么它将可以映射到父进程中的任一uid/gid。 当你启动一个Docker容器后,你可以使用ip link show或ip addr show来查看当前宿主机的网络情况(我们可以看到有一个docker0,还有一个veth22a38e6的虚拟网卡——给容器用的) # 把容器里的 veth-ns1改名为 eth0 (容器外会冲突,容器内就不会了) ip netns exec ns1 ip link set dev veth-ns1 name eth0 # 为容器中的网卡分配一个IP地址,并激活它 ip netns exec ns1 ifconfig eth0 192.168.10.11/24 up # 上面我们把veth-ns1这个网卡按到了容器中,然后我们要把lxcbr0.1添加上网桥上 brctl addif lxcbr0 lxcbr0.1 # 为容器增加一个路由规则,让容器可以访问外面的网络 ip netns exec ns1 ip route add default via 192.168.10.1 # 在/etc/netns下创建network namespce名称为ns1的目录, # 然后为这个namespace设置resolv.conf,这样,容器内就可以访问域名了 mkdir -p /etc/netns/ns1 echo "nameserver 8.8.8.8" > /etc/netns/ns1/resolv.conf 上面基本上就是docker网络的原理了,只不过, Docker的resolv.conf没有用这样的方式,而是用了Mount Namesapce的那种方式 另外,docker是用进程的PID来做Network Namespace的名称的。

阅读全文

linux cgroup

Namespace解决的问题主要是环境隔离的问题,这只是虚拟化中最最基础的一步,我们还需要解决对计算机资源使用上的隔离。也就是说,虽然你通过Namespace把我Jail到一个特定的环境中去了,但是我在其中的进程使用用CPU、内存、磁盘等这些计算资源其实还是可以随心所欲的。所以,我们希望对进程进行资源利用上的限制或控制。这就是Linux CGroup出来了的原因。 Linux CGroup全称Linux Control Group, 是Linux内核的一个功能,用来限制,控制与分离一个进程组群的资源(如CPU、内存、磁盘输入输出等)。这个项目最早是由Google的工程师在2006年发起(主要是Paul Menage和Rohit Seth),最早的名称为进程容器(process containers)。在2007年时,因为在Linux内核中,容器(container)这个名词太过广泛,为避免混乱,被重命名为cgroup,并且被合并到2.6.24版的内核中去。然后,其它开始了他的发展。 Linux CGroupCgroup 可​​​让​​​您​​​为​​​系​​​统​​​中​​​所​​​运​​​行​​​任​​​务​​​(进​​​程​​​)的​​​用​​​户​​​定​​​义​​​组​​​群​​​分​​​配​​​资​​​源​​​ — 比​​​如​​​ CPU 时​​​间​​​、​​​系​​​统​​​内​​​存​​​、​​​网​​​络​​​带​​​宽​​​或​​​者​​​这​​​些​​​资​​​源​​​的​​​组​​​合​​​。​​​您​​​可​​​以​​​监​​​控​​​您​​​配​​​置​​​的​​​ cgroup,拒​​​绝​​​ cgroup 访​​​问​​​某​​​些​​​资​​​源​​​,甚​​​至​​​在​​​运​​​行​​​的​​​系​​​统​​​中​​​动​​​态​​​配​​​置​​​您​​​的​​​ cgroup。 主要提供了如下功能: Resource limitation: 限制资源使用,比如内存使用上限以及文件系统的缓存限制。 Prioritization: 优先级控制,比如:CPU利用和磁盘IO吞吐。 Accounting: 一些审计或一些统计,主要目的是为了计费。 Control: 挂起进程,恢复执行进程。 使​​​用​​​ cgroup,系​​​统​​​管​​​理​​​员​​​可​​​更​​​具​​​体​​​地​​​控​​​制​​​对​​​系​​​统​​​资​​​源​​​的​​​分​​​配​​​、​​​优​​​先​​​顺​​​序​​​、​​​拒​​​绝​​​、​​​管​​​理​​​和​​​监​​​控​​​。​​​可​​​更​​​好​​​地​​​根​​​据​​​任​​​务​​​和​​​用​​​户​​​分​​​配​​​硬​​​件​​​资​​​源​​​,提​​​高​​​总​​​体​​​效​​​率​​​。 在实践中,系统管理员一般会利用CGroup做下面这些事(有点像为某个虚拟机分配资源似的): 隔离一个进程集合(比如:nginx的所有进程),并限制他们所消费的资源,比如绑定CPU的核。 为这组进程 分配其足够使用的内存 为这组进程分配相应的网络带宽和磁盘存储限制 限制访问某些设备(通过设置设备的白名单) Linux把CGroup这个事实现成了一个file system,你可以mount。在我的Ubuntu 14.04下,你输入以下命令你就可以看到cgroup已为你mount好了。 $ mount -t cgroup cgroup on /sys/fs/cgroup/cpuset type cgroup (rw,relatime,cpuset) cgroup on /sys/fs/cgroup/cpu type cgroup (rw,relatime,cpu) cgroup on /sys/fs/cgroup/cpuacct type cgroup (rw,relatime,cpuacct) cgroup on /sys/fs/cgroup/memory type cgroup (rw,relatime,memory) cgroup on /sys/fs/cgroup/devices type cgroup (rw,relatime,devices) cgroup on /sys/fs/cgroup/freezer type cgroup (rw,relatime,freezer) cgroup on /sys/fs/cgroup/blkio type cgroup (rw,relatime,blkio) cgroup on /sys/fs/cgroup/net_prio type cgroup (rw,net_prio) cgroup on /sys/fs/cgroup/net_cls type cgroup (rw,net_cls) cgroup on /sys/fs/cgroup/perf_event type cgroup (rw,relatime,perf_event) cgroup on /sys/fs/cgroup/hugetlb type cgroup (rw,relatime,hugetlb) 或者使用lssubsys命令: $ lssubsys -m cpuset /sys/fs/cgroup/cpuset cpu /sys/fs/cgroup/cpu cpuacct /sys/fs/cgroup/cpuacct memory /sys/fs/cgroup/memory devices /sys/fs/cgroup/devices freezer /sys/fs/cgroup/freezer blkio /sys/fs/cgroup/blkio net_cls /sys/fs/cgroup/net_cls net_prio /sys/fs/cgroup/net_prio perf_event /sys/fs/cgroup/perf_event hugetlb /sys/fs/cgroup/hugetlb 我们可以看到,在/sys/fs下有一个cgroup的目录,这个目录下还有很多子目录,比如: cpu,cpuset,memory,blkio……这些,这些都是cgroup的子系统。分别用于干不同的事的。 如果你没有看到上述的目录,你可以自己mount,下面给了一个示例: mkdir cgroup mount -t tmpfs cgroup_root ./cgroup mkdir cgroup/cpuset mount -t cgroup -ocpuset cpuset ./cgroup/cpuset/ mkdir cgroup/cpu mount -t cgroup -ocpu cpu ./cgroup/cpu/ mkdir cgroup/memory mount -t cgroup -omemory memory ./cgroup/memory/ 一旦mount成功,你就会看到这些目录下就有好文件了,比如,如下所示的cpu和cpuset的子系统: $ ls /sys/fs/cgroup/cpu /sys/fs/cgroup/cpuset/ /sys/fs/cgroup/cpu: cgroup.clone_children cgroup.sane_behavior cpu.shares release_agent cgroup.event_control cpu.cfs_period_us cpu.stat tasks cgroup.procs cpu.cfs_quota_us notify_on_release user /sys/fs/cgroup/cpuset/: cgroup.clone_children cpuset.mem_hardwall cpuset.sched_load_balance cgroup.event_control cpuset.memory_migrate cpuset.sched_relax_domain_level cgroup.procs cpuset.memory_pressure notify_on_release cgroup.sane_behavior cpuset.memory_pressure_enabled release_agent cpuset.cpu_exclusive cpuset.memory_spread_page tasks cpuset.cpus cpuset.memory_spread_slab user cpuset.mem_exclusive cpuset.mems 你可以到/sys/fs/cgroup的各个子目录下去make个dir,你会发现,一旦你创建了一个子目录,这个子目录里又有很多文件了。 hchen@ubuntu:/sys/fs/cgroup/cpu$ sudo mkdir haoel [sudo] password for hchen: ~:/sys/fs/cgroup/cpu$ ls ./haoel cgroup.clone_children cgroup.procs cpu.cfs_quota_us cpu.stat tasks cgroup.event_control cpu.cfs_period_us cpu.shares notify_on_release 好了,我们来看几个示例。 CPU 限制 假设,我们有一个非常吃CPU的程序,叫deadloop,其源码如下: DEADLOOP.C int main(void) { int i = 0; for(;;) i++; return 0; } 用sudo执行起来后,毫无疑问,CPU被干到了100%(下面是top命令的输出) PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
3529 root 20 0 4196 736 656 R 99.6 0.1 0:23.13 deadloop 然后,我们这前不是在/sys/fs/cgroup/cpu下创建了一个haoel的group。我们先设置一下这个group的cpu利用的限制: # cat /sys/fs/cgroup/cpu/haoel/cpu.cfs_quota_us -1 # echo 20000 > /sys/fs/cgroup/cpu/haoel/cpu.cfs_quota_us 我们看到,这个进程的PID是3529,我们把这个进程加到这个cgroup中:

echo 3529 » /sys/fs/cgroup/cpu/haoel/tasks

然后,就会在top中看到CPU的利用立马下降成20%了。(前面我们设置的20000就是20%的意思) PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
3529 root 20 0 4196 736 656 R 19.9 0.1 8:06.11 deadloop 然后,在我们另外一边:

创建memory cgroup

$ mkdir /sys/fs/cgroup/memory/haoel $ echo 64k > /sys/fs/cgroup/memory/haoel/memory.limit_in_bytes

把上面的进程的pid加入这个cgroup

$ echo [pid] > /sys/fs/cgroup/memory/haoel/tasks 你会看到,一会上面的进程就会因为内存问题被kill掉了。 磁盘I/O限制 我们先看一下我们的硬盘IO,我们的模拟命令如下:(从/dev/sda1上读入数据,输出到/dev/null上) sudo dd if=/dev/sda1 of=/dev/null 我们通过iotop命令我们可以看到相关的IO速度是55MB/s(虚拟机内): TID PRIO USER DISK READ DISK WRITE SWAPIN IO> COMMAND
8128 be/4 root 55.74 M/s 0.00 B/s 0.00 % 85.65 % dd if=/de~=/dev/null… 然后,我们先创建一个blkio(块设备IO)的cgroup mkdir /sys/fs/cgroup/blkio/haoel 并把读IO限制到1MB/s,并把前面那个dd命令的pid放进去(注:8:0 是设备号,你可以通过ls -l /dev/sda1获得): # echo ‘8:0 1048576’ > /sys/fs/cgroup/blkio/haoel/blkio.throttle.read_bps_device # echo 8128 > /sys/fs/cgroup/blkio/haoel/tasks 再用iotop命令,你马上就能看到读速度被限制到了1MB/s左右。 TID PRIO USER DISK READ DISK WRITE SWAPIN IO> COMMAND
8128 be/4 root 973.20 K/s 0.00 B/s 0.00 % 94.41 % dd if=/de~=/dev/null… CGroup的子系统 好了,有了以上的感性认识我们来,我们来看看control group有哪些子系统: blkio — 这​​​个​​​子​​​系​​​统​​​为​​​块​​​设​​​备​​​设​​​定​​​输​​​入​​​/输​​​出​​​限​​​制​​​,比​​​如​​​物​​​理​​​设​​​备​​​(磁​​​盘​​​,固​​​态​​​硬​​​盘​​​,USB 等​​​等​​​)。 cpu — 这​​​个​​​子​​​系​​​统​​​使​​​用​​​调​​​度​​​程​​​序​​​提​​​供​​​对​​​ CPU 的​​​ cgroup 任​​​务​​​访​​​问​​​。​​​ cpuacct — 这​​​个​​​子​​​系​​​统​​​自​​​动​​​生​​​成​​​ cgroup 中​​​任​​​务​​​所​​​使​​​用​​​的​​​ CPU 报​​​告​​​。​​​ cpuset — 这​​​个​​​子​​​系​​​统​​​为​​​ cgroup 中​​​的​​​任​​​务​​​分​​​配​​​独​​​立​​​ CPU(在​​​多​​​核​​​系​​​统​​​)和​​​内​​​存​​​节​​​点​​​。​​​ devices — 这​​​个​​​子​​​系​​​统​​​可​​​允​​​许​​​或​​​者​​​拒​​​绝​​​ cgroup 中​​​的​​​任​​​务​​​访​​​问​​​设​​​备​​​。​​​ freezer — 这​​​个​​​子​​​系​​​统​​​挂​​​起​​​或​​​者​​​恢​​​复​​​ cgroup 中​​​的​​​任​​​务​​​。​​​ memory — 这​​​个​​​子​​​系​​​统​​​设​​​定​​​ cgroup 中​​​任​​​务​​​使​​​用​​​的​​​内​​​存​​​限​​​制​​​,并​​​自​​​动​​​生​​​成​​​​​内​​​存​​​资​​​源使用​​​报​​​告​​​。​​​ net_cls — 这​​​个​​​子​​​系​​​统​​​使​​​用​​​等​​​级​​​识​​​别​​​符​​​(classid)标​​​记​​​网​​​络​​​数​​​据​​​包​​​,可​​​允​​​许​​​ Linux 流​​​量​​​控​​​制​​​程​​​序​​​(tc)识​​​别​​​从​​​具​​​体​​​ cgroup 中​​​生​​​成​​​的​​​数​​​据​​​包​​​。​​​ net_prio — 这个子系统用来设计网络流量的优先级 hugetlb — 这个子系统主要针对于HugeTLB系统进行限制,这是一个大页文件系统。 注意,你可能在Ubuntu 14.04下看不到net_cls和net_prio这两个cgroup,你需要手动mount一下: $ sudo modprobe cls_cgroup $ sudo mkdir /sys/fs/cgroup/net_cls $ sudo mount -t cgroup -o net_cls none /sys/fs/cgroup/net_cls $ sudo modprobe netprio_cgroup $ sudo mkdir /sys/fs/cgroup/net_prio $ sudo mount -t cgroup -o net_prio none /sys/fs/cgroup/net_prio 关于各个子系统的参数细节,以及更多的Linux CGroup的文档,你可以看看下面的文档: Linux Kernel的官方文档 Redhat的官方文档 CGroup的术语 CGroup有下述术语: 任务(Tasks):就是系统的一个进程。 控制组(Control Group):一组按照某种标准划分的进程,比如官方文档中的Professor和Student,或是WWW和System之类的,其表示了某进程组。Cgroups中的资源控制都是以控制组为单位实现。一个进程可以加入到某个控制组。而资源的限制是定义在这个组上,就像上面示例中我用的haoel一样。简单点说,cgroup的呈现就是一个目录带一系列的可配置文件。 层级(Hierarchy):控制组可以组织成hierarchical的形式,既一颗控制组的树(目录结构)。控制组树上的子节点继承父结点的属性。简单点说,hierarchy就是在一个或多个子系统上的cgroups目录树。 子系统(Subsystem):一个子系统就是一个资源控制器,比如CPU子系统就是控制CPU时间分配的一个控制器。子系统必须附加到一个层级上才能起作用,一个子系统附加到某个层级以后,这个层级上的所有控制族群都受到这个子系统的控制。Cgroup的子系统可以有很多,也在不断增加中。 下一代的CGroup 上面,我们可以看到,CGroup的一些常用方法和相关的术语。一般来说,这样的设计在一般情况下还是没什么问题的,除了操作上的用户体验不是很好,但基本满足我们的一般需求了。 不过,对此,有个叫Tejun Heo的同学非常不爽,他在Linux社区里对cgroup吐了一把槽,还引发了内核组的各种讨论。 对于Tejun Heo同学来说,cgroup设计的相当糟糕。他给出了些例子,大意就是说,如果有多种层级关系,也就是说有多种对进程的分类方式,比如,我们可以按用户来分,分成Professor和Student,同时,也有按应用类似来分的,比如WWW和NFS等。那么,当一个进程即是Professor的,也是WWW的,那么就会出现多层级正交的情况,从而出现对进程上管理的混乱。另外,一个case是,如果有一个层级A绑定cpu,而层级B绑定memory,还有一个层级C绑定cputset,而有一些进程有的需要AB,有的需要AC,有的需要ABC,管理起来就相当不易。 层级操作起来比较麻烦,而且如果层级变多,更不易于操作和管理,虽然那种方式很好实现,但是在使用上有很多的复杂度。你可以想像一个图书馆的图书分类问题,你可以有各种不同的分类,分类和图书就是一种多对多的关系。 所以,在Kernel 3.16后,引入了unified hierarchy的新的设计,这个东西引入了一个叫__DEVEL__sane_behavior的特性(这个名字很明显意味目前还在开发试验阶段),它可以把所有子系统都挂载到根层级下,只有叶子节点可以存在tasks,非叶子节点只进行资源控制。 我们mount一下看看: $ sudo mount -t cgroup -o __DEVEL__sane_behavior cgroup ./cgroup $ ls ./cgroup cgroup.controllers cgroup.procs cgroup.sane_behavior cgroup.subtree_control $ cat ./cgroup/cgroup.controllers cpuset cpu cpuacct memory devices freezer net_cls blkio perf_event net_prio hugetlb 我们可以看到有四个文件,然后,你在这里mkdir一个子目录,里面也会有这四个文件。上级的cgroup.subtree_control控制下级的cgroup.controllers。 cgroup只有上线控制下级,无法传递到下下级。所以,C和D中没有memory的限制,E中没有blkio和memory的限制。而本层的cgroup.controllers文件是个只读的,其中的内容就看上级的subtree_control里有什么了。 任何被配置过subtree_control的目录都不能绑定进程,根结点除外。所以,A,C,D,E可以绑上进程,但是B不行。 我们可以看到,这种方式干净的区分开了两个事,一个是进程的分组,一个是对分组的资源控制(以前这两个事完全混在一起),在目录继承上增加了些限制,这样可以避免一些模棱两可的情况。 当然,这个事还在演化中,cgroup的这些问题这个事目前由cgroup的吐槽人Tejun Heo和华为的Li Zefan同学负责解决中。总之,这是一个系统管理上的问题,而且改变会影响很多东西,但一旦方案确定,老的cgroup方式将一去不复返。 Cgroup是进行分组化管理的Linux内核功能,具体的资源管理是通过子系统来完成的。可以理解为子系统就是资源控制器,每种子系统就是一个资源的分配器,比如cpu子系统是控制cpu时间分配的,使用方式如下 安装(ubuntu) #apt-get install cgroup-bin 基本命令 cgclassify – cgclassify命令是用来将运行的任务移动到一个或者多个cgroup。 cgclear – cgclear 命令是用来删除层级中的所有cgroup。 cgconfig.conf – 在cgconfig.conf文件中定义cgroup。 cgconfigparser – cgconfigparser命令解析cgconfig.conf文件和并挂载层级。 cgcreate – cgcreate在层级中创建新cgroup。 cgdelete – cgdelete命令删除指定的cgroup。 cgexec – cgexec命令在指定的cgroup中运行任务。 cgget – cgget命令显示cgroup参数。 cgred.conf – cgred.conf是cgred服务的配置文件。 cgrules.conf – cgrules.conf 包含用来决定何时任务术语某些 cgroup的规则。 cgrulesengd – cgrulesengd 在 cgroup 中发布任务。 cgset – cgset 命令为 cgroup 设定参数。 lscgroup – lscgroup 命令列出层级中的 cgroup。 lssubsys – lssubsys 命令列出包含指定子系统的层级 子系统说明 可以使用lssubsys -a来列出系统支持多少种子系统,和:比如cpu是控制cpu时间片的,memory是控制内存使用的 #lssubsys -a cpuset cpu,cpuacct memory devices freezer net_cls,net_prio blkio perf_event hugetlb 主要的几种子系统说明如下: blkio 这个子系统设置限制每个块设备的输入输出控制。例如:磁盘,光盘以及usb等等。 cpu 这个子系统使用调度程序为cgroup任务提供cpu的访问。 cpuacct 产生cgroup任务的cpu资源报告。 cpuset 如果是多核心的cpu,这个子系统会为cgroup任务分配单独的cpu和内存。 devices 允许或拒绝cgroup任务对设备的访问。 freezer 暂停和恢复cgroup任务。 memory 设置每个cgroup的内存限制以及产生内存资源报告。 net_cls 标记每个网络包以供cgroup方便使用。 ns 名称空间子系统 perf_event: 增加了对每group的监测跟踪的能力,即可以监测属于某个特定的group的所有线程以及运行在特定CPU上的线程 要为Cgroup分配限制的资源,首先要挂载子系统,然后才有控制组,比如想要对目标程序进行内存限制,那就需要挂载memory子系统 使用lssubsys -am来显示已经挂载的子系统 #lssubsys -am cpuset /sys/fs/cgroup/cpuset cpu,cpuacct /sys/fs/cgroup/cpu,cpuacct memory /sys/fs/cgroup/memory devices /sys/fs/cgroup/devices freezer /sys/fs/cgroup/freezer net_cls,net_prio /sys/fs/cgroup/net_cls,net_prio blkio /sys/fs/cgroup/blkio perf_event /sys/fs/cgroup/perf_event hugetlb /sys/fs/cgroup/hugetlb 可以手动挂载或者卸载子系统,如执行umount /sys/fs/cgroup/memory,memory子系统就被卸载了,这时候手动执行# mount -t cgroup -o memory memory /sys/fs/cgroup/memory就又挂载上了。 要确保需要的子系统都挂上了,不然创建控制组的时候会报错 is not mounted #cgcreate -g memory,cpu:/hzmali_test cgcreate: can’t create cgroup /hzmali_test: Cgroup one of the needed subsystems is not mounted 如何创建control group(即需要资源管理的组)呢, 这里用cgcreate命令,当然也有其他方法, 如cgconfig.conf等 #cgcreate -g memory,cpu:/hzmali_test 这里有个重要特性:一个组可以同时做多个资源的限制,如这里我同时限制了memory和cpu,然后memory和cpu子系统目录下会自动生成这个组的目录和些文件,如memory #/sys/fs/cgroup/memory/hzmali_test$ ls -lrt 文件很多,选几个重要的讲下: tasks 可以将想要限制资源的进程都加到这个文件中 memory.max_usage_in_bytes内存的最大使用量,用来限制资源 -memory.soft_limit_in_bytes 和 memory.limit_in_bytes 的差异是,这个限制并不会阻止进程使用超过限额的内存,只是在系统内存不足时,会优先回收超过限额的进程占用的内存,使之向限定值靠拢。 memory.oom_control 包含一个标志(0或1)来开启或者关闭cgroup的OOM killer。如果开启(1),任务如果尝试申请内存超过允许,就会被系统OOM killer终止。OOM killer在每个使用cgroup内存子系统中都是默认开启的。如果需要关闭,则可以向memory.oom_control文件写入1: # echo 1 > /sys/fs/cgroup/memory.oom_control 如果OOM killer关闭,那么进程尝试申请的内存超过允许,那么它就会被暂停,直到额外的内存被释放 memory.mem.usage_in_bytes 当前进程内存用量,因为现在还没有进程加到组里,就是0了 memory.mem.failcnt显示内存达到限制值的次数 Cgroup文档 Cgroup的使用细节,子系统和参数设置都可以可以在https://www.kernel.org/doc/Documentation/cgroups/中找到,继承等特性由于篇幅所限,可以看下文档 Cgroup实战 内存限制测试 用控制组限制目标程序内存使用为1000000 byte,当然,需要root执行 echo “1000000” >memory.limit_in_bytes 一般更推荐用cgset来设置数值 cgset -r memory.limit_in_bytes=1000000 hzmali_test 然后构造一个吃内存的程序,每运行一次内存使用就大幅增加 如果我不想把机器跑死,这里想要限制组里的进程的CPU使用,有2种做法 1.在cpu子系统中控制cpu调度的配额 先看下当前cpu分配情况 cat /sys/fs/cgroup/cpu/hzmali_test/cpu.cfs_quota_us -1 cat /sys/fs/cgroup/cpu/hzmali_test/cpu.cfs_period_us 100000 -1表示无限制,这里改为50000,即相对于cpu.cfs_period_us 来说为50000/100000约占1个核50%的cpu时间 #./cpu_test.sh & [1] 17709 # echo 17709 >/sys/fs/cgroup/cpu/hzmali_test/tasks 或者直接使用命令cgexec执行 cgexec -g cpu:hzmali_test ./cpu_test.sh top了下基本上就是在50%的cpu占用 %Cpu0 : 50.5 us, 0.0 sy, 0.0 ni, 49.5 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st %Cpu1 : 0.0 us, 0.3 sy, 0.0 ni, 99.7 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 17709 root 20 0 25368 2020 1764 R 50.2 0.1 1:14.74 bash 2.在cpuset控制物理cpu的分配 当前使用了上面的方法后,我们发现进程的CPU使用都在Cpu0上,这次希望只用Cpu1来跑这个小程序 所以把控制组也加到cpuset # cgcreate -g cpuset:/hzmali_test 看一下现在使用的cpu的设置 # cat /sys/fs/cgroup/cpuset/hzmali_test/cpuset.cpus 0-1 改为只用Cpu1,输入以下命令 # echo 1 > /sys/fs/cgroup/cpuset/hzmali_test/cpuset.cpus # echo 17709 > /sys/fs/cgroup/cpuset/hzmali_test/tasks 或用命令 # cgset -r cpuset.cpus=’1’ hzmali_test # cgclassify -g cpu,cpuset:hzmali_test 17709 top一下,内存的使用从CPU0到CPU1了 %Cpu0 : 0.0 us, 0.0 sy, 0.0 ni, 99.7 id, 0.0 wa, 0.0 hi, 0.3 si, 0.0 st %Cpu1 : 50.3 us, 0.0 sy, 0.0 ni, 49.7 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 17709 root 20 0 25368 2108 2076 R 50.1 0.1 8:56.78 bash IO限制测试 用dd对硬盘进行写操作 # dd if=/dev/sda of=/dev/null & 打开iotop看下IO速度 Total DISK READ : 100.37 M/s | Total DISK WRITE : 0.00 B/s Actual DISK READ: 100.37 M/s | Actual DISK WRITE: 0.00 B/s TID PRIO USER DISK READ DISK WRITE SWAPIN IO> COMMAND 18081 be/4 root 100.37 M/s 0.00 B/s 0.00 % 1.34 % dd if=/dev/sda of=/dev/null 为了控制IO速度,在blkio上创建控制组 # cgcreate -g blkio:/hzmali_test 查看下硬盘号 # ls -l /dev/sda brw-rw—- 1 root disk 8, 0 Jul 25 22:46 /dev/sda 设置硬盘号和对应的读取速度限制,然后执行同样的命令 # cgset -r blkio.throttle.read_bps_device=”8:0 1000000” hzmali_test # cgexec -g blkio:hzmali_test “dd if=/dev/sda of=/dev/null” 用iotop查看下,速度果然就降到1M以下

阅读全文

bridge

网桥设备作为一个虚拟设备,用于连接多个端口,可以构建一个局域网。与之相似的是vlan设备,在linux中,vlan设备是为了处理802.1q的添加和去除TAG的问题,这和传统交换机中vlan的功能—划分局域网,不太一样,在这里仅仅是处理了消息头,只是实现了隔离功能,并未实现交换功能,如果需要vlan内的数据转发,可以把vlan子接口挂接在网桥设备下。

阅读全文

expect

expect是一个自动交互功能的工具。expect是开了一个子进程,通过spawn来执行shell脚本,监测到脚本的返回结果,通过expect判断要进行的交互输入内容(send) 1.安装expect 需要先安装tcl:apt-get install tcl apt-get install expect 2.expect使用 2.1一个简单的输入密码操作

1
2
3
4
5
6
7
 #!/usr/bin/expect
set timeout 100
set password "123456"
spawn sudo rm -rf zzlogic
expect "root123456"
send "$password\n"
interact

说明: 第一行#!/usr/bin/expect表示使用expect的shell交互模式 set是对变量password赋值 set timeout 100:设置超时时间为100秒,如果要执行的shell命令很长可以设置超时时间长一些。expect超过超时时间没有监测到要找的字符串,则不执行,默认timeout为10秒 spawn在expect下执行shell脚本 expect对通过spawn执行的shell脚本的返回进行判断,是否包含“”中的字段 send:如果expect监测到了包含的字符串,将输入send中的内容,\n相当于回车 interact:退出expect返回终端,可以继续输入,否则将一直在expect不能退出到终端 2.2expect的命令行参数 [lindex $argv n]获得index为n的参数(index从0开始计算) $argc为命令行参数的个数 [lrange $argv 0 0]表示第一个参数 [lrange $argv 0 3]表示第1到第3个参数 例如scp_service.sh文件,可以./scp_service.sh -rm来执行,这时是赋值了一个参数 set option [lindex $argv 0](获得第一个参数存到变量option中,参数是的index是从0开始计算的) 2.3if…elif…else… expect支持if语句, if {条件1} { 条件1执行语句 } elif {条件2} { 条件2执行语句 } else { 其他情况执行语句 } 说明: 1.if的条件用{}来包含条件 2.if和后面的{}必须有空格隔开 3.两个花括号之间必须有空格隔开,比如if {} {},否则会报错 expect:extra characters after close-brace 3.使用{来衔接下一行,所以if的条件后需要加左花括号{ 4.else不能单独放一行,所以else要跟在}后面 2.4 expect {},多行期望,匹配到哪条执行哪条 背景:有时执行shell后预期结果是不固定的,有可能是询问是yes/no,有可能是去输入密码,所以可以用expect{} 花括号内放多行语句,从上至下匹配,匹配到哪个expect执行哪句。

阅读全文

Linux下的shell工作原理

Linux系统提供给用户的最重要的系统程序是Shell命令语言解释程序。它不属于内核部分,而是在核心之外,以用户态方式运行。其基本功能是解释并执行用户打入的各种命令,实现用户与Linux核心的接口。系统初启后,核心为每个终端用户建立一个进程去执行Shell解释程序。它的执行过程基本上按如下步骤: (1)读取用户由键盘输入的命令行。

阅读全文

dup dup2

有时我们希望把标准输入重定向到一个文件,或者把标准输出重定向到一个网络连接。 dup()与dup2()能对输入文件描述符进行重定向。 int dup(int oldfd); int dup2(int oldfd, intnewfd); dup函数创建一个新的文件描述符,该新文件描述符和原有文件描述符oldfd指向相同的文件、管道或者网络连接。 并且dup返回的文件描述符总是取系统当前可用的最小整数值。dup2和dup类似,不过它将返回第一个不小于oldfd的整数值。dup和dup2失败时返回-1并设置errno。

阅读全文

rsync

它比scp更强大,支持“不覆盖”原目录 例子: rsync -avz –progress /root/client/ root@202.112.23.12:/home/work/

阅读全文

系统调用

为什么需要系统调用 现代的操作系统通常都具有多任务处理的功能,通常靠进程来实现。由于操作系统快速的在每个进程间切换执行,所以一切看起来就会像是同时的。同时这也带来了很多安全问题,例如,一个进程可以轻易的修改进程的内存空间中的数据来使另一个进程异常或达到一些目的,因此操作系统必须保证每一个进程都能安全的执行。这一问题的解决方法是在处理器中加入基址寄存器和界限寄存器。这两个寄存器中的内容用硬件限制了对储存器的存取指令所访问的储存器的地址。这样就可以在系统切换进程时写入这两个寄存器的内容到该进程被分配的地址范围,从而避免恶意软件。 为了防止用户程序修改基址寄存器和界限寄存器中的内容来达到访问其他内存空间的目的,这两个寄存器必须通过一些特殊的指令来访问。通常,处理器设有两种模式:“用户模式”与“内核模式”,通过一个标签位来鉴别当前正处于什么模式。一些诸如修改基址寄存器内容的指令只有在内核模式中可以执行,而处于用户模式的时候硬件会直接跳过这个指令并继续执行下一个。 当操作系统接收到系统调用请求后,会让处理器进入内核模式,从而执行诸如I/O操作,修改基址寄存器内容等指令,而当处理完系统调用内容后,操作系统会让处理器返回用户模式,来执行用户代码。

阅读全文

iputils

1.1 iputils软件包简介 iputils软件包是linux环境下一些实用的网络工具的集合。一开始由Alexey Kuznetsov维护。

阅读全文

zero copy

许多web应用都会向用户提供大量的静态内容,这意味着有很多data从硬盘读出之后,会原封不动的通过socket传输给用户。这种操作看起来可能不会怎么消耗CPU,但是实际上它是低效的:kernal把数据从disk读出来,然后把它传输给user级的application,然后application再次把同样的内容再传回给处于kernal级的socket。这种场景下,application实际上只是作为一种低效的中间介质,用来把disk file的data传给socket。

阅读全文

进程在后台运行原理

nohup/setsid/& 场景: 如果只是临时有一个命令需要长时间运行,什么方法能最简便的保证它在后台稳定运行呢?

阅读全文

Linux、Mac上面ln命令使用说明

ln是linux中又一个非常重要命令,它的功能是为某一个文件在另外一个位置建立一个同不的链接,这个命令最常用的参数是 -s,具体用法是:ln –s 源文件 目标文件。

阅读全文

单播、多播和广播

  单播在网络中得到了广泛的应用,网络上绝大部分的数据都是以单播的形式传输的,只是一般网络用户不知道而已。例如,你在收发电子邮件、浏览网页时,必须与邮件服务器、Web服务器建立连接,此时使用的就是单播数据传输方式。但是通常使用“点对点通信”(Point to Point)代替“单播”,因为“单播”一般与“多播”和“广播”相对应使用    “多播”也可以称为“组播”,在网络技术的应用并不是很多,网上视频会议、网上视频点播特别适合采用多播方式。因为如果采用单播方式,逐个节点传输,有多少个目标节点,就会有多少次传送过程,这种方式显然效率极低,是不可取的;如果采用不区分目标、全部发送的广播方式,虽然一次可以传送完数据,但是显然达不到区分特定数据接收对象的目的。采用多播方式,既可以实现一次传送所有目标节点的数据,也可以达到只对特定对象传送数据的目的。

阅读全文

arp

地址解析协议,即ARP(Address Resolution Protocol),是根据IP地址获取物理地址的一个TCP/IP协议。主机发送信息时将包含目标IP地址的ARP请求广播到网络上的所有主机,并接收返回消息,以此确定目标的物理地址;收到返回消息后将该IP地址和物理地址存入本机ARP缓存中并保留一定时间,下次请求时直接查询ARP缓存以节约资源。地址解析协议是建立在网络中各个主机互相信任的基础上的,网络上的主机可以自主发送ARP应答消息,其他主机收到应答报文时不会检测该报文的真实性就会将其记入本机ARP缓存;由此攻击者就可以向某一主机发送伪ARP应答报文,使其发送的信息无法到达预期的主机或到达错误的主机,这就构成了一个ARP欺骗。ARP命令可用于查询本机ARP缓存中IP地址和MAC地址的对应关系、添加或删除静态对应关系等。相关协议有RARP、代理ARP。NDP用于在IPv6中代替地址解析协议。 工作过程 主机A的IP地址为192.168.1.1,MAC地址为0A-11-22-33-44-01; 主机B的IP地址为192.168.1.2,MAC地址为0A-11-22-33-44-02; 当主机A要与主机B通信时,地址解析协议可以将主机B的IP地址(192.168.1.2)解析成主机B的MAC地址,以下为工作流程: 第1步:根据主机A上的路由表内容,IP确定用于访问主机B的转发IP地址是192.168.1.2。然后A主机在自己的本地ARP缓存中检查主机B的匹配MAC地址。 第2步:如果主机A在ARP缓存中没有找到映射,它将询问192.168.1.2的硬件地址,从而将ARP请求帧广播到本地网络上的所有主机。源主机A的IP地址和MAC地址都包括在ARP请求中。本地网络上的每台主机都接收到ARP请求并且检查是否与自己的IP地址匹配。如果主机发现请求的IP地址与自己的IP地址不匹配,它将丢弃ARP请求。 第3步:主机B确定ARP请求中的IP地址与自己的IP地址匹配,则将主机A的IP地址和MAC地址映射添加到本地ARP缓存中。 第4步:主机B将包含其MAC地址的ARP回复消息直接发送回主机A。 第5步:当主机A收到从主机B发来的ARP回复消息时,会用主机B的IP和MAC地址映射更新ARP缓存。本机缓存是有生存期的,生存期结束后,将再次重复上面的过程。主机B的MAC地址一旦确定,主机A就能向主机B发送IP通信了。 ARP缓存是个用来储存IP地址和MAC地址的缓冲区,其本质就是一个IP地址–>MAC地址的对应表,表中每一个条目分别记录了网络上其他主机的IP地址和对应的MAC地址。每一个以太网或令牌环网络适配器都有自己单独的表。当地址解析协议被询问一个已知IP地址节点的MAC地址时,先在ARP缓存中查看,若存在,就直接返回与之对应的MAC地址,若不存在,才发送ARP请求向局域网查询。 为使广播量最小,ARP维护IP地址到MAC地址映射的缓存以便将来使用。ARP缓存可以包含动态和静态项目。动态项目随时间推移自动添加和删除。每个动态ARP缓存项的潜在生命周期是10分钟。新加到缓存中的项目带有时间戳,如果某个项目添加后2分钟内没有再使用,则此项目过期并从ARP缓存中删除;如果某个项目已在使用,则又收到2分钟的生命周期;如果某个项目始终在使用,则会另外收到2分钟的生命周期,一直到10分钟的最长生命周期。静态项目一直保留在缓存中,直到重新启动计算机为止。

阅读全文

ioctl

ioctl函数详细说明(网络) ioctl 函数 本函数影响由fd 参数引用的一个打开的文件。 #include

阅读全文

inetd

inetd是监视一些网络请求的守护进程,其根据网络请求来调用相应的服务进程来处理连接请求。它可以为多种服务管理连接,当 inetd 接到连接时,它能够确定连接所需的程序,启动相应的进程,并把 socket 交给它 (服务 socket 会作为程序的标准输入、 输出和错误输出描述符)。 使用 inetd 来运行那些负载不重的服务有助于降低系统负载,因为它不需要为每个服务都启动独立的服务程序。 inetd是通过rc系统启动的。inetd_enable选项默认设为NO,但可以在安装系统时,由用户根据需要sysinstall通过来打开。 inetd.conf则是inetd的配置文件。inetd.conf文件告诉inetd监听哪些网络端口,为每个端口启动哪个服务。在任何的网络环境中使用Linux系统,第一件要做的事就是了解一下服务器到底要提供哪些服务。不需要的那些服务应该被禁止掉,最好卸载掉,这样黑客就少了一些攻击系统的机会。查看“/etc/inetd.conf”文件,了解一下inetd提供哪些服务。用加上注释的方法(在一行的开头加上#号),禁止任何不需要的服务,再给inetd进程发一个SIGHUP信号。

阅读全文

UNIX下的5种IO模型

套接字的IO操作,如recvfrom,分为两个阶段:

阅读全文

用户空间实现线程 内核实现线程 线程的调度

1、在用户空间中实现线程

阅读全文

进程切换

为了控制进程的执行,内核必须有能力挂起正在CPU上运行的进程,并恢复以前挂起的某个进程的执行。这种行为被称为进程切换(process switch)、任务切换(task switch)或上下文切换(content switch)。

阅读全文

IO多路复用之select、poll、epoll

 目前支持I/O多路复用的系统调用有 select,pselect,poll,epoll,I/O多路复用就是通过一种机制,一个进程可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。但select,pselect,poll,epoll本质上都是同步I/O,因为他们都需要在读写事件就绪后自己负责进行读写,也就是说这个读写过程是阻塞的,而异步I/O则无需自己负责进行读写,异步I/O的实现会负责把数据从内核拷贝到用户空间。
 与多进程和多线程技术相比,I/O多路复用技术的最大优势是系统开销小,系统不必创建进程/线程,也不必维护这些进程/线程,从而大大减小了系统的开销。

一、使用场景 IO多路复用是指内核一旦发现进程指定的一个或者多个IO条件准备读取,它就通知该进程。IO多路复用适用如下场合:   1)当客户处理多个描述符时(一般是交互式输入和网络套接口),必须使用I/O复用。   2)当一个客户同时处理多个套接口时,这种情况是可能的,但很少出现。   3)如果一个TCP服务器既要处理监听套接口,又要处理已连接套接口,一般也要用到I/O复用。   4)如果一个服务器即要处理TCP,又要处理UDP,一般要使用I/O复用。   5)如果一个服务器要处理多个服务或多个协议,一般要使用I/O复用。

二、select、poll、epoll简介   epoll跟select都能提供多路I/O复用的解决方案。在现在的Linux内核里有都能够支持,其中epoll是Linux所特有,而select则应该是POSIX所规定,一般操作系统均有实现。 1、select 基本原理:select 函数监视的文件描述符分3类,分别是writefds、readfds、和exceptfds。调用后select函数会阻塞,直到有描述符就绪(有数据 可读、可写、或者有except),或者超时(timeout指定等待时间,如果立即返回设为null即可),函数返回。当select函数返回后,可以通过遍历fdset,来找到就绪的描述符。

基本流程,如图所示:

  select目前几乎在所有的平台上支持,其良好跨平台支持也是它的一个优点。select的一个缺点在于单个进程能够监视的文件描述符的数量存在最大限制,在Linux上一般为1024,可以通过修改宏定义甚至重新编译内核的方式提升这一限制,但是这样也会造成效率的降低。

select本质上是通过设置或者检查存放fd标志位的数据结构来进行下一步处理。这样所带来的缺点是: 1、select最大的缺陷就是单个进程所打开的FD是有一定限制的,它由FD_SETSIZE设置,默认值是1024。   一般来说这个数目和系统内存关系很大,具体数目可以cat /proc/sys/fs/file-max察看。32位机默认是1024个。64位机默认是2048. 2、对socket进行扫描时是线性扫描,即采用轮询的方法,效率较低。   当套接字比较多的时候,每次select()都要通过遍历FD_SETSIZE个Socket来完成调度,不管哪个Socket是活跃的,都遍历一遍。这会浪费很多CPU时间。如果能给套接字注册某个回调函数,当他们活跃时,自动完成相关操作,那就避免了轮询,这正是epoll与kqueue做的。 3、需要维护一个用来存放大量fd的数据结构,这样会使得用户空间和内核空间在传递该结构时复制开销大。

2、poll 基本原理:poll本质上和select没有区别,它将用户传入的数组拷贝到内核空间,然后查询每个fd对应的设备状态,如果设备就绪则在设备等待队列中加入一项并继续遍历,如果遍历完所有fd后没有发现就绪设备,则挂起当前进程,直到设备就绪或者主动超时,被唤醒后它又要再次遍历fd。这个过程经历了多次无谓的遍历。

它没有最大连接数的限制,原因是它是基于链表来存储的,但是同样有一个缺点: 1)大量的fd的数组被整体复制于用户态和内核地址空间之间,而不管这样的复制是不是有意义。 2)poll还有一个特点是“水平触发”,如果报告了fd后,没有被处理,那么下次poll时会再次报告该fd。

注意:从上面看,select和poll都需要在返回后,通过遍历文件描述符来获取已经就绪的socket。事实上,同时连接的大量客户端在一时刻可能只有很少的处于就绪状态,因此随着监视的描述符数量的增长,其效率也会线性下降。

3、epoll   epoll是在2.6内核中提出的,是之前的select和poll的增强版本。相对于select和poll来说,epoll更加灵活,没有描述符限制。epoll使用一个文件描述符管理多个描述符,将用户关系的文件描述符的事件存放到内核的一个事件表中,这样在用户空间和内核空间的copy只需一次。

基本原理:epoll支持水平触发和边缘触发,最大的特点在于边缘触发,它只告诉进程哪些fd刚刚变为就绪态,并且只会通知一次。还有一个特点是,epoll使用“事件”的就绪通知方式,通过epoll_ctl注册fd,一旦该fd就绪,内核就会采用类似callback的回调机制来激活该fd,epoll_wait便可以收到通知。

epoll的优点: 1、没有最大并发连接的限制,能打开的FD的上限远大于1024(1G的内存上能监听约10万个端口)。 2、效率提升,不是轮询的方式,不会随着FD数目的增加效率下降。   只有活跃可用的FD才会调用callback函数;即Epoll最大的优点就在于它只管你“活跃”的连接,而跟连接总数无关,因此在实际的网络环境中,Epoll的效率就会远远高于select和poll。 3、内存拷贝,利用mmap()文件映射内存加速与内核空间的消息传递;即epoll使用mmap减少复制开销。

epoll对文件描述符的操作有两种模式:LT(level trigger)和ET(edge trigger)。LT模式是默认模式,LT模式与ET模式的区别如下: LT模式:当epoll_wait检测到描述符事件发生并将此事件通知应用程序,应用程序可以不立即处理该事件。下次调用epoll_wait时,会再次响应应用程序并通知此事件。 ET模式:当epoll_wait检测到描述符事件发生并将此事件通知应用程序,应用程序必须立即处理该事件。如果不处理,下次调用epoll_wait时,不会再次响应应用程序并通知此事件。 1、LT模式   LT(level triggered)是缺省的工作方式,并且同时支持block和no-block socket。在这种做法中,内核告诉你一个文件描述符是否就绪了,然后你可以对这个就绪的fd进行IO操作。如果你不作任何操作,内核还是会继续通知你的。 2、ET模式   ET(edge-triggered)是高速工作方式,只支持no-block socket。在这种模式下,当描述符从未就绪变为就绪时,内核通过epoll告诉你。然后它会假设你知道文件描述符已经就绪,并且不会再为那个文件描述符发送更多的就绪通知,直到你做了某些操作导致那个文件描述符不再为就绪状态了(比如,你在发送,接收或者接收请求,或者发送接收的数据少于一定量时导致了一个EWOULDBLOCK 错误)。但是请注意,如果一直不对这个fd作IO操作(从而导致它再次变成未就绪),内核不会发送更多的通知(only once)。   ET模式在很大程度上减少了epoll事件被重复触发的次数,因此效率要比LT模式高。epoll工作在ET模式的时候,必须使用非阻塞套接口,以避免由于一个文件句柄的阻塞读/阻塞写操作把处理多个文件描述符的任务饿死。 3、在select/poll中,进程只有在调用一定的方法后,内核才对所有监视的文件描述符进行扫描,而epoll事先通过epoll_ctl()来注册一个文件描述符,一旦基于某个文件描述符就绪时,内核会采用类似callback的回调机制,迅速激活这个文件描述符,当进程调用epoll_wait()时便得到通知。(此处去掉了遍历文件描述符,而是通过监听回调的的机制。这正是epoll的魅力所在。) 注意:如果没有大量的idle-connection或者dead-connection,epoll的效率并不会比select/poll高很多,但是当遇到大量的idle-connection,就会发现epoll的效率大大高于select/poll。

三、select、poll、epoll区别 1、支持一个进程所能打开的最大连接数 2、FD剧增后带来的IO效率问题 3、消息传递方式

综上,在选择select,poll,epoll时要根据具体的使用场合以及这三种方式的自身特点: 1、表面上看epoll的性能最好,但是在连接数少并且连接都十分活跃的情况下,select和poll的性能可能比epoll好,毕竟epoll的通知机制需要很多函数回调。 2、select低效是因为每次它都需要轮询。但低效也是相对的,视情况而定,也可通过良好的设计改善。

阅读全文

ID为0和ID为1的进程

ID为0的进程通常是调度进程,常常被称为交换进程(swapper)。该进程是内核的一部分,它并不执行任何磁盘上的程序,因此也被称为系统进程。 ID为1的进程,通常是init进程,在自举过程结束时由内核调用。该进程的程序文件,在UNIX早起版本中是/etc/init,在较新的版本中是/sbin/init。该进程负责在自举内核后启动一个UNIX系统。init通常读与系统有关的初始化文件(/etc/rc*文件或/etc/inittab文件,以及/etc/init.d中的文件),并将系统引导到一个状态。init 进程绝不会终止,它是一个普通的用户进程(与交换进程不同,它不是内核的系统进程)但是它以超级用户特权运行。 ——摘自APUE 父进程ID为0的进程通常是内核进程,它们作为系统自举过程的一部分而启动,但init进程是个例外,它的父进程是0,但是它是用户进程。

阅读全文

Shell脚本经典之Fork炸弹

众所周知,bash是一款极其强大的shell,提供了强大的交互与编程功能。这样的一款shell中自然不会缺少“函数”这个元素来帮助程序进行模块化的高效开发与管理。于是产生了由于其特殊的特性,bash拥有了fork炸弹。Jaromil在2002年设计了最为精简的一个fork炸弹的实现。

阅读全文

fork

一、fork入门知识一个进程,包括代码、数据和分配给进程的资源。fork()函数通过系统调用创建一个与原来进程几乎完全相同的进程,也就是两个进程可以做完全相同的事,但如果初始参数或者传入的变量不同,两个进程也可以做不同的事。 一个进程调用fork()函数后,系统先给新的进程分配资源,例如存储数据和代码的空间。然后把原来的进程的所有值都复制到新的新进程中,只有少数值与原来的进程的值不同。相当于克隆了一个自己。 我们来看一个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/* 
*  fork_test.c 
*  version 1 
*/  
#include <unistd.h>  
#include <stdio.h>   
int main ()   
{   
pid_t fpid; //fpid表示fork函数返回的值  
int count=0;  
fpid=fork();   
if (fpid < 0)   
printf("error in fork!");   
else if (fpid == 0) {  
printf("i am the child process, my process id is %d/n",getpid());   
printf("我是爹的儿子/n");//对某些人来说中文看着更直白。  
count++;  
}  
else {  
printf("i am the parent process, my process id is %d/n",getpid());   
printf("我是孩子他爹/n");  
count++;  
}  
printf("统计结果是: %d/n",count);  
return 0;  
}  

运行结果是: i am the child process, my process id is 5574 我是爹的儿子 统计结果是: 1 i am the parent process, my process id is 5573 我是孩子他爹 统计结果是: 1 在语句fpid=fork()之前,只有一个进程在执行这段代码,但在这条语句之后,就变成两个进程在执行了,这两个进程的几乎完全相同,将要执行的下一条语句都是if(fpid<0)…… 为什么两个进程的fpid不同呢,这与fork函数的特性有关。fork调用的一个奇妙之处就是它仅仅被调用一次,却能够返回两次,它可能有三种不同的返回值: 1)在父进程中,fork返回新创建子进程的进程ID; 2)在子进程中,fork返回0; 3)如果出现错误,fork返回一个负值; 在fork函数执行完毕后,如果创建新进程成功,则出现两个进程,一个是子进程,一个是父进程。在子进程中,fork函数返回0,在父进程中,fork返回新创建子进程的进程ID。我们可以通过fork返回的值来判断当前进程是子进程还是父进程。引用一位网友的话来解释fpid的值为什么在父子进程中不同。“其实就相当于链表,进程形成了链表,父进程的fpid(p 意味point)指向子进程的进程id, 因为子进程没有子进程,所以其fpid为0. fork出错可能有两种原因: 1)当前的进程数已经达到了系统规定的上限,这时errno的值被设置为EAGAIN。 2)系统内存不足,这时errno的值被设置为ENOMEM。 创建新进程成功后,系统中出现两个基本完全相同的进程,这两个进程执行没有固定的先后顺序,哪个进程先执行要看系统的进程调度策略。 每个进程都有一个独特(互不相同)的进程标识符(process ID),可以通过getpid()函数获得,还有一个记录父进程pid的变量,可以通过getppid()函数获得变量的值。 fork执行完毕后,出现两个进程, 有人说两个进程的内容完全一样啊,怎么打印的结果不一样啊,那是因为判断条件的原因,上面列举的只是进程的代码和指令,还有变量啊。 执行完fork后,进程1的变量为count=0,fpid!=0(父进程)。进程2的变量为count=0,fpid=0(子进程),这两个进程的变量都是独立的,存在不同的地址中,不是共用的,这点要注意。可以说,我们就是通过fpid来识别和操作父子进程的。 还有人可能疑惑为什么不是从#include处开始复制代码的,这是因为fork是把进程当前的情况拷贝一份,执行fork时,进程已经执行完了int count=0;fork只拷贝下一个要执行的代码到新的进程。二、fork进阶知识先看一份代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/* 
*  fork_test.c 
*  version 2 
*/  
#include <unistd.h>  
#include <stdio.h>  
int main(void)  
{  
int i=0;  
printf("i son/pa ppid pid  fpid/n");  
//ppid指当前进程的父进程pid  
//pid指当前进程的pid,  
//fpid指fork返回给当前进程的值  
for(i=0;i<2;i++){  
pid_t fpid=fork();  
if(fpid==0)  
printf("%d child  %4d %4d %4d/n",i,getppid(),getpid(),fpid);  
else  
printf("%d parent %4d %4d %4d/n",i,getppid(),getpid(),fpid);  
}  
return 0;  
}  

运行结果是: i son/pa ppid pid fpid 0 parent 2043 3224 3225 0 child 3224 3225 0 1 parent 2043 3224 3226 1 parent 3224 3225 3227 1 child 1 3227 0 1 child 1 3226 0 这份代码比较有意思,我们来认真分析一下: 第一步:在父进程中,指令执行到for循环中,i=0,接着执行fork,fork执行完后,系统中出现两个进程,分别是p3224和p3225(后面我都用pxxxx表示进程id为xxxx的进程)。可以看到父进程p3224的父进程是p2043,子进程p3225的父进程正好是p3224。我们用一个链表来表示这个关系: p2043->p3224->p3225 第一次fork后,p3224(父进程)的变量为i=0,fpid=3225(fork函数在父进程中返向子进程id),代码内容为:

1
2
3
4
5
6
7
8
for(i=0;i<2;i++){  
pid_t fpid=fork();//执行完毕,i=0,fpid=3225  
if(fpid==0)  
printf("%d child  %4d %4d %4d/n",i,getppid(),getpid(),fpid);  
else  
printf("%d parent %4d %4d %4d/n",i,getppid(),getpid(),fpid);  
}  
return 0;  

p3225(子进程)的变量为i=0,fpid=0(fork函数在子进程中返回0),代码内容为:

1
2
3
4
5
6
7
8
for(i=0;i<2;i++){  
pid_t fpid=fork();//执行完毕,i=0,fpid=0  
if(fpid==0)  
printf("%d child  %4d %4d %4d/n",i,getppid(),getpid(),fpid);  
else  
printf("%d parent %4d %4d %4d/n",i,getppid(),getpid(),fpid);  
}  
return 0;  

所以打印出结果: 0 parent 2043 3224 3225 0 child 3224 3225 0 第二步:假设父进程p3224先执行,当进入下一个循环时,i=1,接着执行fork,系统中又新增一个进程p3226,对于此时的父进程,p2043->p3224(当前进程)->p3226(被创建的子进程)。 对于子进程p3225,执行完第一次循环后,i=1,接着执行fork,系统中新增一个进程p3227,对于此进程,p3224->p3225(当前进程)->p3227(被创建的子进程)。从输出可以看到p3225原来是p3224的子进程,现在变成p3227的父进程。父子是相对的,这个大家应该容易理解。只要当前进程执行了fork,该进程就变成了父进程了,就打印出了parent。 所以打印出结果是: 1 parent 2043 3224 3226 1 parent 3224 3225 3227 第三步:第二步创建了两个进程p3226,p3227,这两个进程执行完printf函数后就结束了,因为这两个进程无法进入第三次循环,无法fork,该执行return 0;了,其他进程也是如此。 以下是p3226,p3227打印出的结果: 1 child 1 3227 0 1 child 1 3226 0 细心的读者可能注意到p3226,p3227的父进程难道不该是p3224和p3225吗,怎么会是1呢?这里得讲到进程的创建和死亡的过程,在p3224和p3225执行完第二个循环后,main函数就该退出了,也即进程该死亡了,因为它已经做完所有事情了。p3224和p3225死亡后,p3226,p3227就没有父进程了,这在操作系统是不被允许的,所以p3226,p3227的父进程就被置为p1了,p1是永远不会死亡的,至于为什么,这里先不介绍,留到“三、fork高阶知识”讲。 总结一下,这个程序执行的流程如下:这个程序最终产生了3个子进程,执行过6次printf()函数。 我们再来看一份代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/* 
*  fork_test.c 
*  version 3 
*/  
#include <unistd.h>  
#include <stdio.h>  
int main(void)  
{  
int i=0;  
for(i=0;i<3;i++){  
pid_t fpid=fork();  
if(fpid==0)  
printf("son/n");  
else  
printf("father/n");  
}  
return 0;  }

它的执行结果是: father son father father father father son son father son son son father son 这里就不做详细解释了,只做一个大概的分析。 for i=0 1 2 father father father son son father son son father father son son father son 其中每一行分别代表一个进程的运行打印结果。 总结一下规律,对于这种N次循环的情况,执行printf函数的次数为2(1+2+4+……+2N-1)次,创建的子进程数为1+2+4+……+2N-1个。(感谢gao_jiawei网友指出的错误,原本我的结论是“执行printf函数的次数为2(1+2+4+……+2N)次,创建的子进程数为1+2+4+……+2N ”,这是错的) 网上有人说N次循环产生2*(1+2+4+……+2N)个进程,这个说法是不对的,希望大家需要注意。数学推理见http://202.117.3.13/wordpress/?p=81(该博文的最后)。 同时,大家如果想测一下一个程序中到底创建了几个子进程,最好的方法就是调用printf函数打印该进程的pid,也即调用printf(“%d/n”,getpid());或者通过printf(“+/n”);来判断产生了几个进程。有人想通过调用printf(“+”);来统计创建了几个进程,这是不妥当的。具体原因我来分析。 老规矩,大家看一下下面的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/* 
*  fork_test.c 
*  version 4
*/  
#include <unistd.h>  
#include <stdio.h>  
int main() {  
pid_t fpid;//fpid表示fork函数返回的值  
//printf("fork!");  
printf("fork!/n");  
fpid = fork();  
if (fpid < 0)  
printf("error in fork!");  
else if (fpid == 0)  
printf("I am the child process, my process id is %d/n", getpid());  
else  
printf("I am the parent process, my process id is %d/n", getpid());  
return 0;  
}  

执行结果如下: fork! I am the parent process, my process id is 3361 I am the child process, my process id is 3362 如果把语句printf(“fork!/n”);注释掉,执行printf(“fork!”); 则新的程序的执行结果是: fork!I am the parent process, my process id is 3298 fork!I am the child process, my process id is 3299 程序的唯一的区别就在于一个/n回车符号,为什么结果会相差这么大呢? 这就跟printf的缓冲机制有关了,printf某些内容时,操作系统仅仅是把该内容放到了stdout的缓冲队列里了,并没有实际的写到屏幕上。但是,只要看到有/n 则会立即刷新stdout,因此就马上能够打印了。 运行了printf(“fork!”)后,“fork!”仅仅被放到了缓冲里,程序运行到fork时缓冲里面的“fork!” 被子进程复制过去了。因此在子进程度stdout缓冲里面就也有了fork! 。所以,你最终看到的会是fork! 被printf了2次!!!! 而运行printf(“fork! /n”)后,“fork!”被立即打印到了屏幕上,之后fork到的子进程里的stdout缓冲里不会有fork! 内容。因此你看到的结果会是fork! 被printf了1次!!!! 所以说printf(“+”);不能正确地反应进程的数量。 大家看了这么多可能有点疲倦吧,不过我还得贴最后一份代码来进一步分析fork函数。

1
2
3
4
5
6
7
8
9
10
 
#include <stdio.h>  
#include <unistd.h>  
int main(int argc, char* argv[])  
{  
fork();  
fork() && fork() || fork();  
fork();  
return 0;  
}  

问题是不算main这个进程自身,程序到底创建了多少个进程。 为了解答这个问题,我们先做一下弊,先用程序验证一下,到此有多少个进程。 [c-sharp] view plain copy #include int main(int argc, char* argv[]) { fork(); fork() && fork() || fork(); fork(); printf("+/n"); } 答案是总共20个进程,除去main进程,还有19个进程。 我们再来仔细分析一下,为什么是还有19个进程。 第一个fork和最后一个fork肯定是会执行的。 主要在中间3个fork上,可以画一个图进行描述。 这里就需要注意&&和||运算符。 A&&B,如果A=0,就没有必要继续执行&&B了;A非0,就需要继续执行&&B。 A||B,如果A非0,就没有必要继续执行||B了,A=0,就需要继续执行||B。 fork()对于父进程和子进程的返回值是不同的,按照上面的A&&B和A||B的分支进行画图,可以得出5个分支。加上前面的fork和最后的fork,总共4*5=20个进程,除去main主进程,就是19个进程了。一、fork()函数 在操作系统的基本概念中进程是程序的一次执行,且是拥有资源的最小单位和调度单位(在引入线程的操作系统中,线程是最小的调度单位)。在Linux系统中创建进程有两种方式:一是由操作系统创建,二是由父进程创建进程(通常为子进程)。系统调用函数fork()是创建一个新进程的唯一方式,当然vfork()也可以创建进程,但是实际上其还是调用了fork()函数。fork()函数是Linux系统中一个比较特殊的函数,其一次调用会有两个返回值,下面是fork()函数的声明: #include // On success, The PID of the process is returned in the parent, and 0 is returned in the child. On failure, // -1 is returned in the parent, no child process is created, and errno is set appropriately. pid_t fork (void);当程序调用fork()函数并返回成功之后,程序就将变成两个进程,调用fork()者为父进程,后来生成者为子进程。这两个进程将执行相同的程序文本,但却各自拥有不同的栈段、数据段以及堆栈拷贝。子进程的栈、数据以及栈段开始时是父进程内存相应各部分的完全拷贝,因此它们互不影响。从性能方面考虑,父进程到子进程的数据拷贝并不是创建时就拷贝了的,而是采用了写时拷贝(copy-on -write)技术来处理。调用fork()之后,父进程与子进程的执行顺序是我们无法确定的(即调度进程使用CPU),意识到这一点极为重要,因为在一些设计不好的程序中会导致资源竞争,从而出现不可预知的问题。下图为写时拷贝技术处理前后的示意图: 在Linux系统中,常常存在许多对文件的操作,fork()的执行将会对文件操作带来一些小麻烦。由于子进程会将父进程的大多数数据拷贝一份,这样在文件操作中就意味着子进程会获得父进程所有文件描述符的副本,这些副本的创建方式类似于dup()函数调用,因此父、子进程中对应的文件描述符均指向相同的打开的文件句柄,而且打开的文件句柄包含着当前文件的偏移量以及文件状态标志,所以在父子进程中处理文件时要考虑这种情况,以避免文件内容出现混乱或者别的问题。下图为执行fork()调用后文件描述符的相关处理及其变化:二、线程 与进程类似,线程(thread)是允许应用程序并发执行多个任务的一种机制。一个进程中可以包含多个线程,同一个程序中的所有线程均会独立执行,且共享同一份全局内存区域,其中包括初始化数据段(initialized data),未初始化数据段(uninitialized data),以及堆内存段(heap segment)。在多处理器环境下,多个线程可以同时执行,如果线程数超过了CPU的个数,那么每个线程的执行顺序将是无法确定的,因此对于一些全局共享数据据需要使用同步机制来确保其的正确性。 在系统中,线程也是稀缺资源,一个进程能同时创建多少个线程这取决于地址空间的大小和内核参数,一台机器可以同时并发运行多少个线程也受限于CPU的数目。在进行程序设计时,我们应该精心规划线程的个数,特别是根据机器CPU的数目来设置工作线程的数目,并为关键任务保留足够的计算资源。如果你设计的程序在背地里启动了额外的线程来执行任务,那这也属于资源规划漏算的情况,从而影响关键任务的执行,最终导致无法达到预期的性能。很多程序中都存在全局对象,这些全局对象的初始化工作都是在进入main()函数之前进行的,为了能保证全局对象的安全初始化(按顺序的),因此在程序进入main()函数之前应该避免线程的创建,从而杜绝未知错误的发生。三、fork()与多线程 在程序中fork()与多线程的协作性很差,这是POSIX系列操作系统的历史包袱。因为长期以来程序都是单线程的,fork()运转正常。当20世纪90年代初期引入线程之后,fork()的适用范围就大为缩小了。 在多线程执行的情况下调用fork()函数,仅会将发起调用的线程复制到子进程中。(子进程中该线程的ID与父进程中发起fork()调用的线程ID是一样的,因此,线程ID相同的情况有时我们需要做特殊的处理。)也就是说不能同时创建出于父进程一样多线程的子进程。其他线程均在子进程中立即停止并消失,并且不会为这些线程调用清理函数以及针对线程局部存储变量的析构函数。这将导致下列一些问题:

  1. 虽然只将发起fork()调用的线程复制到子进程中,但全局变量的状态以及所有的pthreads对象(如互斥量、条件变量等)都会在子进程中得以保留,这就造成一个危险的局面。例如:一个线程在fork()被调用前锁定了某个互斥量,且对某个全局变量的更新也做到了一半,此时fork()被调用,所有数据及状态被拷贝到子进程中,那么子进程中对该互斥量就无法解锁(因为其并非该互斥量的属主),如果再试图锁定该互斥量就会导致死锁,这是多线程编程中最不愿意看到的情况。同时,全局变量的状态也可能处于不一致的状态,因为对其更新的操作只做到了一半对应的线程就消失了。fork()函数被调用之后,子进程就相当于处于signal handler之中,此时就不能调用线程安全的函数(用锁机制实现安全的函数),除非函数是可重入的,而只能调用异步信号安全(async-signal-safe)的函数。fork()之后,子进程不能调用: malloc(3)。因为malloc()在访问全局状态时会加锁。 任何可能分配或释放内存的函数,包括new、map::insert()、snprintf() …… 任何pthreads函数。你不能用pthread_cond_signal()去通知父进程,只能通过读写pipe(2)来同步。 printf()系列函数,因为其他线程可能恰好持有stdout/stderr的锁。 除了man 7 signal中明确列出的“signal安全”函数之外的任何函数。
  2. 因为并未执行清理函数和针对线程局部存储数据的析构函数,所以多线程情况下可能会导致子进程的内存泄露。另外,子进程中的线程可能无法访问(父进程中)由其他线程所创建的线程局部存储变量,因为(子进程)没有任何相应的引用指针。由于这些问题,推荐在多线程程序中调用fork()的唯一情况是:其后立即调用exec()函数执行另一个程序,彻底隔断子进程与父进程的关系。由新的进程覆盖掉原有的内存,使得子进程中的所有pthreads对象消失。 对于那些必须执行fork(),而其后又无exec()紧随其后的程序来说,pthreads API提供了一种机制:fork()处理函数。利用函数pthread_atfork()来创建fork()处理函数。当一个多线程程序 fork(2) 之后 fork(2) 程序 创建了当前进程的副本,包括所有的内存页,还有打开文件的句柄等。所有这些工作对于一个 UNIX 程序员来说,都不陌生。子进程和父进程之间一个非常重要的区别是,子进程只有一个线程。 一个程序员也许不希望复制包括所有线程在内的整个进程,而且,这也容易出问题。想想:所有的线程都因为一个系统调用(这里指的是 fork(2))而被暂停(Suspended)。所以,fork(2) 仅仅会复制调用它的那个线程。那么(当前的实现方式)会遇到什么问题呢?关键部分,互斥锁(mutex) 这种做法一个潜在的问题是,当 fork(2) 被调用的时候,某些线程可以正在执行关键部分的代码,在互斥锁的保护下对数据进行非原子操作。在子进程里,这些线程消失了,只留下一些修改到一半却没有可能“修正”的数据,不可能去确定 “其他线程正在做什么”和“怎么做可以保持数据一致”。此外,那些(复制过来的互斥锁)的状态是未定义,他们也许不能用(unusable),除非子进程调用 pthread_mutex_init() 去重置他们的状态为一个可用的值。它( pthread_mutex_init() )的实现取决于互斥锁在 fork(2) 执行之后的具体行为。在我的 Linux 机器上,被锁定(locked)的互斥锁的状态(重置之后)在子进程中仍是(locked)。库函数 上面关于互斥锁和关键代码的问题,又引出了另一个潜在的问题。理论上,写一些在多线程上运行并且在调用 fork(2) 之后不会出错的代码,是可行的。但是,实践中,却有一个问题──库函数。你不能确认你正在用的库函数不会使用到全局数据。即使它(用到的库函数)是线程安全的,它也可能是通过在内部使用互斥锁来达到目的。你永远无法确认。即使系统的线程安全的库函数,也可能使用了互斥锁。一个潜在的例子是,malloc() 函数,至少在我的多线程程序里,内部使用了锁。所以,在其他线程调用 malloc() 的时候调用 fork(2) 是不安全的!一般来说,我们应该怎么做呢?在一个多线程程序调用 fork(2) 之后,你只应该调用异步安全(async-safe)的函数(在signal(7) http://www.kernel.org/doc/man-pages/online/pages/man7/signal.7.html 列出)。这个列表与你在一个消息回调函数(signal hanlder)里面可以调用的函数的列表是相似的,而原因也相似:在两种情况下,在调用一个函数时,线程会被终止(原文为带引号的interrupted,由于该线程在新的子进程里已经不存在,所以翻译为终止)。这里是几个在我的系统里,使用类内部锁的函数,仅仅是想让你知道,几乎没有东西是安全的:* malloc()* stdio的函数,比如printf() - 这是标准要求的* syslog()execve() 和文件句柄 似乎使用 execve(2) 来启动一个需要调用fork(2)的多线程程序,是你唯一明智的选择。但即使这样做,也还有一点不足。当调用execve(2) 时,需要注意的是,打开的文件句柄还将维持打开的状态(在新的子进程中 —— 译者Xorcerer),可以继续被读取和写入数据。你在调用 execve(2) 之前打开了一个你不希望在新的子进程里被使用的文件,问题就出现了。这甚至会产生安全方便的问题。对此,有一个解决方案,你必须使用 fcntl(2) 来对每一个打开的文件句柄设施 FD_CLOEXEC 标记,这样,它们会在新的进程中被自动关闭。不幸的是,在多线程程序里,这没那么简单。当我们使用 fcntl(2) 去设置 FD_CLOEXEC 时,会有一个间隙: fd = open (“file”, O_RDWR | O_CREAT | O_TRUNC,0600); if(fd <0){
    perror (“open()”); return0;
    } fcntl (fd, F_SETFD, FD_CLOEXEC);
    如果另一个线程正好在当前线程执行 open(2) 之后 fcntl(2) 之前调用 fork(2) 和 execve(2) ,那么得到的新进程将获得这个文件句柄的副本。这不是我们想要的。一个解决方案已经随着新标准(如:POSIX.1-2008)和新的 Linux 内核(2.6.23以及之后的版本)到来了。我们现在可以在 open(2) 使用 O_CLOEXEC 标记,所以,“开打文件与设置 FD_CLOEXEC” 已经成为了一个原子操作。除了使用 open(2) 之外,还有其他的创建文件句柄的方法:使用 dup(2) 复制它们,使用 socket(2) 创建socket,等。所有这些函数现在都有一个相似的标记如O_CLOEXEC或者其他更新的版本(其中某些函数,如dup2(2)没有一个用于标记位的参数,所以dup3(2)为此产生了)。值得提到的一点是同样的东西在单线程程序里也可能发生,如果它在同一个消息处理函数(singal handler)中使用 fork(2) 和 execve(2) 。这个操作是完全合法的,因为这两个函数是异步安全并且允许在消息处理函数中被调用,但是问题是这个程序也许会在调用 open(2) 和 fcntl(2) 之间时,被中断。想知道更多关于设置 FD_CLOEXEC 新API的信息,请参考 《Ulrich Drepper’s blog: Secure File Descriptor Handling》。一个有用的系统函数:pthread_atfork() 其中一个尝试解决多线程程序中使用 fork(2) 的问题的函数是 pthread_atfork()。它拥有如下原型: int pthread_atfork(void (prepare)(void), void (parent)(void), void (*child)(void)); 它允许指定在 fork 被调用时的处理函数:prepare 新进程产生之前被调用。 parent 新进程产生之后在父进程被调用。 child 新进程产生之后,在子进程被调用。 调用的目的是在 fork(2) 被调用时,处理多线程程序的关键部分(本文开始部分提及)。一个常见的场景时在 prepare 处理函数中加锁,在 parent 处理函数解锁和在 child 处理函数重新初始化锁。
阅读全文

Linux进程控制——exec函数族

在Linux中,并不存在exec()函数,exec指的是一组函数,一共有6个,分别是: #include extern char **environ; int execl(const char *path, const char *arg, ...); int execlp(const char *file, const char *arg, ...); int execle(const char *path, const char *arg, ..., char * const envp[]); int execv(const char *path, char *const argv[]); int execvp(const char *file, char *const argv[]); int execve(const char *path, char *const argv[], char *const envp[]); 其中只有execve是真正意义上的系统调用,其它都是在此基础上经过包装的库函数。 exec函数族的作用是根据指定的文件名找到可执行文件,并用它来取代调用进程的内容,换句话说,就是在调用进程内部执行一个可执行文件。这里的可执行文件既可以是二进制文件,也可以是任何Linux下可执行的脚本文件。 函数名与参数的关系: 细看一下,这6个函数都是以exec开头(表示属于exec函数组),前3个函数接着字母l的,后3个接着字母v的,我的理解是l表示list(列举参数),v表示vector(参数向量表) 。它们的区别在于,execv开头的函数是以"char *argv[]"(vector)形式传递命令行参数,而execl开头的函数采用了罗列(list)的方式,把参数一个一个列出来,然后以一个NULL表示结束。这里的NULL的作用和argv数组里的NULL作用是一样的。 字母p是指在环境变量PATH的目录里去查找要执行的可执行文件。2个以p结尾的函数execlp和execvp,看起来,和execl与execv的差别很小,事实也如此,它们的区别从第一个参数名可以看出:除 execlp和execvp之外的4个函数都要求,它们的第1个参数path必须是一个完整的路径,如"/bin/ls";而execlp和execvp 的第1个参数file可以仅仅只是一个文件名,如"ls",这两个函数可以自动到环境变量PATH指定的目录里去查找。 字母e是指给可执行文件指定环境变量。在全部6个函数中,只有execle和execve使用了char *envp[]传递环境变量,其它的4个函数都没有这个参数,这并不意味着它们不传递环境变量,这4个函数将把默认的环境变量不做任何修改地传给被执行的应用程序。而execle和execve用指定的环境变量去替代默认的那些。 返回值 与一般情况不同,exec函数族的函数执行成功后不会返回,因为调用进程的实体,包括代码段,数据段和堆栈等都已经被新的内容取代,只有进程ID等一些表面上的信息仍保持原样。调用失败时,会设置errno并返回-1,然后从原程序的调用点接着往下执行。 与其他系统调用比起来,exec很容易失败,被执行文件的位置,权限等很多因素都能导致调用失败。因此,使用exec函数族时,一定要加错误判断语句。最常见的错误: 找不到文件或路径,此时errno被设置为ENOENT; 数组argv和envp忘记用NULL结束,此时errno被设置为EFAULT; 没有对要执行文件的运行权限,此时errno被设置为EACCES。 2、应用 如果一个进程想执行另一个程序,它就可以fork或vfork出一个新进程,然后调用任何一个exec函数。 为此,Linux还专门对fork作了优化:通常fork会将调用进程的所有内容原封不动的拷贝到新产生的子进程中去,这些拷贝的动作很消耗时 间,而如果fork完之后我们马上就调用exec,那这些辛辛苦苦拷贝来的东西就会被立刻抹掉,这看起来非常不划算,于是人们设计了一种"写时复制(copy-on-write)" 技术,使得fork结束后并不立刻复制父进程的内容到子进程,而是到了真正使用时才复制,这样如果下一条语句是exec,它就不会作无用功了。其实"写时 复制"还是有复制,进程的mm结构、页表都还是被复制了("写时复制"也必须由这些信息来支撑。否则内核捕捉到CPU访存异常,怎么区分 这是“写时复制”引起的,还是真正的越权访问呢?)。 而vfork就把事情做绝了,所有有关于内存的东西都不复制了,父子进程的内存是完全共享的。 但是这样一来又有问题了,虽然用户程序可以设计很多方法来避免父子进程间的访存冲突。但是关键的一点,父子进程共用着栈,这可不由用户程序控制的。一个进 程进行了关于函数调用或返回的操作,则另一个进程的调用栈 (实际上就是同一个栈)也被影响了。这样的程序没法运行下去。所以,vfork有个限制,子进程生成后,父进程在vfork中被内核挂起,直到子进程有了 自己的内存空间(exec**)或退出(_exit)。并且, 在此之前,子进程不能从调用vfork的函数中返回(同时,不能修改栈上变量、不能继续调用除_exit或exec系列之外的函数,否则父进程的数据可能 被改写)。 尽管限制很多,vfork后马上exec效率会比fork高不少。 fork函数是用于创建一个子进程,该子进程几乎是父进程的副本,而有时我们希望子进程去执行另外的程序,exec函数族就提供了一个在进程中启动另一个程序执行的方法。它可以根据指定的文件名或目录名找到可执行文件,并用它来取代原调用进程的数据段、代码段和堆栈段,在执行完之后,原调用进程的内容除了进程号外,其他全部被新程序的内容替换了。另外,这里的可执行文件既可以是二进制文件,也可以是Linux下任何可执行脚本文件。 (2)在Linux中使用exec函数族主要有以下两种情况 a. 当进程认为自己不能再为系统和用户做出任何贡献时,就可以调用任何exec 函数族让自己重生。 b. 如果一个进程想执行另一个程序,那么它就可以调用fork函数新建一个进程,然后调用任何一个exec函数使子进程重生。 (3)exec函数族语法 实际上,在Linux中并没有exec函数,而是有6个以exec开头的函数族,下表列举了exec函数族的6个成员函数的语法。 所需头文件: #include 函数说明: 执行文件 函数原型: [plain] view plain copy int execl(const char *path, const char *arg, ...) int execv(const char *path, char *const argv[]) int execle(const char *path, const char *arg, ..., char *const envp[]) int execve(const char *path, char *const argv[], char *const envp[]) int execlp(const char *file, const char *arg, ...) int execvp(const char *file, char *const argv[])

阅读全文

radix tree

基数树

阅读全文

truss、strace或ltrace

truss和strace用来 跟踪一个进程的系统调用或信号产生的情况,而 ltrace用来 跟踪进程调用库函数的情况。truss是早期为System V R4开发的调试程序,包括Aix、FreeBSD在内的大部分Unix系统都自带了这个工具;而strace最初是为SunOS系统编写的,ltrace最早出现在GNU/Debian Linux中。这两个工具现在也已被移植到了大部分Unix系统中,大多数Linux发行版都自带了strace和ltrace,而FreeBSD也可通过Ports安装它们。 你不仅可以从命令行调试一个新开始的程序,也可以把truss、strace或ltrace绑定到一个已有的PID上来调试一个正在运行的程序。三个调试工具的基本使用方法大体相同,下面仅介绍三者共有,而且是最常用的三个命令行参数: -f :除了跟踪当前进程外,还跟踪其子进程。 -o file :将输出信息写到文件file中,而不是显示到标准错误输出(stderr)。 -p pid :绑定到一个由pid对应的正在运行的进程。此参数常用来调试后台进程。 使用上述三个参数基本上就可以完成大多数调试任务了,下面举几个命令行例子: truss -o ls.truss ls -al: 跟踪ls -al的运行,将输出信息写到文件/tmp/ls.truss中。 strace -f -o vim.strace vim: 跟踪vim及其子进程的运行,将输出信息写到文件vim.strace。 ltrace -p 234: 跟踪一个pid为234的已经在运行的进程。 三个调试工具的输出结果格式也很相似,以strace为例: brk(0) = 0x8062aa8 brk(0x8063000) = 0x8063000 mmap2(NULL, 4096, PROT_READ, MAP_PRIVATE, 3, 0x92f) = 0x40016000 每一行都是一条系统调用,等号左边是系统调用的函数名及其参数,右边是该调用的返回值。 truss、strace和ltrace的工作原理大同小异,都是使用ptrace系统调用跟踪调试运行中的进程,详细原理不在本文讨论范围内,有兴趣可以参考它们的源代码。 每一行都是一条系统调用,等号左边是系统调用的函数名及其参数,右边是该调用的返回值。 strace 显示这些调用的参数并返回符号形式的值。strace 从内核接收信息,而且不需要以任何特殊的方式来构建内核。 -c 统计每一系统调用的所执行的时间,次数和出错的次数等. -d 输出strace关于标准错误的调试信息. -f 跟踪由fork调用所产生的子进程. -ff 如果提供-o filename,则所有进程的跟踪结果输出到相应的filename.pid中,pid是各进程的进程号. -F 尝试跟踪vfork调用.在-f时,vfork不被跟踪. -h 输出简要的帮助信息. -i 输出系统调用的入口指针. -q 禁止输出关于脱离的消息. -r 打印出相对时间关于,,每一个系统调用. -t 在输出中的每一行前加上时间信息. -tt 在输出中的每一行前加上时间信息,微秒级. -ttt 微秒级输出,以秒了表示时间. -T 显示每一调用所耗的时间. -v 输出所有的系统调用.一些调用关于环境变量,状态,输入输出等调用由于使用频繁,默认不输出. -V 输出strace的版本信息. -x 以十六进制形式输出非标准字符串 -xx 所有字符串以十六进制形式输出. -a column 设置返回值的输出位置.默认 为40. -e expr 指定一个表达式,用来控制如何跟踪.格式如下: [qualifier=][!]value1[,value2]… qualifier只能是 trace,abbrev,verbose,raw,signal,read,write其中之一.value是用来限定的符号或数字.默认的 qualifier是 trace.感叹号是否定符号.例如: -eopen等价于 -e trace=open,表示只跟踪open调用.而-etrace!=open表示跟踪除了open以外的其他调用.有两个特殊的符号 all 和 none. 注意有些shell使用!来执行历史记录里的命令,所以要使用\. -e trace=set 只跟踪指定的系统 调用.例如:-e trace=open,close,rean,write表示只跟踪这四个系统调用.默认的为set=all. -e trace=file 只跟踪有关文件操作的系统调用. -e trace=process 只跟踪有关进程控制的系统调用. -e trace=network 跟踪与网络有关的所有系统调用. -e strace=signal 跟踪所有与系统信号有关的 系统调用 -e trace=ipc 跟踪所有与进程通讯有关的系统调用 -e abbrev=set 设定 strace输出的系统调用的结果集.-v 等与 abbrev=none.默认为abbrev=all. -e raw=set 将指 定的系统调用的参数以十六进制显示. -e signal=set 指定跟踪的系统信号.默认为all.如 signal=!SIGIO(或者signal=!io),表示不跟踪SIGIO信号. -e read=set 输出从指定文件中读出 的数据.例如: -e read=3,5 -e write=set 输出写入到指定文件中的数据. -o filename 将strace的输出写入文件filename -p pid 跟踪指定的进程pid. -s strsize 指定输出的字符串的最大长度.默认为32.文件名一直全部输出. -u username 以username 的UID和GID执行被跟踪的命令 通用的完整用法:

阅读全文

netlink

Linux中的进程间通信机制源自于Unix平台上的进程通信机制。Unix的两大分支AT&T Unix和BSD Unix在进程通信实现机制上的各有所不同,前者形成了运行在单个计算机上的System V IPC,后者则实现了基于socket的进程间通信机制。同时Linux也遵循IEEE制定的Posix IPC标准,在三者的基础之上实现了以下几种主要的IPC机制:管道(Pipe)及命名管道(Named Pipe),信号(Signal),消息队列(Message queue),共享内存(Shared Memory),信号量(Semaphore),套接字(Socket)。通过这些IPC机制,用户空间进程之间可以完成互相通信。为了完成内核空间与用户空间通信,Linux提供了基于socket的Netlink通信机制,可以实现内核与用户空间数据的及时交换。 本文第2节概述相关研究工作,第3节与其他IPC机制对比,详细介绍Netlink机制及其关键技术,第4节使用KGDB+GDB组合调试,通过一个示例程序演示Netlink通信过程。第5节做总结并指出Netlink通信机制的不足之处。 2 相关研究 到目前Linux提供了9种机制完成内核与用户空间的数据交换,分别是内核启动参数、模块参数与 sysfs、sysctl、系统调用、netlink、procfs、seq_file、debugfs和relayfs,其中模块参数与sysfs、procfs、debugfs、relayfs是基于文件系统的通信机制,用于内核空间向用户控件输出信息;sysctl、系统调用是由用户空间发起的通信机制。由此可见,以上均为单工通信机制,在内核空间与用户空间的双向互动数据交换上略显不足。Netlink是基于socket的通信机制,由于socket本身的双共性、突发性、不阻塞特点,因此能够很好的满足内核与用户空间小量数据的及时交互,因此在Linux 2.6内核中广泛使用,例如SELinux,Linux系统的防火墙分为内核态的netfilter和用户态的iptables,netfilter与iptables的数据交换就是通过Netlink机制完成。 3 Netlink机制及其关键技术 3.1 Netlink机制

阅读全文

linux sysfs

在调试驱动,或驱动涉及一些参数的输入输出时,难免需要对驱动里的某些变量或内核参数进行读写,或函数调用。此时sysfs接口就很有用了,它可以使得可以在用户空间直接对驱动的这些变量读写或调用驱动的某些函数。sysfs接口与proc文件系统很相似,有人将proc文件系统形容为Windows XP,而将sysfs接口形容为Windows 7。 而在Android系统中,振动器、背光、电源系统等往往使用sysfs接口作为内核空间和用户空间的接口,驱动程序需要提供这些接口内容。

阅读全文

proc文件系统

proc文件系统是一种无存储的文件系统,当读其中的文件时,其内容动态生成,当写文件时,文件所关联的写函数被调用。每个proc文件都关联的字节特定的读写函数,因而它提供了另外的一种和内核通信的机制:内核部件可以通过该文件系统向用户空间提供接口来提供查询信息、修改软件行为,因而它是一种比较重要的特殊文件系统。 由于proc文件系统以文件的形式向用户空间提供了访问接口,这些接口可以用于在运行时获取相关部件的信息或者修改部件的行为,因而它是非常方便的一个接口。内核中大量使用了该文件系统。proc文件系统就是一个文件系统,它可以挂载在目录树的任意位置,不过通常挂载在/proc下,它大致包含了如下信息: 内存管理 每个进程的相关信息 文件系统 设备驱动程序 系统总线 电源管理 终端 系统控制参数 网络 使用proc文件系统之前必须将其初始化并且挂载到系统中。proc文件系统的的初始化主要完成: 调用proc_init_inodecache创建proc文件系统所使用的专用缓冲区 调用register_filesystem注册proc文件系统,这里会提供proc文件系统自己的file_system_type,其中包括了用于mount的函数指针。在执行mount的时候会用到这些信息,并最终找到mount函数进行挂载操作 调用proc_mkdir创建一些proc文件目录 在sys文件系统下注册proc文件系统的相关信息 在proc的mount函数中会调用proc_fill_super,它会给出proc文件系统超级块所需要的信息(比如文件系统的超级块操作函数指针,超级块大小等),并且会创建proc文件系统的根目录,在创建根目录时也会指定与之对应的inode_operations和file_operations,有了这些信息后,VFS就可以在该文件系统上进行各种操作了(创建、删除、查找文件)。

阅读全文

netfliter

Netfilter是Linux 2.4.x引入的一个子系统,它作为一个通用的、抽象的框架,提供一整套的hook函数的管理机制,使得诸如数据包过滤、网络地址转换(NAT)和基于协议类型的连接跟踪成为了可能。 netfilter的架构就是在整个网络流程的若干位置放置了一些检测点HOOK),而在每个检测点上登记了一些处理函数进行处理。

阅读全文

namespace

Linux Namespace

阅读全文

vfs

在Linux中,文件系统主要分为下面3种: (1)基于磁盘的文件系统(Disk-based Filesystem) 是在非易失介质上存储文件的经典方法,用以在多次会话之间保持文件的内容。如Ext2/3/4, Reiserfs, FAT等。 (2)虚拟文件系统(Virtual Filesystem) 在内核中生成,是一种用户应用程序与内核通信的方法。如proc,它不许要在任何类的硬件设备上分配存储空间,所有的信息都是动态在内存中开辟和存储。 (3)网络文件系统(Network Filesystem) 是基于磁盘的文件系统和虚拟文件系统之间的折中。这种文件系统允许访问另一台计算机上的数据,该计算机通过网络连接到本地计算机。在这种情况下,数据实际上存储在一个不同系统的硬件设备上。 由于VFS抽象层的存在,用户空间进程不会看到本地文件系统与网络文件系统之间的区别。

  1. VFS的模型与结构 VFS不仅为文件系统提供了方法和抽象,还支持文件系统中对象(或文件)的统一视图。并非每一种文件系统都支持VFS中的所有抽象,如FAT,因为其设计没有考虑到此类对象。定义一个最小的通用模型,来支持内核中所有文件系统都实现的那些功能,这是不实际的。因为这样会损失许多本质性的功能特性,或者导致这些特性只能通过特定文件系统的路径访问。 VFS的方案完全相反:提供一种结构模型,包含了一个强大文件系统所具备的所有组件。但该模型只存在于虚拟中,必须使用各种对象和函数指针与每种文件系统适配。所有文件系统的实现都必须提供与VFS定义的结构配合的例程,以弥合两种视图之间的差异。 VFS是由基于经典文件系统的结构衍化而来,所以VFS与Ext类文件系统类似,从而在处理Ext类文件系统的时候,Ext和VFS之间的转换,几乎不会损失时间。
阅读全文

linux_rcu

1:RCU使用在读者多而写者少的情况.RCU和读写锁相似.但RCU的读者占锁没有任何的系统开销.写者与写写者之间必须要保持同步,且写者必须要等它之前的读者全部都退出之后才能释放之前的资源. 2:RCU保护的是指针.这一点尤其重要.因为指针赋值是一条单指令.也就是说是一个原子操作.因它更改指针指向没必要考虑它的同步.只需要考虑cache的影响. 3:读者是可以嵌套的.也就是说rcu_read_lock()可以嵌套调用. 4:读者在持有rcu_read_lock()的时候,不能发生进程上下文切换.否则,因为写者需要要等待读者完成,写者进程也会一直被阻塞.

阅读全文

Linux的mmap内存映射机制

一个进程应该包括一个mm_struct(memory manage struct), 该结构是进程虚拟地址空间的抽象描述,里面包括了进程虚拟空间的一些管理信息: start_code, end_code, start_data, end_data, start_brk, end_brk等等信息.另外,也有一个指向进程虚存区表(vm_area_struct: virtual memory area)的指针,该链是按照虚拟地址的增长顺序排列的.在Linux进程的地址空间被分作许多区(vma),每个区(vma)都对应虚拟地址空间上一段连续的区域, vma是可以被共享和保护的独立实体,这里的vma就是前面提到的内存对象.

阅读全文

linux_lock

在现代操作系统里,同一时间可能有多个内核执行流在执行,因此内核其实象多进程多线程编程一样也需要一些同步机制来同步各执行单元对共享数据的访问。尤其是在多处理器系统上,更需要一些同步机制来同步不同处理器上的执行单元对共享的数据的访问。在主流的Linux内核中包含了几乎所有现代的操作系统具有的同步机制,这些同步机制包括:原子操作、信号量(semaphore)、读写信号量(rw_semaphore)、spinlock、 BKL(Big Kernel Lock)、rwlock、brlock(只包含在2.4内核中)、RCU(只包含在2.6内核中)和seqlock(只包含在2.6内核中)。

阅读全文

linux_elf

可执行连接格式是UNIX系统实验室(USL)作为应用程序二进制接口 (Application Binary Interface(ABI)而开发和发布的。工具接口标准委 员会(TIS)选择了正在发展中的ELF标准作为工作在32位INTEL体系上不同操 作系统之间可移植的二进制文件格式。 假定开发者定义了一个二进制接口集合,ELF标准用它来支持流线型的软件 发展。 应该减少不同执行接口的数量。因此可以减少重新编程重新编译的 代码。

阅读全文

linux_cow

在Linux程序中,fork()会产生一个和父进程完全相同的子进程,但子进程在此后多会exec系统调用,出于效率考虑,linux中引入了“写时复制“技术,也就是只有进程空间的各段的内容要发生变化时,才会将父进程的内容复制一份给子进程。

阅读全文

linux_memory

TLB(Translation Lookaside Buffer)转换检测缓冲区是一个内存管理单元,用于改进虚拟地址到物理地址转换速度的缓存。 TLB是一个小的,虚拟寻址的缓存,其中每一行都保存着一个由单个PTE(Page Table Entry,页表项)组成的块。如果没有TLB,则每次取数据都需要两次访问内存,即查页表获得物理地址和取数据。 当cpu要访问一个虚拟地址/线性地址时,CPU会首先根据虚拟地址的高20位(20是x86特定的,不同架构有不同的值)在TLB中查找。如果是表中没有相应的表项,称为TLB miss,需要通过访问慢速RAM中的页表计算出相应的物理地址。同时,物理地址被存放在一个TLB表项中,以后对同一线性地址的访问,直接从TLB表项中获取物理地址即可,称为TLB hit。 Linux把物理内存划分为三个层次来管理 存储节点(Node) CPU被划分为多个节点(node), 内存则被分簇, 每个CPU对应一个本地物理内存, 即一个CPU-node对应一个内存簇bank,即每个内存簇被认为是一个节点 管理区(Zone) 每个物理内存节点node被划分为多个内存管理区域, 用于表示不同范围的内存, 内核可以使用不同的映射方式映射物理内存 页面(Page) 内存被细分为多个页面帧, 页面是最基本的页面分配的单位  为了支持NUMA模型,也即CPU对不同内存单元的访问时间可能不同,此时系统的物理内存被划分为几个节点(node), 一个node对应一个内存簇bank,即每个内存簇被认为是一个节点

阅读全文

脚本输出带颜色文字

文本终端的颜色可以使用“ANSI非常规字符序列”来生成。举例:     echo -e “\033[44;37;5m ME \033[0m COOL”     以上命令设置作用如下:背景色为蓝色,前景色为白色,字体闪烁,输出字符“ME”,然后重新设置屏幕到缺省设置,输出字符 “COOL”。“e”是命令 echo 的一个可选项,它用于激活特殊字符的解析器。“\033”引导非常规字符序列。“m”意味着设置属性然后结束非常规字符序列,这个例子里真正有效的字符是 “44;37;5” 和“0”。修改“44;37;5”可以生成不同颜色的组合,数值和编码的前后顺序没有关系。 可以选择的编码如下所示: 编码 颜色/动作 0 重新设置属性到缺省设置 1 设置粗体 2 设置一半亮度(模拟彩色显示器的颜色) 4 设置下划线(模拟彩色显示器的颜色) 5 设置闪烁 7 设置反向图象 22 设置一般密度 24 关闭下划线 25 关闭闪烁 27 关闭反向图象 30 设置黑色前景 31 设置红色前景 32 设置绿色前景 33 设置棕色前景 34 设置蓝色前景 35 设置紫色前景 36 设置青色前景 37 设置白色前景 38 在缺省的前景颜色上设置下划线 39 在缺省的前景颜色上关闭下划线 40 设置黑色背景 41 设置红色背景 42 设置绿色背景 43 设置棕色背景 44 设置蓝色背景 45 设置紫色背景 46 设置青色背景 47 设置白色背景 49 设置缺省黑色背景   例如:在编译脚本文件时,对服务启动完成后的OK字符串设置成绿色 除了echo还有printf也可以显示颜色,这里就不多说了,用法一样的。那么开始用php调用吧。

阅读全文

Search

Recent posts

This blog is maintained by 夏泽民

Get in touch with me at 465474307@qq.com

Subscribe to our mailing list

* indicates required