闲谈v2ray的负载均衡

由于Oracle之前推出了always free(虽不知这个always会是多久)的云服务器,带宽限制在单台45Mbps左右,在白天电信国际出口不怎么拥塞的时候,还算是不错的一个选择,可以用来节省自己bwg cn2 gia线路为数不多的500G流量(按双向计费的规则来算,实则只有250G)。 由于我在外地是以vmess普通协议接入家中的软路由,软路由上的v2ray再中转统一走ws+tls的方式去科学上网,由于Oracle的机器有两台的免费额度,因此如果能并发用起来,效果应该还是不错的。 然而根据周末的测试结果来看,在软路由上,分别测试Oracle两台机器往家里软路由打的带宽总是超不过50Mbps,这让我觉得有点诡异,思考其中原因,应该是: 由于我的软路由性能一般,且pppoe拨号后,使得网卡的rss机制实际上是无法工作的,因为报文有一层pppoe封装,使得网卡无法看到pppoe里ppp封装的ip报文,因而无法进行多queue处理,查了内核rps的功能,似乎也没有这个功能。因而所有入方向即通常我们从外网的下载流量,都只能由单个cpu核心处理,如果没有做好cpu隔离的话,就会影响到性能。 基于这个思考,且考虑到家里的电信支持多拨,马上在软路由上拨出来别外一个ppp接口,将另外一台云服务器的路由配置到此ppp接口上去。果不其然,并发流量直接搞到了100Mbps,这样从家里内网使用Oracle线路下载文件时,会被v2ray负载分担到两条云主机上,且通过不同的ppp接口出去,下载速度自然会dobule,实为物尽其用也。 写到这些,也谈谈我对负载均衡的理解,我觉得,负载均衡实际上来自于对于一个业务的拆解,比如从http协议的角度来说,http本身的处理,渲染页面实际是比较占cpu的,那么,通过前置一个4层proxy,让这个proxy主要负责tcp的报文中转,就可以将一堆http服务器置于这个proxy的后面,由于proxy只需要处理tcp报文中转,是可以实现比较高的性能的,并且可以通过硬件能力堆上去来scale up提高处理性能,而proxy则可以通过多台scale out来实现匹配到proxy的转发能力,从而提高整体集群的处理能力。 当然,如果在proxy前,再放一台三层交换机,来做下三层的ecmp,这样就又可以实现proxy一层的scale out,整个集群的能力就越来越强了。 附: 1、v2ray的负载均衡文档见: https://toutyrater.github.io/routing/balance2.html 这是该文档作者后来更新的负载均衡版本,先前他给出的方案并不好用,所以作者用了v2ray提供的新方案,我自己也是用这个模式配置的。 2、看起来这内核的维护者似乎也意识到ppp这种封装使得rps无效了? http://vger.kernel.org/~davem/davem_spain10.pdf

March 8, 2020 · 1 min · fortitude.zhang

v2ray kitsunebi客户端与iptables dnat转发

因为手机是不限量套餐,故而在杭州我一直主用自己的电信套餐来做为日常上网的方法,亦无需开通宽带了,然而电信在浙江的漫游策略则是漫游至南京的GGSN设备以便出互联网(江苏移动就比较好,可以直接从浙江本地出去),而该GGSN的上网ip,访问我的科学上网服务器因为中间有跳电信202.97的路由器,从而无法体现我科学上网服务器全程CN2 GIA线路的特质,而我家里电信则可以享用到双程CN2 GIA线程的特质,故而我决定在家里路由器通过iptables dnat直接转发下,这样在杭州可以先连回江苏家中,从而再走国内,这样跨境仍利用我家的双程CN2 GIA线程,效果预计会更好些。 然而在家中路由器直接开启dnat却无法让我直接使用,直到抓包才发现,由于我采用了Caddy做为https来反向代理websocket方式的v2ray服务端,而kitsunebi直接将我家中路由器ddns绑定的域名做为tls 1.2协商所用的sni,而Caddy服务器绑定的域名则是另外一个,这样就导致服务器tls协商不成功,从而无法正常加速。 而kitsunebi这样就要么在android手机上做个dns代理,仍用原来的域名,但通过dns代理为我家里路由器的ip,而这样的玩法较为复杂,而改kitsunebi的android代码,我也觉得较为费功夫;因而或可考虑再在家中的路由器再部署一个https代理来再代理一次,但这样亦较为复杂。 后来经查询发现,原来v2ray自己就有转发模式,这样可以直接在家中路由器开启vmess做为接入协议,出口则去连科学上网服务器的ws+tls服务,这样kitsunebi就可以直接通过vmess接入家中,快速部署了一下,果然成功解决。 实际测试发现下载带宽性能大约能够提升40%,油管更加顺畅,开心看视频去也。

