oracle之前提供了免费的云服务器,据说是终身免费的。不过之前申请后用了一段时间居然被删除了!当时也没想再用,因为速度也不快,不过现在有需要把它重新用起来,于是就重新申请了一个来搭建fq的服务。
oracle的云服务器创建还是比较简单,不过有个坑的地方是有两个地方的防火墙设置,设置ok后才能使用。
第一个地方:云服务器的子网里面有设置防火墙,需要在入站规则里面把服务的端口打开。
第二个地方:在云主机上面,需要把iptables对应的端口打开。
root@instance-xxx:/home/ubuntu# iptables -L
Chain INPUT (policy ACCEPT)
target prot opt source destination
ACCEPT all -- anywhere anywhere state RELATED,ESTABLISHED
ACCEPT icmp -- anywhere anywhere
ACCEPT all -- anywhere anywhere
ACCEPT udp -- anywhere anywhere udp spt:ntp
ACCEPT tcp -- anywhere anywhere state NEW tcp dpt:ssh
REJECT all -- anywhere anywhere reject-with icmp-host-prohibited
可以看到没有打开我们的服务端口,需要增加一条INPUT规则:
iptables -i INPUT 2 -m tcp --dport 10086 -j ACCEPT
注意不要添加到最后面,-i INPUT 2 即添加到第二条规则。然后就可以访问我们的服务了。
2020年5月9日星期六
2017年11月8日星期三
分布式锁的使用
最近项目碰到一个问题,涉及到分布式锁的使用,场景:多个客户端同时上传文件,server可能同时写文件,由于腾讯云的文件系统(COS:对象存储)对同时多个相同文件上传时,会移动其中一个文件,导致会有一个写失败,因此需要server在写的时候用锁来避免竞争。
同事一开始想的方案是直接用个文件来做锁:任何一个server收到上传请求的时候,会创建一个文件(名字用上传文件名来命名),多个server同时创建文件时,只会有一个创建成功,这样就可以锁住了。然后锁失败的server就反复读这个文件,直到获得锁的一端处理完数据,删除这个文件为止。
这个方案还是有一些问题的:比如如果获取锁的一方挂了,就会导致锁一直不会被销毁,这样其他server就会上传不了文件了。
对于分布式锁,之前用得最多的就是zk了,这次又重新复习了一下分布式锁的内容,网上有几篇文章说的比较好的:
分布式锁的几种实现方式:http://www.hollischuang.com/archives/1716,这篇文章谈到了分布式锁的几个要求:
同事一开始想的方案是直接用个文件来做锁:任何一个server收到上传请求的时候,会创建一个文件(名字用上传文件名来命名),多个server同时创建文件时,只会有一个创建成功,这样就可以锁住了。然后锁失败的server就反复读这个文件,直到获得锁的一端处理完数据,删除这个文件为止。
这个方案还是有一些问题的:比如如果获取锁的一方挂了,就会导致锁一直不会被销毁,这样其他server就会上传不了文件了。
对于分布式锁,之前用得最多的就是zk了,这次又重新复习了一下分布式锁的内容,网上有几篇文章说的比较好的:
分布式锁的几种实现方式:http://www.hollischuang.com/archives/1716,这篇文章谈到了分布式锁的几个要求:
这把锁要是一把可重入锁(避免死锁)
这把锁最好是一把阻塞锁(根据业务需求考虑要不要这条)
有高可用的获取锁和释放锁功能
获取锁和释放锁的性能要好
其实主要是避免死锁,和阻塞,失效时间,性能问题还在其次。文章还对几种锁:数据库锁,缓存锁,zk做了分析。从结论和我们实际应用来看,还是zk比较靠谱一点。
还有一篇文章谈到了redis做分布式锁的问题,里面对锁的机制和问题也做了很深入的分析。
http://zhangtielei.com/posts/blog-redlock-reasoning.html 。最开始我们也考虑过用redis做分布式锁,但是过期时间很不好设置。最后也放弃了redis作为分布式锁的方案。
我们最后用了什么方案呢?没用redis,也没用zk,只是做了简单的重试,这样就基本解决了问题。之所以用这个方案,一个时间成本考虑,一个是从现状分析:并发的情况不多,出现冲突的时候可以重试一下。所以说在分布式领域,有时候很难有完美的技术解决方案,但是根据实际情况可以采用更灵活的方案来解决问题。
2016年7月20日星期三
Redis 内存容量优化
在上一篇blog中(http://wadeling.blogspot.com/2016/07/redis.html),提到接手的一个redis服务,这个redis服务一开始是完全用string类型来存储key值,导致需要的内存容量非常的大。而且咨询使用方,有可能业务量还要几倍的上涨,而我们现在的机器只有16G内存,就算加内存,内存压力也太大了。于是有了内存优化的想法。
查询了网络上的一些优化文章,通过实验,发现还是有不少优化效果。具体思路就是把string类型的数据转成hash来存,同时保证每个hashkey里面的filed个数在一定范围内,这样redis会把hash的内存做优化处理,减小了内存的使用量。经过实验,100万个key,采用string类型存储,需要88M的内存,如果用hash来存,只需要45M内存。
优化方案如下:
1.查看redis的hash设置
通过cli方式查看:config get hash-max-ziplist-entries,值是512.
config get hash-max-ziplist-value,值是64.
即每个hashkey里面的field数目为512一下,value长度是64字节以内时,redis会对内存做优化。
2.对10万个key做分hash存储
//把md5字符串转成整数,具体方法是取前面8个字符,用base_convert转成10进制
$md5int = $this->md5str_to_int($md5str);
//3000为估算,即最后会有3000个hashkey,每个hashkey里面大概有300多个field,满足要求
$key = $md5int % 3000;
//hset来存入
$ret = $this->redis->hSet($key,$md5str,$value);
参考:
redis内存容量优化
Redis内存优化案例
instagram使用redis内存优化
redis官方内存优化方案
查询了网络上的一些优化文章,通过实验,发现还是有不少优化效果。具体思路就是把string类型的数据转成hash来存,同时保证每个hashkey里面的filed个数在一定范围内,这样redis会把hash的内存做优化处理,减小了内存的使用量。经过实验,100万个key,采用string类型存储,需要88M的内存,如果用hash来存,只需要45M内存。
优化方案如下:
1.查看redis的hash设置
通过cli方式查看:config get hash-max-ziplist-entries,值是512.
config get hash-max-ziplist-value,值是64.
即每个hashkey里面的field数目为512一下,value长度是64字节以内时,redis会对内存做优化。
2.对10万个key做分hash存储
//把md5字符串转成整数,具体方法是取前面8个字符,用base_convert转成10进制
$md5int = $this->md5str_to_int($md5str);
//3000为估算,即最后会有3000个hashkey,每个hashkey里面大概有300多个field,满足要求
$key = $md5int % 3000;
//hset来存入
$ret = $this->redis->hSet($key,$md5str,$value);
参考:
redis内存容量优化
Redis内存优化案例
instagram使用redis内存优化
redis官方内存优化方案
2016年7月12日星期二
redis内存容量估算
最近刚接手一个redis服务,由于是内部使用,所以在redis前端封装了一层代理,用于内部协议和redis协议的转化,并不复杂。但是使用方刚使用两天,就发现内存爆量了。具体现象是redis返回的错误:OOM command not allowed when used memory > 'maxmemory',使用方给出的请求量是:5000万key,每个key40字节(string类型),每个value8字节,这样算下来只要2.4G内存啊,怎么一下内存就爆了。
我也是刚接触redis,只好研究了一番,还好网上有一篇文章,详细说明了redis的容量估算问题,简单的说就是redis占用的内存除了业务数据之外,还有redis自己的数据结构也会占用一部分内存,所以实际占用内存会大于业务数据的。链接在此:http://www.searchdatabase.com.cn/showcontent_55189.htm
根据文章提供的公式,结合使用方提供的数据,估算的容量如下:
1.首先是计算公式:
string类型的内存大小 = 键值个数 * (dictEntry大小 + redisObject大小 + 包含key的sds大小 + 包含value的sds大小) + bucket个数 * 4
2.根据使用方提供的数据,占用的redis内存容量如下:
我也是刚接触redis,只好研究了一番,还好网上有一篇文章,详细说明了redis的容量估算问题,简单的说就是redis占用的内存除了业务数据之外,还有redis自己的数据结构也会占用一部分内存,所以实际占用内存会大于业务数据的。链接在此:http://www.searchdatabase.com.cn/showcontent_55189.htm
根据文章提供的公式,结合使用方提供的数据,估算的容量如下:
1.首先是计算公式:
string类型的内存大小 = 键值个数 * (dictEntry大小 + redisObject大小 + 包含key的sds大小 + 包含value的sds大小) + bucket个数 * 4
2.根据使用方提供的数据,占用的redis内存容量如下:
dictEntry大小: 16 字节
redisObject大小:16字节
key:40字节, sds对象:9 字节。总共是49字节,按jemalloc分配机制会占用到64字节。
value:8字节,sds对象:9字节,总共是17字节,按jemalloc分配机制会占用到32字节。
以上的内存占用量为:
(16 + 16 + 64 + 32)* 5000*1000*10 = 6.4G
bucket个数大概为6000万(5000万向上取整到2的n次方,即2的26次方,6000多万)
bucket大小:6000万*4 = 240M左右。
所以总容量为6.4G左右,而redis的配置里面maxmemeroy只配置了4G,难怪要报错了。修改配置后,运行正常了。
从redis里面也可以看到峰值使用内存为6.44G, 和计算出来的容量是吻合的.
2016年6月29日星期三
PHP SSH2 的各种问题
最近两天,不断碰到php ssh2的问题,总结下来有几个:
1.PHP Warning: ssh2_scp_send(): Failed copying file
1.PHP Warning: ssh2_scp_send(): Failed copying file
此问题出现在传大文件的时候
2.PHP Warning: ssh2_exec(): Unable to request a channel from remote host
连接某台机器的时候,执行shell命令的时候。其他机器没问题。
对于第一个问题,测试机上没问题(php ssh2 扩展版本为0.11.3),生产环境有问题(php ssh2 版本为 0.11.0),所以更新生产环境为0.11.3。但是测试还是有问题,百思不得其解。
后面再更新为0.13 stabel版本,ssh2_scp_send 不再报错了,但是传文件后发现文件md5结果不一样!又再google一番,在ssh2_scp_send之后增加一句:ssh2_exec($con, 'exit');这样才ok了。
但是在测试的时候,发现另外一个新的问题:
php: symbol lookup error: /usr/local/php/lib/php/extensions/no-debug-zts-20100525/ssh2.so: undefined symbol: libssh2_session_set_timeout
然后 nm ssh2.so |grep libssh2_session_set_timeout,发现这个函数是引用的外部实现。那这个函数在哪个so里面呢?找了一下,在libssh2.so.1里面,但是为啥没找到呢。
和运维同学想了半天,最后发现/etc/ld.so.conf.d/libssh2.conf 这个文件里面没有把 libssh2.so.1的路径包含进去,最后修改libssh2.conf文件,增加 /usr/local/libssh2/lib,这下ok了。
总结:碰到过两次php扩展版本的问题,之前碰到过memcache的问题,也是用的beta版本导致很多问题。这次也是一样。所以以后任何的扩展,都要仔细检查是否是stable版本的。
2016年3月31日星期四
php foreach reference 导致的问题
今天碰到一个奇怪的问题,在一个php脚本中写了一个foreache循环,遍历一个数组,同时修改数组里面的元素。大致就是这样:
foreach ($a as $key => &$value) {
$value['ttt'] = '123';
}
forearch ($a as $key => $value) {
}
结果第二次循环的时候,数组最后一行数据居然没有了。
第一次循环完dump出来的数组又是对的,但是其中一行有点奇怪,大概是这样:
&array(1) {
[0]=>
&array(20) {
["sampleID"]=>'aaa'
}
}
和这个有没有关系呢?搜了一圈,找到原因了。
这篇解释得很清楚:
php foreach reference
解决方法就是在使用引用完后要unset一下。php manual也有说明
php:foreach
foreach ($a as $key => &$value) {
$value['ttt'] = '123';
}
forearch ($a as $key => $value) {
}
结果第二次循环的时候,数组最后一行数据居然没有了。
第一次循环完dump出来的数组又是对的,但是其中一行有点奇怪,大概是这样:
&array(1) {
[0]=>
&array(20) {
["sampleID"]=>'aaa'
}
}
和这个有没有关系呢?搜了一圈,找到原因了。
这篇解释得很清楚:
php foreach reference
解决方法就是在使用引用完后要unset一下。php manual也有说明
php:foreach
2016年2月23日星期二
“Illegal double … value found during parsing” 错误
刚刚碰到的一个问题,数据提交时报"Illegal double '20160221E0259700' value found during parsing"错误。但是程序运行了一段时间,怎么会报错呢?查了一下,带"E"的串会被PHP认为是double变量,而要更新的这个字段是一个varchar类型,所以导致错误。
之前为啥没报错呢,因为代码太搓:update strid=2016123456,这样就没报错,实际上应该是update strrid='201612345'。坑啊!
之前为啥没报错呢,因为代码太搓:update strid=2016123456,这样就没报错,实际上应该是update strrid='201612345'。坑啊!
2016年1月15日星期五
GCC 4.4.6 const char* to char* compiler error
今天在编译一个同事的代码发现一个小问题,就是报编译错误: error: invalid conversion from ‘const char*’ to ‘char*’,报错出在strstr这个函数,但是他原来编译又是ok的。猜测是GCC版本不一致导致的问题,一看果然是,他用的gcc 4.1.2,我用的gcc 4.4.6。但是为啥gcc 4.4.6编译会报错呢,查了一下gcc 手册:
The table below shows the functions and files that have been changed.
An example.
链接:https://gcc.gnu.org/gcc-4.4/porting_to.html
=====
原来是C++的标准库头文件有变化,目标是“improving const-correctness”,其中就有strstr这个函数。好吧,在原来的代码修改了加了强制转化就ok了。
Strict null-terminated sequence utilities
Some of the standard C++ library include files have been edited to use replacement overloads for some common C library functions (if available), with the goal of improving const-correctness: functions passed aconst char* return const char*.The table below shows the functions and files that have been changed.
| Header | Functions |
|---|---|
| <cstring> | strchr, strpbrk, strrchr, strstr, memchr |
| <cwchar> | wcschr wcspbrk, wcsrchr, wcsstr, wmemchr |
#include <cstring> const char* str1; char* str2 = strchr(str1, 'a');Gives the following compiler error:
error: invalid conversion from ‘const char*’ to ‘char*’Fixing this is easy, as demonstrated below.
#include <cstring> const char* str1; const char* str2 = strchr(str1, 'a');More information about the C++ standard requirements can be found in chapter 21, section "Null-terminated sequence utilities."
链接:https://gcc.gnu.org/gcc-4.4/porting_to.html
=====
原来是C++的标准库头文件有变化,目标是“improving const-correctness”,其中就有strstr这个函数。好吧,在原来的代码修改了加了强制转化就ok了。
2015年6月9日星期二
linux OOM
最近碰到的一个问题:进程A先启动,运行正常,然后进程B启动,系统一下卡住了,过了一会,进程B启动完成,进行A却退出了。对这个现象百思不得其解啊。gdb调了一会,发现进程B在分配内存的时候(要分配的内存很大),会导致系统卡住,然后进程A退出。网上搜了一下,发现是linux OOM机制导致的这种问题。
简而言之:
1.当一个进程向linux请求内存分配时(不论是new或者malloc),linux通常会直接返回成功,实际上并未分配内存。
2.实际分配内存的时候,linux若发现系统内存不够,则可能执行OOM(根据系统配置决定)把其他进程kill掉(发送SIGTERM信号)
3.程序中对new或者malloc的分配是否成功的判断有时候是没用的。只有通过监控系统内存来判断程序的内存的使用是否有问题。
内核关于OOM的文章:
https://www.kernel.org/doc/gorman/html/understand/understand016.html
另外以下这篇文章对OOM分析比较到位:
http://blog.csdn.net/guomsh/article/details/6536915
简而言之:
1.当一个进程向linux请求内存分配时(不论是new或者malloc),linux通常会直接返回成功,实际上并未分配内存。
2.实际分配内存的时候,linux若发现系统内存不够,则可能执行OOM(根据系统配置决定)把其他进程kill掉(发送SIGTERM信号)
3.程序中对new或者malloc的分配是否成功的判断有时候是没用的。只有通过监控系统内存来判断程序的内存的使用是否有问题。
内核关于OOM的文章:
https://www.kernel.org/doc/gorman/html/understand/understand016.html
另外以下这篇文章对OOM分析比较到位:
http://blog.csdn.net/guomsh/article/details/6536915
Linux Out-of-Memory(OOM) Killer
Linux有一个特性:OOM Killer,一个保护机制,用于避免在内存不足的时候不至于出现严重问题,把一些无关的进程优先杀掉,即在内存严重不足时,系统为了继续运转,内核会挑选一个进程,将其杀掉,以释放内存,缓解内存不足情况,不过这种保护是有限的,不能完全的保护进程的运行。
在很多情况下,经常会看到还有剩余内存时,oom-killer依旧把进程杀死了,现象是在/var/log/messages日志文件中有如下信息:
Out of Memory: Killed process [PID] [process name].
该问题是low memory耗尽,因为内核使用low memory来跟踪所有的内存分配。
当low memory耗尽,不管high memory剩多少,oom-killer都会杀死进程,以保持系统的正常运行。
在32位CPU下寻址范围是有限的,Linux内核定义了下面三个区域:
# DMA: 0x00000000 - 0x00999999 (0 - 16 MB)
# LowMem: 0x01000000 - 0x037999999 (16 - 896 MB) - size: 880MB
# HighMem: 0x038000000 - <硬件特定>
LowMem区(也叫NORMAL ZONE)共880MB,并且是固定不能变的(除非使用hugemem内核),对于高负荷的系统,可能因为LowMem使用不好而触发了OOM Killer机制。因为内存分配是一个连续的区域,在此时,如果LowMem里存在很多碎片或者LowFree太少,此时无法分配到一块连续的内存区域,就触发了OOM Killer。
查看当前LowFree值:
cat /proc/meminfo | grep LowFree
查看LowMem内存碎片:
cat /proc/buddyinfo
上面这命令需要在2.6内核才有效。
有如下方法可以解决该问题:
1、升级到64位系统,这是最好的方法,因为此时所有的内存都属low memory,如此时提示out of memory,则真的是low memory耗尽,真的OOM了。
2、如必须使用32位系统,那么可以使用hugemem内核,此时内核会以不同的方式分割low/high memory,而大多数情况下会提供足够多的low memory至high memory的映射,此时很简单的一个修复方法是可以安装hugemem内核包,然后重启。
3、如果hugemem内核也用不了,那么我们可以尝试将/proc/sys/vm/lower_zone_protection的值设为250或更大,可使用如下命令查看和设置该值:
cat /proc/sys/vm/lower_zone_protection
echo 250 > /proc/sys/vm/lower_zone_protection
或者可以修改/etc/sysctl.conf文件,以便重启后生效,添加的内容如下:
vm.lower_zone_protection = 250
4、实在没办法,那么我们把oom-killer关掉,不过该选项可能导致系统挂起,故要看实际情况使用。
查看当前的oom-killer的状态:cat /proc/sys/vm/oom-kill
关闭/打开oom-killer:
echo "0" > /proc/sys/vm/oom-kill
echo "1" > /proc/sys/vm/oom-kill
或者直接加到/etc/sysctl.conf文件,内容如下:
vm.oom-kill = 0
此时,当进程被oom-killer尝试杀死而没有成功时,会把相关信息记录到/var/log/messages文件中,信息如下:
"Would have oom-killed but /proc/sys/vm/oom-kill is disabled"
5、或者不把oom-killer关掉,只针对单一进程处理,把某个进程保护起来,此时,我们可以使用如下命令:
echo -17 > /proc/[pid]/oom_adj
/proc/[pid]/oom_adj中oom_adj的取值范围是-17~15,当该值为-17时,系统将不会杀死指定pid的进程,而-16~15则会使得进程的/proc/[pid]/oom_adj值呈指数(K*2^n)形式递增,即它们被杀掉的可能性呈指数递增。针对init(进程号为1)这个进程,无论该值设为多少都不会被杀。
6、参考资料
以上是从网络上查到,结合自己的问题进行下补充:
一开始由于系统配置是2G,而且没有交换分区,所以每天导致out of memory,后来增加了物理内存,并做了交换分区,情况有所改善,但是运行2-3天后还是会出现out of memory的情况,后来分析日志文件messages发现粗体部分,分析是low memory不足导致,后来自己试验用sync ;echo 3 >> /proc/sys/vm/drop_caches可以将内存释放出来,查看/proc/buddyinfo内存释放出来。
Jun 10 13:33:11 xx user.warn kernel: DMA free:3548kB min:68kB low:84kB high:100kB active:0kB inactive:0kB present:16384kB pages_scanned:0 all_unreclaimable? yes
Jun 10 13:33:11 xx user.warn kernel: lowmem_reserve[]: 0 0 880 4080
Jun 10 13:33:11 xx user.warn kernel: DMA32 free:0kB min:0kB low:0kB high:0kB active:0kB inactive:0kB present:0kB pages_scanned:0 all_unreclaimable? no
Jun 10 13:33:11 xx user.warn kernel: lowmem_reserve[]: 0 0 880 4080
Jun 10 13:33:11 xx user.warn kernel: Normal free:1340kB min:3756kB low:4692kB high:5632kB active:0kB inactive:28kB present:901120kB pages_scanned:2456536 all_unreclaimable? yes
Jun 10 13:33:11 xx user.warn kernel: lowmem_reserve[]: 0 0 0 25600
Jun 10 13:33:11 xx user.warn kernel: HighMem free:1615600kB min:512kB low:3928kB high:7344kB active:274112kB inactive:24928kB present:3276800kB pages_scanned:0 all_unreclaimable? no
Jun 10 13:33:11 xx user.warn kernel: lowmem_reserve[]: 0 0 0 0
Jun 10 13:33:11 xx user.warn kernel: DMA: 1*4kB 1*8kB 1*16kB 0*32kB 1*64kB 1*128kB 1*256kB 0*512kB 1*1024kB 1*2048kB 0*4096kB = 3548kB
Jun 10 13:33:11 xx user.warn kernel: DMA32: empty
Jun 10 13:33:11 xx user.warn kernel: Normal: 1*4kB 1*8kB 1*16kB 1*32kB 0*64kB 0*128kB 1*256kB 0*512kB 1*1024kB 0*2048kB 0*4096kB = 1340kB
Jun 10 13:33:11 CHINASOFT user.warn kernel: HighMem: 8192*4kB 7506*8kB 5166*16kB 2932*32kB 1528*64kB 688*128kB 253*256kB 86*512kB 67*1024kB 12*2048kB 234*4096kB = 1615600kB
以上是自己遇到的问题,处理肯定有不对的地方,如果您发现请指正!补充:内核是2.26.16
2014年12月24日星期三
解决Samba访问慢的问题
之前的一台开发机samba访问一直很正常,但是突然有天就变得很慢,现象就是保存一个文件时可能要得10多秒,这实在是受不了。折腾了两天,网上的方法都试了一下,完全没用。今天偶尔发现 /etc/resolv.conf 里面的一台nameserver有点慢,ping都要40ms,然后换了一台快的,ping基本上在1ms以内,然后samba的速度一下就快起来了,秒存文件,爽啊。
2014年9月13日星期六
linux:解决too many open files错误
一大早被叫起来解决故障,悲催啊。就是因为机器设置的max open files太小了,一会就不够了。
登上机器,一看:ulimit -n,结果只有1024. 好吧,临时解决方案:ulimit -n 409600,注意这个命令只对当前shell有效,所以修改后要在当前shell重启进程。
后来运维上线来搞了,方法记录一下:
登上机器,一看:ulimit -n,结果只有1024. 好吧,临时解决方案:ulimit -n 409600,注意这个命令只对当前shell有效,所以修改后要在当前shell重启进程。
后来运维上线来搞了,方法记录一下:
/etc/security/limits.conf
加上:
* soft nofile 65535
* hard nofile 65535
* hard nofile 65535
就ok了。
2014年7月18日星期五
多个进程打开同一个文件,各个进程得到的fd可能是一样的!
两个程序都用open打开同一个文件,然后把fd打印出来,一看,fd怎么是一样的?一下没想出来原因,还去stackoverflow上查了半天。下意识的以为fd是系统唯一的,一个进程占用了这个fd,另一个进程应该就不会有分配到这个fd了。而且看《Unix环境高级编程》里面给出的多进程打开一个文件的例子,fd是不一样的。当时就晕了。
后来在http://linux.die.net/man/3/open上仔细看了一下:
The open() function shall return a file descriptor for the named file that is the lowest file descriptor not currently open for that process
就是说open返回的fd是这个进程内未分配的最小文件描述符,好吧,这样就清楚了。
后来在http://linux.die.net/man/3/open上仔细看了一下:
The open() function shall return a file descriptor for the named file that is the lowest file descriptor not currently open for that process
就是说open返回的fd是这个进程内未分配的最小文件描述符,好吧,这样就清楚了。
想到的其他问题:
1.一个进程如果多次打开同一文件,会得到不同的fd。
2.一个进程内多个线程打开一个文件会得到不同的fd。
3.对于记录锁,一个进程可以提出多个锁请求,即新锁可以替换老锁。一个进程多次打开一个文件,得到多个fd,再对这些fd上锁,这是ok的,即不会出现等锁的情况。
4.继续,一个进程创建了多个线程,多个线程打开一个文件,得到不同fd,众线程对各种的fd设置锁,是不会出现一个线程等待另一个线程的情况。所以,记录锁不能用于线程。
2014年7月17日星期四
semop和ipcs
说起semop函数,其实man手册都说得比较清楚了,其作用就是对信号量进行PV操作。平时使用的时候参考手册就可以,但是如果结合ipcs命令的话就可以更直观的看到信号量的变化。
上代码:
上代码:
C++语言: 高亮代码由发芽网提供
001 C++语言: 高亮代码由发芽网提供
002 #include <stdlib.h>
003 #include <stdio.h>
004 #include <unistd.h>
005 #include <stdint.h>
006 #include <semaphore.h>
007 #include <sys/ipc.h>
008 #include <sys/shm.h>
009 #include <sys/types.h>
010 #include <sys/socket.h>
011 #include <sys/wait.h>
012 #include <sys/stat.h>
013 #include <sys/time.h>
014 #include <sys/sem.h>
015 #include <netinet/in.h>
016 #include <arpa/inet.h>
017 #include <signal.h>
018 #include <string.h>
019 #include <stdarg.h>
020 #include <netdb.h>
021 #include <iostream>
022 #include <sstream>
023 #include <map>
024 #include <cstdarg> //var_list
025
026 #include <fcntl.h>
027 #include <dlfcn.h>
028 #include <errno.h>
029
030 using namespace std;
031
032 void sem_P(int semid)
033 {
034 int ret = -1;
035 struct sembuf buf;
036 buf.sem_num = 0;
037 buf.sem_op = -1;
038 buf.sem_flg = SEM_UNDO;//退出时会撤销此次操作
039 // buf.sem_flg = 0;//如果是0则不会撤销
040
041 errno = 0;
042 while (((ret = semop(semid, &buf, 1)) < 0) && (errno == EINTR)) ;
043 }
044
045 void sem_P2(int semid)
046 {
047 struct sembuf sops[2];
048 int ret = -1;
049 sops[0].sem_num = 0;
050 sops[0].sem_op = 0;
051 sops[0].sem_flg = 0;
052
053 sops[1].sem_num = 0;
054 sops[1].sem_op = 1;
055 sops[1].sem_flg = 0;
056
057 errno = 0;
058 while (((ret = semop(semid, sops, 2)) < 0) && (errno == EINTR)) ;
059 }
060
061 void sem_V(int semid)
062 {
063 int ret = -1;
064 struct sembuf buf;
065 buf.sem_num = 0;
066 buf.sem_op = 1;
067 buf.sem_flg = SEM_UNDO;
068 // buf.sem_flg = 0;
069
070 errno = 0;
071 while (((ret = semop(semid, &buf, 1)) < 0) && (errno == EINTR)) ;
072 }
073
074 int main(int argc, char *argv[])
075 {
076 int m_semid = -1;
077
078 //生成key值
079 const char *path = "/tmp";
080 key_t key = ftok(path, 11);
081 if (key < 0) {
082 return 1;
083 }
084 printf("key %d\n", key);
085
086 //生成新的信号量
087 int new_sem = 1;
088 int semid = semget(key, 1, IPC_CREAT | IPC_EXCL | 0666);
089 if (semid < 0) {
090 printf("semget error %d\r\n", semid);
091 if (errno == EEXIST) {
092 new_sem = 0;
093 semid = semget(key, 1, 0666);
094 printf("semget exist %d\r\n", semid);
095 if (semid < 0) {
096 return 1;
097 }
098 } else {
099 return 1;
100 }
101 }
102
103 printf("semid %d \r\n", semid);
104
105 //设置新的信号量值为1
106 if (new_sem) {
107 if (semctl(semid, 0, SETVAL, 1) < 0) {
108 printf("semctl err.\r\n");
109 semctl(semid, 0, IPC_RMID);
110 return 1;
111 }
112 }
113
114 //这里用ipcs命令可以看到信号量值为1
115 printf("before sem_p \r\n");
116 sleep(10);
117
118 sem_P(semid);
119 printf("sem_P ok \r\n");
120
121
122 //这里用ipcs命令可以看到信号量值为0
123 sleep(15);
124 printf("end \r\n");
125
126 return 0;
127
128 }
002 #include <stdlib.h>
003 #include <stdio.h>
004 #include <unistd.h>
005 #include <stdint.h>
006 #include <semaphore.h>
007 #include <sys/ipc.h>
008 #include <sys/shm.h>
009 #include <sys/types.h>
010 #include <sys/socket.h>
011 #include <sys/wait.h>
012 #include <sys/stat.h>
013 #include <sys/time.h>
014 #include <sys/sem.h>
015 #include <netinet/in.h>
016 #include <arpa/inet.h>
017 #include <signal.h>
018 #include <string.h>
019 #include <stdarg.h>
020 #include <netdb.h>
021 #include <iostream>
022 #include <sstream>
023 #include <map>
024 #include <cstdarg> //var_list
025
026 #include <fcntl.h>
027 #include <dlfcn.h>
028 #include <errno.h>
029
030 using namespace std;
031
032 void sem_P(int semid)
033 {
034 int ret = -1;
035 struct sembuf buf;
036 buf.sem_num = 0;
037 buf.sem_op = -1;
038 buf.sem_flg = SEM_UNDO;//退出时会撤销此次操作
039 // buf.sem_flg = 0;//如果是0则不会撤销
040
041 errno = 0;
042 while (((ret = semop(semid, &buf, 1)) < 0) && (errno == EINTR)) ;
043 }
044
045 void sem_P2(int semid)
046 {
047 struct sembuf sops[2];
048 int ret = -1;
049 sops[0].sem_num = 0;
050 sops[0].sem_op = 0;
051 sops[0].sem_flg = 0;
052
053 sops[1].sem_num = 0;
054 sops[1].sem_op = 1;
055 sops[1].sem_flg = 0;
056
057 errno = 0;
058 while (((ret = semop(semid, sops, 2)) < 0) && (errno == EINTR)) ;
059 }
060
061 void sem_V(int semid)
062 {
063 int ret = -1;
064 struct sembuf buf;
065 buf.sem_num = 0;
066 buf.sem_op = 1;
067 buf.sem_flg = SEM_UNDO;
068 // buf.sem_flg = 0;
069
070 errno = 0;
071 while (((ret = semop(semid, &buf, 1)) < 0) && (errno == EINTR)) ;
072 }
073
074 int main(int argc, char *argv[])
075 {
076 int m_semid = -1;
077
078 //生成key值
079 const char *path = "/tmp";
080 key_t key = ftok(path, 11);
081 if (key < 0) {
082 return 1;
083 }
084 printf("key %d\n", key);
085
086 //生成新的信号量
087 int new_sem = 1;
088 int semid = semget(key, 1, IPC_CREAT | IPC_EXCL | 0666);
089 if (semid < 0) {
090 printf("semget error %d\r\n", semid);
091 if (errno == EEXIST) {
092 new_sem = 0;
093 semid = semget(key, 1, 0666);
094 printf("semget exist %d\r\n", semid);
095 if (semid < 0) {
096 return 1;
097 }
098 } else {
099 return 1;
100 }
101 }
102
103 printf("semid %d \r\n", semid);
104
105 //设置新的信号量值为1
106 if (new_sem) {
107 if (semctl(semid, 0, SETVAL, 1) < 0) {
108 printf("semctl err.\r\n");
109 semctl(semid, 0, IPC_RMID);
110 return 1;
111 }
112 }
113
114 //这里用ipcs命令可以看到信号量值为1
115 printf("before sem_p \r\n");
116 sleep(10);
117
118 sem_P(semid);
119 printf("sem_P ok \r\n");
120
121
122 //这里用ipcs命令可以看到信号量值为0
123 sleep(15);
124 printf("end \r\n");
125
126 return 0;
127
128 }
编译后运行程序,程序在115行会sleep一下,这时候用ipcs -s -i semid 命令就可以看到信号量的值是1的(semid程序运行时已经打出来了):
程序运行结果:
key 184644993
semget error -1
semget exist 5668871
semid 5668871
before sem_p
sem_P ok
end
1.可以看到semid是5668871,在打印出“before sem_p ”的时候用命令ipcs -s -i 5668871,结果:
--------------------------------------------------
Semaphore Array semid=5668871
uid=0 gid=0 cuid=0 cgid=0
mode=0666, access_perms=0666
nsems = 1
otime = Thu Jul 17 11:38:55 2014
ctime = Wed Jul 16 17:48:24 2014
semnum value ncount zcount pid
0 1 0 0 3009
--------------------------------------------------
最后一行value就是信号量的值(1)了
2.在打印出“sem_P ok ”的时候,再用ipcs -s -i 5668871 来看,结果:
--------------------------------------------------
Semaphore Array semid=5668871
uid=0 gid=0 cuid=0 cgid=0
mode=0666, access_perms=0666
nsems = 1
otime = Thu Jul 17 16:40:18 2014
ctime = Wed Jul 16 17:48:24 2014
semnum value ncount zcount pid
0 0 0 0 30155
-------------------------------------------------------
可以看到vlaue已经变成了0,同时pid也变成了我们程序的pid
3.程序运行结束后,我们再使用ipcs命令来看看:
---------------------------------------------------
Semaphore Array semid=5668871
uid=0 gid=0 cuid=0 cgid=0
mode=0666, access_perms=0666
nsems = 1
otime = Thu Jul 17 16:40:33 2014
ctime = Wed Jul 16 17:48:24 2014
semnum value ncount zcount pid
0 1 0 0 30155
-----------------------------------------------------------
信号量的value又变成了1,为啥呢?因为在sem_P函数里面我们指定了进程退出时对信号量的操作buf.sem_flg = SEM_UNDO,就是我们在程序里面对信号量减一了,退出后这个操作就会被撤销了。
哈哈,这样看起来是不是清楚多了。
另外,在sem_P2函数里面还有对一个信号量进行了两次操作,先是等待信号量变成0,然后再对信号量进行加1。这也是一个蛮有意思的操作。
2014年4月22日星期二
修改SecureCRT显示宽度
使用SecureCrt觉得最不爽的问题一个是颜色,一个是显示宽度。颜色的问题在这篇文章 linux开发环境配置 已经解决了,今天仔细研究一下,发现原来显示宽度也是可以修改的,如下图
在option->Global options里面,修改 Maximum columns的值就可以了。原来只有132,显示文本太长的话就会自动换行,现在改成200,基本上都可以显示完全了。
PS:修改完这两个选项后,SecureCRT和xshell一样好用了。
在option->Global options里面,修改 Maximum columns的值就可以了。原来只有132,显示文本太长的话就会自动换行,现在改成200,基本上都可以显示完全了。
PS:修改完这两个选项后,SecureCRT和xshell一样好用了。
2014年4月2日星期三
source insight 不能解析namespace关键字的问题
如果在项目里使用了namespace关键字,并且其他文件使用了namespace里面的函数,则在source insight里面看这些函数都是黑色的。解决办法:source insight 解析 namespace
具体设置如下:
具体设置如下:
勾选“Ignore namespace declaration”选项即可
2014年3月26日星期三
gcc (g++) 编译时只链接静态库
为啥编译时要只链接静态库呢?链接动态库时,编译后的程序需要目标机器安装对应的动态库。链接静态库就不存在这个问题了。而且有时候链接的动态库还依赖于其他动态库,只要有一个版本不对就抓虾了。
今天就碰到这样的一个问题:我们的程序使用了google protocol buffers库,在开发机器上(suse 10)安装了pb库,编译正常,程序运行也正常。但是在测试机器上就有问题了。首先在测试机器上安装了pb库,安装顺利,运行pb库的make check也显示所有测试通过。但是运行时提示: /usr/lib64/libstdc++.so.6: version `GLIBCXX_3.4.10' not found (required by /usr/lib64/libprotobuf.so.8),然后我把开发机器上的 libstdc++.so.6 拷贝过来,还是不行。这就抓瞎了(后来发现了错误,libstdc++.so.6只是一个软链接,链接到libstdc++.so.6.0.8这个文件的。所以我只是拷贝了一个软链接!)
当时时间比较急,所以就把程序重新用pb的静态库文件编译了。这里大概总结一下链接只链接静态库的方法。
1.gcc (g++) 编译选项里面 -l 选项是默认先链接动态库的,所以我一开始用 -lprotobuf 选项链接的都是 libprotobuf.so.8 这个库。
2.要只链接静态库 libprotobuf.a 这个文件的话,可以直接写死静态库路径(相当与把静态库当作.o文件来使用),比如:g++ /usr/local/lib/libprotobuf.a -o myserver,或者用 g++ -o myserver /usr/local/lib/libprotobuf.a 也可以。同时把原来的-lprotobuf选项去掉,这样就可以了。最后链接完成后,用ldd myserver就可以看到程序里面已经没有pb的动态库了。
3.可以把libprotobuf.a文件拷贝到makefile的搜索路径里面。比如我的搜索路径设置为当前的./lib目录,然后就把libprotobuf.a拷贝到这个目录里面。(这个方法其实还是有问题的,有时候链接的还是动态库)
4.最好的办法:使用-Wl,-Bstatic选项。在要链接的库文件前加入这个选项,可以强制链接静态库。比如我的makefile最后就写成这个样子:
-Wl,-Bstatic -lprotobuf -Wl,-Bdynamic -lgcc_s
-Wl,-Bstatic -lprotobuf 即强制链接libprotobuf.a
-Wl,-Bdynamic -lgcc_s 这个是因为需要gcc_s的动态库,所以在前面加了一个使用动态库的选项。
5.-static选项:这个选项是全局的,一旦用了就要求全部链接静态库。
今天就碰到这样的一个问题:我们的程序使用了google protocol buffers库,在开发机器上(suse 10)安装了pb库,编译正常,程序运行也正常。但是在测试机器上就有问题了。首先在测试机器上安装了pb库,安装顺利,运行pb库的make check也显示所有测试通过。但是运行时提示: /usr/lib64/libstdc++.so.6: version `GLIBCXX_3.4.10' not found (required by /usr/lib64/libprotobuf.so.8),然后我把开发机器上的 libstdc++.so.6 拷贝过来,还是不行。这就抓瞎了(后来发现了错误,libstdc++.so.6只是一个软链接,链接到libstdc++.so.6.0.8这个文件的。所以我只是拷贝了一个软链接!)
当时时间比较急,所以就把程序重新用pb的静态库文件编译了。这里大概总结一下链接只链接静态库的方法。
1.gcc (g++) 编译选项里面 -l 选项是默认先链接动态库的,所以我一开始用 -lprotobuf 选项链接的都是 libprotobuf.so.8 这个库。
2.要只链接静态库 libprotobuf.a 这个文件的话,可以直接写死静态库路径(相当与把静态库当作.o文件来使用),比如:g++ /usr/local/lib/libprotobuf.a -o myserver,或者用 g++ -o myserver /usr/local/lib/libprotobuf.a 也可以。同时把原来的-lprotobuf选项去掉,这样就可以了。最后链接完成后,用ldd myserver就可以看到程序里面已经没有pb的动态库了。
3.
4.最好的办法:使用-Wl,-Bstatic选项。在要链接的库文件前加入这个选项,可以强制链接静态库。比如我的makefile最后就写成这个样子:
-Wl,-Bstatic -lprotobuf -Wl,-Bdynamic -lgcc_s
-Wl,-Bstatic -lprotobuf 即强制链接libprotobuf.a
-Wl,-Bdynamic -lgcc_s 这个是因为需要gcc_s的动态库,所以在前面加了一个使用动态库的选项。
5.-static选项:这个选项是全局的,一旦用了就要求全部链接静态库。
2014年3月5日星期三
linux 开发环境配置
一.命令配置(alias)。
用l,“..” ,“...”命令习惯了,换到新机器的时候没这几个命令还不习惯,所以这样来设置:root用户,先执行cd,进入到用户目录,然后vi .bashrc,加入
alias l='ls -al'
alias ..='cd ..'
alias ...='cd ../..'
这三条,这样就ok了。
二.颜色设置
如果用Xshell的话,那就很方便了,颜色配置很好,基本不用改。
如果用securCrt的话,默认情况下用蓝色显示文件夹,基本上看不清楚,必须修改,方法如下:
1.设置终端类型为linux,勾选ansi color,不选 Use color scheme(如果选了,则在终端-外观-颜色 里面选中的方案就会生效,这里面的颜色方案个人不太喜欢).
2.在SecureCrt全局选项里面修改颜色,在这里才能把文件夹的蓝色修改掉,修改Bold colors里面的蓝色,Normal colors不变。如图:
这样修改后,SecurCrt的颜色就好看多了
用l,“..” ,“...”命令习惯了,换到新机器的时候没这几个命令还不习惯,所以这样来设置:root用户,先执行cd,进入到用户目录,然后vi .bashrc,加入
alias l='ls -al'
alias ..='cd ..'
alias ...='cd ../..'
这三条,这样就ok了。
二.颜色设置
如果用Xshell的话,那就很方便了,颜色配置很好,基本不用改。
如果用securCrt的话,默认情况下用蓝色显示文件夹,基本上看不清楚,必须修改,方法如下:
1.设置终端类型为linux,勾选ansi color,不选 Use color scheme(如果选了,则在终端-外观-颜色 里面选中的方案就会生效,这里面的颜色方案个人不太喜欢).
![]() |
| 终端颜色设置 |
这样修改后,SecurCrt的颜色就好看多了
2014年1月24日星期五
strstk_r 分隔符之坑
今天发现一个问题,用strstk_r来分割字符串,发现结果不是想象的那样。原因是分割符参数是多个字符,而strstk_r是把分割符看成一个集合,只要匹配集合里面的任何一个元素就会进行分割。用了才知道坑啊。
socket编程手记
1.同一socket连着两次connect会发生什么?
第一次connect成功后,第二次connect会提示 EISCONN,/* Transport endpoint is already connected */,即该socket已经连接上了。
2.客户端connect成功后退出,server端收到什么
客户端connect成功后退出,发送一个fin到server端,server端调用recv返回空数据
linux 手册里面的:If no messages are available to be received and the peer has performed an orderly shutdown,recv() shall return 0
客户端退出后,往server端发送了一个fin:
14:07:10.118038 IP client.49032 > server.30010: F 1:1(0) ack 1 win 64 <nop,nop,timestamp 1558633984 1558633984>
14:07:10.119100 IP server 30010 > client 49032: . ack 2 win 64 <nop,nop,timestamp 1558633985 1558633984>
这时tcp连接处于半关闭状态.协议栈还是等待server端发出关闭信息。
server端退出后(或者主动close),tcpdump可以看到server端的确认关闭的信息:
14:21:20.802625 IP server.30010 >client.49032: F 1:1(0) ack 2 win 64 <nop,nop,timestamp 1558846656 1558633984>
14:21:20.802628 IP client.49032 > server.30010: R 1502573033:1502573033(0) win 0
3.listen socket设置成为非阻塞,accept后的connected socket还是非阻塞吗?
第一次connect成功后,第二次connect会提示 EISCONN,/* Transport endpoint is already connected */,即该socket已经连接上了。
2.客户端connect成功后退出,server端收到什么
客户端connect成功后退出,发送一个fin到server端,server端调用recv返回空数据
linux 手册里面的:If no messages are available to be received and the peer has performed an orderly shutdown,recv() shall return 0
客户端退出后,往server端发送了一个fin:
14:07:10.118038 IP client.49032 > server.30010: F 1:1(0) ack 1 win 64 <nop,nop,timestamp 1558633984 1558633984>
14:07:10.119100 IP server 30010 > client 49032: . ack 2 win 64 <nop,nop,timestamp 1558633985 1558633984>
这时tcp连接处于半关闭状态.协议栈还是等待server端发出关闭信息。
server端退出后(或者主动close),tcpdump可以看到server端的确认关闭的信息:
14:21:20.802625 IP server.30010 >client.49032: F 1:1(0) ack 2 win 64 <nop,nop,timestamp 1558846656 1558633984>
14:21:20.802628 IP client.49032 > server.30010: R 1502573033:1502573033(0) win 0
3.listen socket设置成为非阻塞,accept后的connected socket还是非阻塞吗?
int s1 = socket();s1设置成为非阻塞,在s1上面listen,有连接建立后,accept后得到的是一个新的socket(s2),这个socket默认是阻塞的。所以accept后,在s2上面recv数据是阻塞的。
2014年1月21日星期二
开发日记
这篇主要记录工作中碰到的一些问题,特别是奇怪的问题。
1.‘ucontext_t’ was not declared in this scope
先说一下开发环境:通过samba使用远程机器进行开发,代码都放在远程目录下面,然后用sourceinsight写代码。
这个问题真是神奇,原来编译得好好的代码,突然就编译不过,然后提示如上。然后我加上 #include <sys/ucontext.h> ,不行,换成#include <ucontext.h> 还是不行。然后仔细的想今天做过什么改动,终于想起来了,因为在sourceinsight里面没有include 系统头文件,所以我就拷贝一份系统头文件到工作目录,结果makefile就包含了这个目录,导致编译出错。
解决办法很简单,就是把工作目录下的系统头文件删掉就可以了。
1.‘ucontext_t’ was not declared in this scope
先说一下开发环境:通过samba使用远程机器进行开发,代码都放在远程目录下面,然后用sourceinsight写代码。
这个问题真是神奇,原来编译得好好的代码,突然就编译不过,然后提示如上。然后我加上 #include <sys/ucontext.h> ,不行,换成#include <ucontext.h> 还是不行。然后仔细的想今天做过什么改动,终于想起来了,因为在sourceinsight里面没有include 系统头文件,所以我就拷贝一份系统头文件到工作目录,结果makefile就包含了这个目录,导致编译出错。
解决办法很简单,就是把工作目录下的系统头文件删掉就可以了。
订阅:
博文 (Atom)






