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重启进程。
后来运维上线来搞了,方法记录一下:
/etc/security/limits.conf
加上:
* soft 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是这个进程内未分配的最小文件描述符,好吧,这样就清楚了。

想到的其他问题:
1.一个进程如果多次打开同一文件,会得到不同的fd。
2.一个进程内多个线程打开一个文件会得到不同的fd。
3.对于记录锁,一个进程可以提出多个锁请求,即新锁可以替换老锁。一个进程多次打开一个文件,得到多个fd,再对这些fd上锁,这是ok的,即不会出现等锁的情况。
4.继续,一个进程创建了多个线程,多个线程打开一个文件,得到不同fd,众线程对各种的fd设置锁,是不会出现一个线程等待另一个线程的情况。所以,记录锁不能用于线程

2014年7月17日星期四

semop和ipcs

说起semop函数,其实man手册都说得比较清楚了,其作用就是对信号量进行PV操作。平时使用的时候参考手册就可以,但是如果结合ipcs命令的话就可以更直观的看到信号量的变化。

上代码:
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 }

编译后运行程序,程序在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一样好用了。

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选项:这个选项是全局的,一旦用了就要求全部链接静态库。

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的颜色就好看多了

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还是非阻塞吗
  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就包含了这个目录,导致编译出错。
解决办法很简单,就是把工作目录下的系统头文件删掉就可以了。