August 8, 2019 · 1 min · fortitude.zhang

主流公有云外网IP实现调研

因工作需要, 尝试分析主流公有云外网IP是否具有较好的隔离性,以下记录下分析结果。 主流厂商方案调研 aws aws的实例可获得一个动态的public ip(重启后会变)以及不变的elastic ip,这两种ip官方文档上明确给出是属于1:1 NAT,在vm内部仍只能看到私有网地址,使vm只能访问内网及外网,不能向同外网子网的机器注入广播报文,具有较好的隔离安全性。 aws的私有网(即EC2-VPC)做了较多的限制,在FAQ中明确给出不支持组播及广播,在网络上也看到有其他基于第三方软件规避此问题的方案。 aws的网络实现经由了EC2-Classic到EC2-VPC的转变,目前主推EC2-VPC,提供虚拟子网功能。 相关资料: https://aws.amazon.com/articles/1346 https://aws.amazon.com/vpc/faqs/?nc1=h_ls https://www.ravellosystems.com/blog/advanced-enterprise-networking-in-aws-ec2/ https://www.lisenet.com/2014/create-and-attach-a-second-elastic-network-interface-with-eip-to-ec2-vpc-instance/ microsoft azure azure具有两种创建虚拟机模式,注册了试用账号,并结合文档,总结外网在不同模式下实现如下: Azure Resource Manager(v2版本,以资源方式管理一组vm): 默认动态外网ip,可配置成静态外网ip(需关机重开机) ;此种模式下无法动态或静态ip,在内部只能看到一个内网ip的接口。 Azure Service Management(v1版本,称为classic deployment,以服务方式管理一组vm):默认无外网ip,需通过service的vip对外呈现,需要手工打开vip端口,并关联后端vm dip端口;可设置实例级别的外网ip,称为ilpip,vip本身在vm内部无网卡,ilpip也同样无网卡。 也即无论是v1/v2版本的部署模式,无论是共享的vip还是独立的public ip或ilpip,在vm内部均不可见,类似于floating ip技术,即vm只能访问内网及外网,不能向同外网子网的机器注入广播报文,具有较好的隔离安全性。 而对于私有网,二层做了大量的限制,比如收方向收不到不是到自己地址的报文(含L2/L3过滤),对ARP及DHCP进行了速率限制且做了spoofing处理,组网、广播、udp组播明确表明不支持(详见下附资料)。 相关资料: https://azure.microsoft.com/en-us/documentation/articles/virtual-network-ip-addresses-overview-arm/ https://azure.microsoft.com/en-in/documentation/articles/virtual-networks-faq/ https://blogs.msdn.microsoft.com/igorpag/2014/09/28/my-personal-azure-faq-on-azure-networking-slas-bandwidth-latency-performance-slb-dns-dmz-vnet-ipv6-and-much-more/ https://blogs.msdn.microsoft.com/mast/2016/02/04/azure-networking-public-ip-addresses-in-classic-vs-arm/ google cloud engine 从GCE的官方文档来看,其实例的external public ip基于NAT实现,即vm只能访问内网及外网,不能向同外网子网的机器注入广播报文,具有较好的隔离安全性。 其子网仅支持IPv4单播,不支持IPv4组播及广播,而其资料上提到子网内ARP通过代理予以回复,综合来看,二层组播及广播显然不支持,且在其网络中,也不会存在未知单播的情况。 GCE的网络实现与AWS类似,经由了legacy network到subnet network的变化。 相关资料: https://cloud.google.com/compute/docs/networking aliyun 根据资料及试用结果,aliyun classical network中云主机具有两个接口,外网接口(/22)直接暴露给用户,通过tcpdump可以监听到来自于其他节点及网关的ARP请求,说明未做了隔离,而VPC网络则进行了改进,云主机只有一个接口,外网采用1:1 NAT实现,在vm内部仍只能看到私有网地址,使vm只能访问内网及外网,不能向同外网子网的机器注入广播报文,具有较好的隔离安全性 aliyun同样经过了classic network到VPC网络的改进。 相关资料: http://docs-aliyun-com-cn-b.oss-cn-hangzhou.aliyuncs.com/vpc/pdf/vpc_faq.pdf http://blog.chinaunix.net/uid-28212952-id-5153991.html ucloud 根据上次在为上海客户基于ucloud临时搭建的操作经历,以及Ucloud所公开的设计细节,其EIP同样采用1:1 NAT实现,在vm内部仍只能看到私有网地址,使vm只能访问内网及外网,不能向同外网子网的机器注入广播报文,具有较好的隔离安全性。 相关资料: http://www.infoq.com/cn/articles/UCloud-Sixshot-1 tencent cloud 根据在腾讯云主机的实际验证,linux云主机内部仅有一个接口,具有一个/18子网的内网地址(10.X.X.X),且根据其资料所示,腾读云方外网IP同样采用1:1实现,使vm只能访问内网及外网,不能向同外网子网的机器注入广播报文,具有较好的隔离安全性。 腾讯云EIP 15年底发布,之前与aliyun一样,仍是实例级的额外接口配置公网IP,相当于经过了优化。 相关资料: ...

