从nginx说cpu affinity
syntax: worker_cpu_affinity cpumask ...;default: —context: mainBinds worker processes to the sets of CPUs. Each CPU set is represented by a bitmask of allowed CPUs. There should be a separate set defined for each of the worker processes. By default, worker processes are not bound to any specific CPUs.For example,worker_processes 4;worker_cpu_affinity 0001 0010 0100 1000;binds each worker process to a separate CPU, whileworker_processes 2;worker_cpu_affinity 0101 1010;binds the first worker process to CPU0/CPU2, and the second worker process to CPU1/CPU3. The second example is suitable for hyper-threading.The directive is only available on FreeBSD and Linux.
void ngx_setaffinity(uint64_t cpu_affinity, ngx_log_t *log) { cpu_set_t mask; ngx_uint_t i; ngx_log_error(NGX_LOG_NOTICE, log, 0, "sched_setaffinity(0x%08Xl)", cpu_affinity); CPU_ZERO(&mask); i = 0; do { if (cpu_affinity & 1) { CPU_SET(i, &mask); } i++; cpu_affinity >>= 1; } while (cpu_affinity); if (sched_setaffinity(0, sizeof(cpu_set_t), &mask) == -1) { ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, "sched_setaffinity() failed"); } }
cpu_set_t的定义见/usr/include/bits/sched.h /* Size definition for CPU sets. */ # define __CPU_SETSIZE 1024 # define __NCPUBITS (8 * sizeof (__cpu_mask)) /* Type for array elements in 'cpu_set_t'. */ typedef unsigned long int __cpu_mask; /* Data structure to describe CPU mask. */ typedef struct { __cpu_mask __bits[__CPU_SETSIZE / __NCPUBITS]; } cpu_set_t;
/* Access functions for CPU masks. */ # define __CPU_ZERO_S(setsize, cpusetp) \ do { \ size_t __i; \ size_t __imax = (setsize) / sizeof (__cpu_mask); \ __cpu_mask *__bits = (cpusetp)->__bits; \ for (__i = 0; __i < __imax; ++__i) \ __bits[__i] = 0; \ } while (0) # endif # define __CPU_SET_S(cpu, setsize, cpusetp) \ (__extension__ \ ({ size_t __cpu = (cpu); \ __cpu / 8 < (setsize) \ ? (((__cpu_mask *) ((cpusetp)->__bits))[__CPUELT (__cpu)] \ |= __CPUMASK (__cpu)) \ : 0; })) # define __CPU_CLR_S(cpu, setsize, cpusetp) \ (__extension__ \ ({ size_t __cpu = (cpu); \ __cpu / 8 < (setsize) \ ? (((__cpu_mask *) ((cpusetp)->__bits))[__CPUELT (__cpu)] \ &= ~__CPUMASK (__cpu)) \ : 0; })) # define __CPU_ISSET_S(cpu, setsize, cpusetp) \ (__extension__ \ ({ size_t __cpu = (cpu); \ __cpu / 8 < (setsize) \ ? ((((const __cpu_mask *) ((cpusetp)->__bits))[__CPUELT (__cpu)] \ & __CPUMASK (__cpu))) != 0 \ : 0; }))
#define _GNU_SOURCE #include <sched.h> #include <stdio.h> #include <stdint.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <unistd.h> int print_cpu_affinity() { int i = 0; cpu_set_t cpu_mask; if(sched_getaffinity(0, sizeof(cpu_set_t), &cpu_mask) == -1) { printf("get_cpu_affinity fail, %s\n", strerror(errno)); return -1; } printf("cpu affinity: "); for(i = 0; i < CPU_SETSIZE; i++) { if(CPU_ISSET(i, &cpu_mask)) printf("%d ", i); } printf("\n"); return 0; } int set_cpu_affinity(uint32_t cpu_affinity) { int i = 0; cpu_set_t cpu_mask; CPU_ZERO(&cpu_mask); for(i = 0;cpu_affinity; i++, cpu_affinity >>= 1) { if(cpu_affinity & 1) { CPU_SET(i, &cpu_mask); } } if(sched_setaffinity(0, sizeof(cpu_set_t), &cpu_mask) == -1) { printf("set_cpu_affinity fail, %s\n", strerror(errno)); return -1; } return 0; } void hold() { while(1) { ; } } int main(int argc, char **argv) { uint32_t cpu_affinity = 10; if( argc < 2 ) { printf("no affinity given, use cpu_affinity = 10\n"); } else { cpu_affinity = strtoul(argv[1], NULL, 2); } print_cpu_affinity(); set_cpu_affinity(cpu_affinity); print_cpu_affinity(); hold(); return 0; }
编译运行
$ cc cpu_affinity.c -o cpu_affinity $ ./cpu_affinity 100 cpu affinity: 0 1 2 3 4 5 6 7 cpu affinity: 2 $ mpstat -P ALL 1 Average: CPU %usr %nice %sys %iowait %irq %soft %steal %guest %gnice %idle Average: all 12.51 0.00 0.00 0.00 0.00 0.00 0.06 0.00 0.00 87.43 Average: 0 0.00 0.00 0.17 0.00 0.00 0.00 0.00 0.00 0.00 99.83 Average: 1 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 100.00 Average: 2 99.50 0.00 0.00 0.00 0.00 0.00 0.50 0.00 0.00 0.00 Average: 3 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 100.00 Average: 4 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 100.00 Average: 5 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 100.00 Average: 6 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 100.00 Average: 7 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 100.00
# taskset -c 3,5-7 ./cpu_affinity 100 cpu affinity: 3 5 6 7 cpu affinity: 2
# pgrep cpu_affinity 11353 # taskset -p -c 3,5-7 11353 pid 11353's current affinity list: 2 pid 11353's new affinity list: 3,5-7
Tcpdump抓包重放
开发工作中经常有这样的场景,和同事联调的时候,对方发了一个测试包过来,这边用tcpdump –Xlnsp0抓到包了,但是程序结果不正确,然后自己debug,修改,然后需要再测试,这时候再让同事发一个?如果不正确,这一来一回的比较慢,如果有个工具自动把tcpdump抓下来的内容再发出去,就ok了。
于是问题就是这样的,比如用tcpdump –Xlnps0抓到一个udp包,然后用工具把这个包重新发出去。
![endif]--> !--[if> ![endif]--> !--[if> ![endif]--> !--[if> ![endif]--> !--[if>
Linux下用g++编译共享库的一个问题
最近在使用linux下的共享库so的时候遇到一个奇怪的问题,做个记录,方便备查。
一般来说,如果用gcc编译的时候加上-shared和-fPIC选项,可以把源文件编译成一个so文件,可以在其他源程序连接阶段把这个链接上去,从而可以调用so文件提供的函数接口,这样可以多文件共用一个so文件提供的函数,即节省内存空间,也便于更新,所有的接口只需要更新so文件就行。
其实除了上面的方法,还有一个方法,那就是在运行时由程序自己动态加载so文件,使用一系列系统调用如dlopen,dlsym,dlclose等来进行动态加载,获取函数地址从而进行函数调用,关闭加载的so文件等。
一般以第一种方法用得多,但是第二种方法更灵活,结合配置文件,更具一般意义上的服务扩展性。 但是就是在用第二种方法的时候出现了一点问题。
这里把问题抽象一下,假设有一个源文件是要编译为so文件的,假设这个源文件只提供一个简单的函数,add,取两个整数为参数,返回它们的和,源文件为add.c,代码如下:
#include <stdio.h> int add(int a, int b) { return a+b; }
gcc -shared -fPIC add.c -o libadd.so
#include <stdio.h> #include <stdlib.h> #include <dlfcn.h> int main(int argc, char *argv[]) { void * handle; int (*func)(int, int); char *error; handle = dlopen("libadd.so", RTLD_LAZY); if(!handle) { fprintf(stderr, "%s\n", dlerror()); exit(1); } func = (int (*)(int,int))dlsym(handle, "add"); if((error = dlerror()) != NULL) { fprintf(stderr, "%s\n", error); exit(1); } func(3, 4); dlclose(handle); return 0; }
gcc test.c -o test -ldl
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:.
./libadd.so: undefined symbol: add
$ nm libadd.so |grep add
00000000000005ec T _Z3addii
$ c++filt _Z3addii
add(int, int)
extern "C" { // the function code ... }
$ nm libadd.so |grep add
00000000000005dc T add
统计整数二进制表示中1的个数
这是一个很有意思的问题,也是在面试中最容易被问到的问题之一。这个问题有个正式的名字叫Hamming_weight,而且wikipedia上也提供了很好的位运算解决的方法,这个下面也会提到。
解决这个问题的第一想法是一位一位的观察,判断是否为1,是则计数器加一,否则跳到下一位,于是很容易有这样的程序。
int test(int n) { int count=0; while(n != 0){ if(n%2 ==1) count++; n /= 2; } return count; }
或者和其等价的位运算版本:
int test(int n) { int count=0; while(n != 0){ count += n&1; n >>= 1; } return count; }
这样的方法复杂度为二进制的位数,即[tex]\log_2n[/tex],于是可是想一下,有没有只与二进制中1的位数相关的算法呢。
可以考虑每次找到从最低位开始遇到的第一个1,计数,再把它清零,清零的位运算操作是与一个零,但是在有1的这一位与零的操作要同时不影响未统计过的位数和已经统计过的位数,于是可以有这样一个操作 n&(n-1) ,这个操作对比当前操作位高的位没有影响,对低位则完全清零。拿6(110)来做例子,第一次 110&101=100,这次操作成功的把从低位起第一个1消掉了,同时计数器加1,第二次100&011=000,同理又统计了高位的一个1,此时n已变为0,不需要再继续了,于是110中有2个1。
代码如下:
int test(int n) { int count=0; while(n != 0){ n &= n-1; count ++; } return count; }
这几个方法虽然也用到了位运算,但是并没有体现其神奇之处,下面这个版本则彰显位运算的强大能力,若不告诉这个函数的功能,一般一眼看上去是想不到这是做什么的,这也是wikipedia上给出的计算hamming_weight方法。
int test(int n) { n = (n&0x55555555) + ((n>>1)&0x55555555); n = (n&0x33333333) + ((n>>2)&0x33333333); n = (n&0x0f0f0f0f) + ((n>>4)&0x0f0f0f0f); n = (n&0x00ff00ff) + ((n>>8)&0x00ff00ff); n = (n&0x0000ffff) + ((n>>16)&0x0000ffff); return n; }
没有循环,5个位运算语句,一次搞定。
比如这个例子,143的二进制表示是10001111,这里只有8位,高位的0怎么进行与的位运算也是0,所以只考虑低位的运算,按照这个算法走一次
+---+---+---+---+---+---+---+---+
| 1 | 0 | 0 | 0 | 1 | 1 | 1 | 1 | <---143
+---+---+---+---+---+---+---+---+
| 0 1 | 0 0 | 1 0 | 1 0 | <---第一次运算后
+-------+-------+-------+-------+
| 0 0 0 1 | 0 1 0 0 | <---第二次运算后
+---------------+---------------+
| 0 0 0 0 0 1 0 1 | <---第三次运算后,得数为5
+-------------------------------+
这里运用了分治的思想,先计算每对相邻的2位中有几个1,再计算每相邻的4位中有几个1,下来8位,16位,32位,因为2^5=32,所以对于32位的机器,5条位运算语句就够了。
像这里第二行第一个格子中,01就表示前两位有1个1,00表示下来的两位中没有1,其实同理。再下来01+00=0001表示前四位中有1个1,同样的10+10=0100表示低四位中有4个1,最后一步0001+0100=00000101表示整个8位中有5个1。
15身份证号码转18位的程序
以前在哪看到的,安全焦点吧!丢这做个备份
/*输入原来的15位身份证号码,产生新的18位身份证号码的程序*/ #include "stdio.h" #include "string.h" #include "conio.h" /* * gen New 18 ID Card from old 15 ID */ char genNewID( char ID[], char NewID[]) { int W[18] = {7,9,10,5,8,4,2,1,6,3,7,9,10,5,8,4,2,1}; char A[11] = {'1','0','x','9','8','7','6','5','4','3','2'}; int i,j,S; if(strlen(ID) != 15) return -1; memcpy( NewID, ID, 6 ); NewID[6]='1'; NewID[7]='9'; NewID[8]=0; strcat( NewID, &ID[6] ); S = 0; for(i=0;i<17;i++) { j = (NewID[i] - '0') * W[i]; S = S + j; } S = S % 11; NewID[17] = A[S]; NewID[18] = 0; return A[S]; } int main(int argc, char* argv[]) { char ID[20], NewID[20], ret; puts("输入原来的15位身份证号码,产生新的18位身份证号码\n"); do{ printf("Input your old 15 ID Card: "); scanf( "%s", ID ); if(stricmp(ID, "exit") == 0)break; ret = genNewID( ID, NewID ); printf("Your New 18 ID Card: %s \n", ret != -1 ? NewID : "Input Error!!"); }while(1); getch(); return 0; }