August 25, 2016 · 1 min · fortitude.zhang

linux max open files限制过小导致openvswitch工作异常

上篇分析了linux内核的netdev_max_backlog默认设置导致了openvswitch在大量virtual port转发异常的问题,本篇再额外记录一处与大量virtual port相关的调优点。 自openvswitch 2.x版本开始,ovs引入了多线程的支持,在非dpdk即传统的ovs应用模式下,多线程主要用于处理内核fastpath lookup miss的流表,此过程称为upcall;并对这些流表进行统计、老化处理,此过程称为revalidate。 upcall和revalidate的线程数量计算,以openvswitch 2.3.x为例,代码中的判断是将物理核数约3/4分配给upcall,而revalidate则占1/4,这个分配直观上是合理的,毕竟upcall处理转发,要尽可能的快,处理能力也要更强。 而我们知道openvswitch datapath对用户态的每个virtual port都会在linux内核datapath中创建一个vport,内核datapath通过netlink发送消息给upcall,为了更快的处理每个vport的upcall请求,openvswitch将会为每个virtual port分配等同upcall线程数量的netlink socket,内核datapath根据flow hash将同一vport的upcall分发给不同的netlink socket,实现不同upcall线程间的并发处理,提升upcall性能。 从这里可以看出,openvswitch在优化性能方面确实做足了功夫,但这里确存在一个值得考虑的地方,我们知道linux process存在一个max open files的限制,当限制达到后,新的fd将无法被分配,导致建立文件、创建socket等需要用到fd资源的操作无法执行。 那么我们可以估计一个24 physical core的系统上,如果有2000个virtual port,openvswitch将会有多少个netlink socket,24 3/4 2000即36000,也即单音用于处理vport的netlink socket就占用了36000个fd,如果对openvswitch的max open files限制过小,很容易导致在大量virtual port下,出现各种工作异常。 对于此问题主要的优化思路如下: 调整ovs-vswitchd进程的max open files limit上限,可按照前述算法估量 openvswitch提供了一个n-handler-threads的配置,可通过ovs-vsctl持久化配置,根据性能需求适当的减少upcall线程数量

April 24, 2016 · 1 min · fortitude.zhang

linux内核netdev_max_backlog设置在极端情况下导致openvswitch转发不通

近期在调试问题时,遇到了一个从配置上看openvswitch无任何问题,但却导致转发不通的问题,特此记录下说,说明在openvswitch在大规模部署时,仍需要有较多调优之处。 设想如下openstack场景,大量tenant router通过linux namespace即仿真vrouter访问外网,此namespace中通过openvswitch internal port(或veth,如果不追求转发性能)连接到另一公网namespace,此时如果公网namespace中接口数量过多,比如说2000个(比如openstack中的qg-*接口),将有可能面临转发不通的情况。 从openvswitch的角度,我们知道openvswitch在内核态存在一个datapath,负责生成内核流表,实现高性能转发,对于一个拥有上千个port的ovs bridge来说,比如对应于上面的公网namespace,收到一个arp请求报文,由于是广播,报文通守openvswitch在userspace命中默认的normal流表后将推送广播至其他所有成员端口的内核流表,此后arp报文广播复制逻辑将在内核datapath模块处执行,最终命中以下内核代码: :::c /* * enqueue_to_backlog is called to queue an skb to a per CPU backlog * queue (may be a remote CPU queue). */ static int enqueue_to_backlog(struct sk_buff *skb, int cpu, unsigned int *qtail) { struct softnet_data *sd; unsigned long flags; sd = &per_cpu(softnet_data, cpu); local_irq_save(flags); rps_lock(sd); if (skb_queue_len(&sd->input_pkt_queue) <= netdev_max_backlog) { if (skb_queue_len(&sd->input_pkt_queue)) { enqueue: __skb_queue_tail(&sd->input_pkt_queue, skb); input_queue_tail_incr_save(sd, qtail); rps_unlock(sd); local_irq_restore(flags); return NET_RX_SUCCESS; } /* Schedule NAPI for backlog device * We can use non atomic operation since we own the queue lock */ if (!__test_and_set_bit(NAPI_STATE_SCHED, &sd->backlog.state)) { if (!rps_ipi_queued(sd)) ____napi_schedule(sd, &sd->backlog); } goto enqueue; } sd->dropped++; rps_unlock(sd); local_irq_restore(flags); atomic_long_inc(&skb->dev->rx_dropped); kfree_skb(skb); return NET_RX_DROP; } 可以注意到在上面的代码中有一个对sd->input_pkt_queue的检查过程,而sd是每个cpu核心net rx soft irq用于维护待处理报文的队列,netdev_max_backlog则是内核所提供的一个配置选项,默认为1000。 ...

April 23, 2016 · 1 min · fortitude.zhang

emacs与konsole不对付的那点事

作为Linux重度用户,折腾过各式各样的WM/DE,最终还是觉得KDE PLASMA的易用性和美观程度是最佳的,考虑到不折腾的缘故,一直使用KDE至今,虽则时有crash,但总体上不优点大于缺点,瑕不掩瑜。 在linux上,存在较多的终端仿真器,即terminal emulator,所谓的仿真器就是把图形系统即X窗口上的键盘输入,转化成virtual terminal的输入,传送给那些将标准输入关联在/dev/pts这些伪终端设备上的控制台程序,使得这些控制台程序能够像在文本模式下那样工作。 这些仿真器中就包括KDE自带的konsole。 但konsole有一个问题就是,如果我们在终端仿真器里进行emacs -nw即文本模式的emacs(图形模式下不存在下面所述问题),则ctrl+2/ctrl+2这个emacs默认用来激活mark的命令大概率不能用,当然ctrl+space也可以用,但对于中文用户而言,ctrl+space大多绑定到输入法中英文切换,相应的键不能用,还是带来了诸多不便。 由于konsole可以通过手工设置key binding,因此可以手工将ctrl+2这个键绑定到发送\0这个控制序列给终端程序,来解决此问题,但对于适应了ctrl+@这个相似功能的键位的用户来说,目前key binding无法设置,仍是非常不便。 https://bugs.kde.org/show_bug.cgi?id=341157 KDE社区有人提了类似的bug,且已经解决,但似乎只解决了西语用户所关注的ctrl+space问题,但ctrl+@这个仍未解决,我已经提供了相关的反馈,期待进一步的进展。 :::console fortitude@fortitude:~$ konsole -version QCoreApplication::arguments: Please instantiate the QApplication object first Qt: 5.5.1 KDE Frameworks: 5.19.0ppp Konsole: 15.12.2 由于C++代码不够熟悉,自己也没有过我的精力去钻研这里,就先写在这里,供遇到相似问题的朋友参考关注。

March 1, 2016 · 1 min · fortitude.zhang

OpenStack网络方案近期状态分析

目前OpenStack网络方案存在有较多的选择,liberty发布已经有大半多时间,mikata版本也快要发布,有必要比较一下近期各种openstack网络方案的状态。 主流方案介绍 当前,比较主流的方案有如下几种: ovs-agent ofagent ovn dragonflow midonet/opencontrail 除此之外,还有一些闭源的商业方案,如nsx、plumgrid,以及一些试验性的开源方案如networking-odl等,本次分析暂不考虑。 现有网络方案特性集及特点 ovs-agent 功能点支持情况备注L2 isolation using overlayY支持vlan/vxlan/grearp responderY要求ovs 2.1 + l2pop使能L3 DVRY要求每台计算节点都需要安装l3-agent,通过netns及linux协议栈实现ovsdb nativeY独立功能,与openflow native无关,可单独开启,有一定的性能提升openflow nativeYovs-agent将内置ryu,运行一个of app用来处理与本地ovs的交互,基本逻辑与原ovs-agent相似,通过driver机制实现不同方式的bridge操作,目前仍处于实验阶段security groupY目前仍基于iptables,随着ovs conntrack功能的完善,可以演进至基于ovs conntrack实现dhcpY仍采用dhcp agentnorth-sourth流量Y仍采用l3agent进行集中式转发,即便在dvr模式下 ofagent 如同ofagent在openstack上的wiki所述: OFAgent is a neutron core-plugin, implemented as ML2 mechanism driver. It aims to support pure OpenFlow1.3 switches. ofagent关注的更多是指向向设备(如vswitch/pswitch)可移植性,因此基于纯openflow协议进行实现,而纵观业界,基本上没有基于纯openflow的openstack网络方案(目前仍成功商用的则是bigswitch的big fabric,但其采用了深度修改的openflow),因此这种方式可能是过于理想化的方案,可能并不一定能够容易落地。 从ovs的状态来看,ovs 2.5中为支持conntrack,引入了ct_state/ct这样的match/atction,这些nicira扩展落入openflow spec的时间点可能还比较长,而为了实现dv足够多的功能,ovs将会继续引入扩展,如果基于纯openflow实现的话,这些功能都将难以利用。 从wiki上的比较来看,ofagent也不支持dvr,实现上同样基于ryu,而部署模式上与ovs-agent相似,在compute/network节点都需部署。 ovn ovn是由vmware所主导的新项目,其介绍如下所示: OVN, the Open Virtual Network, is a system to support virtual network abstraction. OVN complements the existing capabilities of OVS to add native support for virtual network abstractions, such as virtual L2 and L3 overlays and security groups. Services such as DHCP are also desirable features. Just like OVS, OVN’s design goal is to have a production-quality implementation that can operate at significant scale. ...

January 31, 2016 · 2 min · fortitude.zhang

关于网卡VXLAN offload能力的误解

网络技术这个行业里,厂商通常会对一些技术做出高大上的包装,像网卡VXLAN offload这种技术,就是一个典型的例子,然而其底层技术,也许并不像想象中的那么神秘。 做为网络交换机的开发人员,近年来一直从事数据中心网络虚拟化的相关工作,最近读取一篇关于青云SDN 2.0的文章,里面提到了采用网卡VXLAN offload这种技术后overlay情形下的虚拟网络性能得到了较大规模的提升,考虑到后续我亦将加入云计算公司进行网络虚拟化相关的研发,自然也十分想了解这种技术的底层实现如何,以便从理论上分析其性能是否具有较大的价值。 分析之前,稍微VXLAN的封装格式进行简单的说明,本质上VXLAN类似于一种l2vpn技术,即将二层以太报文封装在udp报文里,从而跨越underlay L3网络,实现不同服务器或不同数据中心间的互联,其格式总结如下: |ETH_HEADER|IP_HEADER|UDP_HEADER|VXLAN_HEADER|INNER_ETH_HEADER|INNER_IP_HEADER|…| 在采用VXLAN技术后,由于vm/docker的报文被封装于外层的UDP报文中予以传输,使得以往的TCP SEGMENT OPTIMIZATION、TCP CHECKSUM OFFLOAD等功能对于内层VM的TCP数据收发失效,较大地影响了VM间通信的性能,给最终用户带来了很差的用户体验。 下面我们来看看不同的方案都是如何实现的: 支持Overlay的交换机 目前较新的万兆交换机大多支持Overlay功能,即支持将以太报文封装在VXLAN报文中予以转发,也即将VXLAN封装功能放在专用的网络设备上,来提供确定性的线速转发性能,但这很明显增加了交换机与服务器之间的耦合,给管理维护带来了一定的复杂度。 NIC VXLAN OFFLOAD 而网卡VXLAN offload则不像Overlay交换机那样要求对组网方案进行较大的变更,而是对于网卡的能力进行了增加,与网卡驱动配合,使得网卡能够知晓VXLAN内部以太报文的位置,从而使得TSO、TCP CHECKSUM OFFLOAD这些技术能够对内部以太报文生效,从而提升TCP性能。 以TSO为例,内核封装出的TCP报文如下: |ETH_HEADER|IP_HEADER|UDP_HEADER|VXLAN_HEADER|INNER_ETH_HEADER|INNER_IP_HEADER|INNER_TCP_HEADER|INNER_TCP_DATA_NEED_TSO| 该报文的内部TCP数据需要进行TSO处理,即切成满足网卡MTU的报文,内核驱动将报文通过DMA送给网卡后(同时要提供一些元信息,如VXLAN头位置等),网卡负责将内部的TCP报文转换为多个内部TCP报文,封装相同的外层VXLAN头后进行转发,从而减少CPU的干预,大规模提升性能。 小结 个人认为VXLAN OFFLOAD这个名字不如用VXLAN-AWARE OPTIMIZATION比较直观,但厂商为了吸引眼球,自然是需要适当的包装,可以理解。 从实现的角度来说,万兆甚至更高性能的网卡本来就比较贵,这样的优化可能并不一定导致过高的复杂度,但在实现上却能够不依赖于OVERLAY交换机更实现性能的提升,应该是做为优化OVERLAY性能的首选方案。

January 9, 2016 · 1 min · fortitude.zhang

linux下的session leader及controlling terminal

最近在调试嵌入式下设备启动基于busybox的init程序所执行的rc.sysinit脚本时,发现该脚本执行的后台脚本,会因为没有关联到串口设备(通过ps的TTY一列可以确认) ,导致我们做了tty设备检测的程序无法顺利执行,借此机会,查阅了busybox的init代码,并对linux的session group和controlling terminal有了一定的理解。 在libc的info文档中Job Control一节,有如下几个重要的定义: process group,即进程组,用管道符创建的一堆命令就行成了一个进程组。 session,即会话,通常从login shell登陆后的所有进程都属于一个session,即session由login shell创建,注意libc的文档还是从传统的语义来讲的,对于现代的大多数桌面环境而言(DE,如KDE/GNOME等),一个图形会话,往往会通过setsid函数起大量的session),一个session下可以管理多个process group,一个进程可以在同一个session下的不同process group中迁移,但将一个进程移到另外的会话中则只能通过setsid这个函数来创建新的会话实现。 session leader,即会话leader,创建会话的进程称为会话leader,从上面的描述可知,login shell一般为会话leader,首次调用setsid的进程也将成为session leader。 controlling terminal,即控制终端,进程的重要属性之一,由fork创建的子进程继续父进程的controlling terminal,而从setsid的libc文档中可以看出(或者用man 3 setsid查看),setsid调用后,进程将做为新的会话的会话leader,并且丢失controlling terminal属性,而其后该会话leader打开的首个tty设备,将成为该会话的controlling terminal(见附2说明);shell通常只会将controlling terminal这个属性给予一个进程组,以便由这个进程组通过终端设备获取输入或者输出信息,这组进程将称为前台任务,而未获得输入或输出等权限的进程组,在尝试向controlling terminal读取或写入数据时,将收到SIGTTIN或SIGTTOU信号,默认情况下这个信号将中止相关程序的执行。这点是合理的,因为如果不做此限制,后台程序很可能扰乱终端输出或者输入的处理。 经过上述讲述,以代码形式给出上述概念的演示。 #include "stdlib.h" #include "errno.h" #include "unistd.h" #include "fcntl.h" int main() { int err, fd; pid_t pid; char *pts_name; pid = fork(); if (pid != 0) { exit(0); // 主进程退出。 } pid = setsid(); // 在子进程中创建新的会话 // 打印从父进程继续而来的tty,注意,该tty已经不是我们的controlling terminal printf("before we prepare the new stdio, the child inherit its parent's stdio %s\n", ttyname(0)); // 如下一段代码用于创建一个pseudo-terminal,将作为tty设备被新的会话leader打开,成为该会话的controlling terminal fd = getpt(); pts_name = ptsname(fd); printf("allocated a new pts is %s\n", pts_name); grantpt(fd); unlockpt(fd); // 首次open的tty设备将成为controlling terminal fd = open(ptsname(fd), O_RDWR); // 将该fd做为标准输入,在本例中意义不大 close(STDIN_FILENO); dup2(fd, STDIN_FILENO); // 停留2分,以便我们可以通过ps检验是否获得了新的session及controlling terminal sleep(120); } 编译并执行上述程序后,执行后,立即用如下命令,可以查看到新进程确实拥有了新的controlling terminal了。 ...

September 25, 2015 · 1 min · fortitude.zhang

shell中通过set builtin操作positional parameter

最近在调试ubuntu 14.04上的openvswitch服务时,发现openvswitch在upstart里的脚本实现频繁用到了set这样一个shell builtin,代码写的不是很好理解,后来经过查询shell相关文档,终于理解了通过set这个builtin来操作位置变量确实是一个很不错的办法。 bash的info文档中专门有一章节来介绍The Set Builtin,并且由于builtin is so complicated that it deserves its own section,但从本文的角度来看,我们认为set就是用来操作位置变量的,以下用一个简单的示例,就可以很容易的给出set的妙用了。 set -- # --这个特殊的用法来清空位置变量,即用$@来访问位置变量将得到空,除了--,set还有其他很多用法,具体可以查看shell的info文档 set "$@" ls # 用ls来初始化位置变量,ls将被赋值给$1 set "$@" -l # 将$@展开后,与-l一起重新赋值给位置变量,使得ls被赋值给$1,-l给赋值给$@ $@ # 展开位置变量,并执行,等同于执行ls -l命令 从上面的代码示例可以看出,set可以将其后的一串参数赋值给位置变量,从而方便对命令行扩充,支持更好的扩展性,并且由于@这个位置变量全集的通用性,使得在脚本嵌套时,可以方便在被执行的脚本中,对原调用脚本传过来的参数进行进一步的编辑,从而实现更为复杂的应用,比如上述openvswitch的upstart脚本,就在这点上大做了文章。 从这点感觉,bash设计的还是很精妙的,工作中大多数采用C编程,对于shell脚本的编程实践较少,后续需要继续掌握这些细节和强大之处:)

August 27, 2015 · 1 min · fortitude.zhang