glibc readv和writev函数改进

最近在改进公司内部网络发送库,发现了linux下高级io操作函数readv和writev,在glibc里面实现的。

使用这两函数需要include<sys/uio.h>

ssize_t readv(int fd,const struct iovec *iov, int count); 

从文件描述符fd所对应的的文件中读取count字节大小数据到多个指定顺序buffers中,该buffer用iovec描述

ssize_t writev(int fd,const struct iovec *iov, int count);

把count个指定顺序的数据buffer(使用iovec描述)写入到文件描述符fd所对应的的文件中

struct iovec结构在bits/uio.h中定义的,是一种向量形式的结构体。

/* Structure for scatter/gather I/O.  */
struct iovec
  {
    void *iov_base; /* Pointer to data.  */
    size_t iov_len; /* Length of data.  */
  };

能将本来需要多次发送的数据,聚合在一起,一次发送,提高IO效率。

但使用时发现了一些问题,readv一次不能完全接收到期望长度数据。查看glibc源码,发现readv、writev底层分别是基于read、write实现的,而read一次本来就可能获得不了期望长度数据。

It is not an error if this number is smaller than the number of bytes requested; this may happen for example because fewer bytes are actually available right now (maybe because we were close to end-of- file, or because we are reading from a pipe, or from a terminal), or because read() was interrupted by a signal.

查看glic源码实现,发现里面并没有处理这个问题,所以才数显与期望不一致问题。

以下是glibc readv实现,glibc/sysdeps/posix/writev.c,read只调用了一次。

#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <limits.h>
#include <stdbool.h>
#include <sys/param.h>
#include <sys/uio.h>
#include <errno.h>
static void
ifree (char **ptrp)
{
  free (*ptrp);
}
/* Read data from file descriptor FD, and put the result in the
   buffers described by VECTOR, which is a vector of COUNT 'struct iovec's.
   The buffers are filled in the order specified.
   Operates just like 'read' (see <unistd.h>) except that data are
   put in VECTOR instead of a contiguous buffer.  */
ssize_t
__readv (int fd, const struct iovec *vector, int count)
{
  /* Find the total number of bytes to be read.  */
  size_t bytes = 0;
  for (int i = 0; i < count; ++i)
    {
      /* Check for ssize_t overflow.  */
      if (SSIZE_MAX - bytes < vector[i].iov_len)
        {
          __set_errno (EINVAL);
          return -1;
        }
      bytes += vector[i].iov_len;
    }
  /* Allocate a temporary buffer to hold the data.  We should normally
     use alloca since it's faster and does not require synchronization
     with other threads.  But we cannot if the amount of memory
     required is too large.  */
  char *buffer;
  char *malloced_buffer __attribute__ ((__cleanup__ (ifree))) = NULL;
  if (__libc_use_alloca (bytes))
    buffer = (char *) __alloca (bytes);
  else
    {
      malloced_buffer = buffer = (char *) malloc (bytes);
      if (buffer == NULL)
        return -1;
    }
  /* Read the data.  */
  ssize_t bytes_read = __read (fd, buffer, bytes);
  if (bytes_read < 0)
    return -1;
  /* Copy the data from BUFFER into the memory specified by VECTOR.  */
  bytes = bytes_read;
  for (int i = 0; i < count; ++i)
    {
      size_t copy = MIN (vector[i].iov_len, bytes);
      (void) memcpy ((void *) vector[i].iov_base, (void *) buffer, copy);
      buffer += copy;
      bytes -= copy;
      if (bytes == 0)
        break;
    }
  return bytes_read;
}

所以需要把read调用改进一下,保证数据能读取完整。以下是改进

#define __set_errno(val) (errno = (val))

static void
ifree (char **ptrp)
{
  free (*ptrp);
}

/* Read data from file descriptor FD, and put the result in the
   buffers described by VECTOR, which is a vector of COUNT 'struct iovec's.
   The buffers are filled in the order specified.
   Operates just like 'read' (see <unistd.h>) except that data are
   put in VECTOR instead of a contiguous buffer.  */
ssize_t
my_readv (int fd, const struct iovec *vector, int count)
{
  /* Find the total number of bytes to be read.  */
  size_t bytes = 0;
  for (int i = 0; i < count; ++i)
    {
      /* Check for ssize_t overflow.  */
      if (SSIZE_MAX - bytes < vector[i].iov_len)
	{
	  __set_errno (EINVAL);
	  return -1;
	}
      bytes += vector[i].iov_len;
    }

  /* Allocate a temporary buffer to hold the data.  We should normally
     use alloca since it's faster and does not require synchronization
     with other threads.  But we cannot if the amount of memory
     required is too large.  */
  char *buffer;
  char *malloced_buffer __attribute__ ((__cleanup__ (ifree))) = NULL;
  if (bytes < 128)
    buffer = (char *) alloca (bytes);
  else
    {
      malloced_buffer = buffer = (char *) malloc (bytes);
      if (buffer == NULL)
	return -1;
    }

  /* Read the data.  */
  //ssize_t bytes_read = read (fd, buffer, bytes);

  // recv loop
  int bytes_read = 0, ret = 0;
  while(bytes_read < bytes) {
  	  ret = read (fd, buffer + bytes_read, bytes - bytes_read);
      if(ret > 0) {
          bytes_read += ret;
          continue;
      }
  
      if(ret == 0){
          break;
      } else {
          if(errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK) {
              continue;
          }
          break;
      }
  }

  if (bytes_read < 0)
    return -1;

  /* Copy the data from BUFFER into the memory specified by VECTOR.  */
  bytes = bytes_read;
  for (int i = 0; i < count; ++i)
    {
      size_t copy = MIN (vector[i].iov_len, bytes);

      (void) memcpy ((void *) vector[i].iov_base, (void *) buffer, copy);

      buffer += copy;
      bytes -= copy;
      if (bytes == 0)
	break;
    }
  return bytes_read;
}

完整code见https://github.com/zhangjun/my_notes/blob/master/linux/io

另外facebook  folly也有实现,见 https://github.com/facebook/folly/blob/master/folly/portability/SysUio.cpp

CentOS7 防火墙配置

最近在CentOS 7上使用rsync,莫名奇妙发现无法连通,最后发现是防火墙打开了。。。
而且CentOS7上防火墙进行了升级,不是使用的iptables
开启防火墙 (关闭stop)

systemctl start firewalld.service

添加端口

firewall-cmd –permanent –zone=public –add-port=873/tcp

(添加后,记得firewall-cmd reload)

无锁编程

无锁编程有两个关键点:

  • 原子操作或者cas原语
  • aba问题
inline bool CAS(pointer_t *addr, pointer_t &old_value, pointer_t &new_value)
{
    bool ret;
    __asm__ __volatile__(
    "lock cmpxchg16b %1;\n"
    "sete %0;\n"
    :"=m"(ret),"+m" (*(volatile pointer_t *) (addr))
    :"a" (old_value.ptr), "d" (old_value.tag), "b" (new_value.ptr), "c" (new_value.tag));
    return ret;
}

lock锁地址总线
aba问题:在读取v值和进行cas操作前,如果有一个线程将v值由a改成b,另外一个线程在将b改成了a,就会cas操作混乱。解决方法是用标记或版本编号与进行CAS操作的每个值相关联,并原子的更新值和标记。
 
入队列

EnQueue (x) //进队列改良版
{
    q = new record ();
    q->value = x;
    q->next = NULL;
    p = tail;
    oldp = p
    do {
        while (p->next != NULL)
            p = p->next;
    } while( CAS (p.next, NULL, q) != TRUE); //如果没有把结点链上,再试
    CAS (tail, oldp, q); //置尾结点
}

出队列

DeQueue () //出队列
{
    do{
        p = head;
        if (p->next == NULL){
            return ERR_EMPTY_QUEUE;
        }
    while( CAS (head, p, p->next);
    return p->next->value;
}

 
https://www.codeproject.com/Articles/153898/Yet-another-implementation-of-a-lock-free-circular
http://www.drdobbs.com/parallel/writing-lock-free-code-a-corrected-queue/210604448?pgno=1
https://blog.csdn.net/chenjiayi_yun/article/details/16333595
https://blog.csdn.net/mergerly/article/details/39009473 scsp无锁kfifo队列
 
 
 
 

pthread_attr_t属性

  • 线程属性
 int pthread_attr_init(pthread_attr_t *attr);
 int pthread_attr_destroy(pthread_attr_t *attr);

如果要去除对pthread_attr_t结构的初始化,可以调用pthread_attr_destroy函数。

int pthread_attr_getstack(const pthread_attr_t *restrict attr,void **restrict stackaddr, size_t *restrict stacksize);
int pthread_attr_setstack(pthread_attr_t *attr, void *stackaddr, size_t *stacksize);

这两个函数可以用于管理stackaddr线程属性,也可以管理stacksize线程属性。
对进程来说,虚拟地址空间的大小是固定的,进程中只有一个栈,所以它的大小通常不是问题。但对于线程来说,同样大小的虚拟地址空间必须被所有的线程共享。如果应用程序使用了太多的线程,致使线程栈的累计大小超过了可用的虚拟地址空间,这时就需要减少线程默认的栈大小。另一方面,如果线程调用的函数分配了大量的自动变量或者调用的函数涉及很深的栈帧,那么这时需要的栈大小可能要比默认的要大。
如果希望改变栈的默认大小,又不想自己分配地址,这时用pthread_attr_setstacksize函数就非常有用。

int pthread_attr_getguardsize(const pthread_attr_t *restrict attr, size_t *restrict guardsize);
int pthread_init_setguardsize(pthread_attr_t *attr, size_t guardsize);

线程属性guardsize控制着线程栈末尾之后用以避免栈溢出的扩展内存的大小。这个属性默认设置为PAGESIZE个字节。可以把guardsize线程属性设为0,从而不允许属性的这种特征行为发生:在这种情况下不会提供警戒缓冲区。同样地,如果对线程属性stackaddr作了修改,系统就会假设我们会自己管理栈,并使警戒栈无效,等同于把其设为0.

  • 线程的分离状态
 int pthread_attr_getdetachstate(const pthread_attr_t *restrict attr, int *deachstate);
 int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);

如果在创建线程时就知道不需要了解线程的终止状态,则可以修改pthread_attr_t结构中的detachstate线程属性,让线程以分离状态启动。可以使用pthread_attr_setdetachstate把线程属性detachstate设置为下面两个合法值之一:PTHREAD_CREATE_DETACHED,以分离状态启动线程;PTHREAD_CREATE_JOINABLE,正常启动线程。默认创建的线程是非分离状态的,这种情形下,原有的线程等待创建的线程结束。只有在pthread_join()函数返回时,创建的线程才算终止,才能释放自己占用的系统资源。分离线程没有被其他的线程所等待,当自己运行结束时,线程也就终止了,马上释放系统资源。

  • 线程的调度
int pthread_attr_setschedpolicy(pthread_attr_t *attr,int policy);
int pthread_attr_getschedpolicy(const pthread_attr_t *attr,int *policy);

第1个参数是指向属性对象的指针,第2个参数是调度策略或指向调度策略的指针。调度策略可能的值是先进先出(SCHED_FIFO)、轮转法(SCHED_RR),或其它(SCHED_OTHER)。

  • 线程的继承性
int pthread_attr_getinheritsched(const pthread_attr_t*attr,int *inheritsched);
int pthread_attr_setinheritsched(pthread_attr_t *attr,intinheritsched);

分别用来设置和得到线程的继承性。第一个参数指向属性对象的指针,第二个参数是继承性或者指向继承性的指针。继承性决定调度的参数是从创建的进程中继承还是使用在schedpolicy和schedparam属性中显式设置的调度信息。 继承性的可能值是PTHREAD_INHERIT_SCHED(表示新现成将继承创建线程的调度策略和参数)和PTHREAD_EXPLICIT_SCHED(表示使用在schedpolicy和schedparam属性中显式设置的调度策略和参数)。
如果你需要显式的设置一个线程的调度策略或参数,那么你必须在设置之前将inheritsched属性设置为PTHREAD_EXPLICIT_SCHED.

  • 线程的调度参数
int pthread_attr_getschedparam(const pthread_attr_t *attr,struct sched_param *param);
int pthread_attr_setschedparam(pthread_attr_t *attr,conststruct sched_param *param);

分别用来得到和设置线程的调度参数。sched_param结构体的一个子成员sched_priority控制一个优先权值,大的有限权值对应高的优先权。系统支持的最大和最小的优先权值可以分别通过sched_get_priority_max( int policy )和sched_get_priority_min( int policy )两个函数获得。

  • 设置进程的竞争范围属性
int pthread_attr_setscope(pthread_attr_t *attr,int scope);
int pthread_attr_getscope(pthread_attr_t *attr,int *scope);

竞争返回属性定义一个线程对资源,如CPU,争夺的线程范围。第二个参数为PTHREAD_SCOPE_SYSTEM时,表示与系统中相同调度分配域下所有进程的其他线程进行竞争; 第二个参数为PTHREAD_SCOPE_PROCESS时,表示与同一个进程中同样是以PTHREAD_SCOPE_PROCESS属性创建的其他线程进行竞争。
注:更多关于线程互斥量、读写锁、条件变量的属性设置不在此文中详述,需要查阅时参考《Unix环境高级编程第2版》p318。
线程安全:如果一个函数在同一时刻可以被多个线程安全地调用,就称该函数是线程安全的。
如果一个函数是线程安全的,但这并不能说明对信号处理函数也是可重入的。如果函数对异步信号处理程序的重入是安全的,那么就说函数是异步-信号安全的。

int ftrylockfile(FILE *fp);
void flockfile(FILE *fp); 
void funlockfile(FILE *fp);

可以使用flockfile和ftrylockfile获取与给定FILE对象关联的锁。这个锁是递归的,当占有这把锁的时候,还可以再次获取改锁,这并不会导致死锁。

 pthread_key_create(pthread_key_t *keyp, void (*destructor)(void *));

在分配线程私有数据之前,需要创建与该数据关联的键。这个键将用于获取对线程私有数据的访问权。
创建的键存放在keyp指向的内存单元,这个键可以被进程中的所有线程使用,但每个线程把这个键与不同的线程私有数据地址相关联。创建新键时,每个线程的数据地址设为null值。
除了创建键以外,pthread_key_create可以选择为该键关联析构函数,当线程退出时,如果数据地址已经被置为非null值,那么析构函数会被调用。当线程调用pthread_exit或者线程执行返回,正常退出时,会被调用;但线程如果调用了exit,_exit,_Exit,abort等非正常退出时,就不会调用析构函数。
线程通常使用malloc为线程私有数据分配空间,析构函数通常释放已分配的内存,如果线程没有释放内存就退出了,那么线程所属进程出现了内存泄漏。

 int pthread_key_delete(pthread_key_t *key);

对所有的线程,都可以通过调用pthread_key_delete来取消键与线程私有数据值之间的关联关系。

 void *pthread_getspecific(pthread_key_t key);
 int pthread_setspecific(pthread_key_t, key, const void *value);

键一旦创建,就可以通过调用pthread_setspecific把键和线程私有数据关联起来。可以通过pthread_getspecific函数获得线程私有数据的地址。如果没有线程私有数据值与键关联,pthread_getspecific将返回一个空指针,可以据此确定是否需要调用pthread_setspecific。

 int pthread_sigmask(int how, const sigset_t *restrict set,sigset_t *restrict oset);

pthread_sigmask函数与sigprocmask函数基本相同,除了pthread_sigmask工作在线程中,并且失败时返回错误码,而不像sigprocmask中那样设置errno并返回-1。

 int sigwait(const sigset_t *restrict set, int *restrict signop);

线程可以通过调用sigwait等待一个或多个信号发生。

 int pthread_kill(pthread_t thread, int signo);

要把信号发送到进程,可以调用kill;要把信号发送给线程可以调用pthread_kill。可以传一个0值的signo来检查线程是否存在。如果信号的默认处理动作是终止该进程,那么把信号传递给某个线程仍然会杀掉整个进程。

 int pthrad_atfork(void (*prepare)(void), void (*parent)(void), void (*child)(void));

子进程通过继承整个地址空间的副本,也从父进程那里继承了所有互斥量、读写锁、和条件变量的状态栏。如果父进程包含多个线程,子进程在fork返回以后,如果紧接着不是马上exec的话就需要清理锁状态。
用pthread_atfork函数最多可以安装三个帮助清理锁的函数。prepare fork处理程序由父进程在fork创建子进程之前调用,这个fork处理程序的任务是获取父进程定义的所有锁。parent fork处理程序是在fork创建了子进程以后,但在fork返回之前在父进程环境中调用的,这个fork处理程序的任务是对parent fork处理程序获得的所有锁进行解锁。child fork处理程序在fork返回之前在子进程环境中调用,与parent fork一样,child fork处理程序也必须释放prepare fork处理程序获得的所有锁。

指定进程运行的CPU

1、taskset
-p, –pid
operate on an existing PID and not launch a new task
-c, –cpu-list
specify a numerical list of processors instead of a bitmask. The list may contain multiple items, separated by comma, and ranges. For example, 0,5,7,9-11.
taskset -p pid   显示进程号为pid的进行运行的cpu。显示结果为和cpu affinity有关的二进制bitmask。从地位到高位,每一个1对应一个cpu
taskset -pc 3 pid  制定进程运行在特定cpu上,这里是第4个cpu
taskset -c 1 ./redis-server 进程启动时指定cpu
2、sched_setaffinity系统调用
int sched_setaffinity(pid_t pid, size_t cpusetsize, cpu_set_t *mask) 将某个进程绑定到特定的CPU。定义在sched.h头文件中。成功返回0,错误返回-1。 pid为0,表示当前调用进行。
int sched_getaffinity(pid_t pid, size_t cpusetsize, cpu_set_t *mask) 查看一个进程的CPU亲和度掩码
cpu affinity bitmask会从父进程传递给其派生出的子进程。
POSIX API环境下使用int pthread_setaffinity_np(pthread_t thread, size_t cpusetsize,
const cpu_set_t *cpuset);
int pthread_getaffinity_np(pthread_t thread, size_t cpusetsize,
cpu_set_t *cpuset);

git连接github

配置本机git

git config --global user.name "username"
git config --global user.email youremail

生成密钥

ssh-keygen -t rsa -C "youremail"

将生成密钥.pub内容添加到github帐号。进入github帐号account setting,添加ssh key
检验是否连接上github

ssh git@github.com

Permission denied (publickey).解决办法
ssh-add ~/.ssh/git_id_rsa(ssh-add之前首先运行ssh-agent bash, 否则出现错误Could not open a connection to your authentication agent)

#create a new repository on the command line
git init
git add README.md
git commit -m "first commit"
git remote add origin https://github.com/username/project.git
git push -u origin master
#push an existing repository from the command line
git remote add origin https://github.com/username/project.git
git push -u origin master
git remote add origin git@github.com:username/project.git
git push origin master

查看提交地址

git remot -v

 
http://my.oschina.net/alvin404/blog/205745
http://www.cnblogs.com/plinx/archive/2013/04/08/3009159.html
http://blog.aboutc.net/linux/60/github-login-error
http://www.linuxidc.com/Linux/2011-04/35036.htm

Git错误non-fast-forward后的冲突解决

问题(Non-fast-forward)的出现原因在于:git仓库中已经有一部分代码,所以它不允许你直接把你的代码覆盖上去。于是你有2个选择方式:
1,强推,即利用强覆盖方式用你本地的代码替代git仓库内的内容
git push -f
2,先把git的东西fetch到你本地然后merge后再push
$ git fetch
$ git merge
这2句命令等价于

$ git pull

可是,这时候又出现了如下的问题:

上面出现的 [branch “master”]是需要明确(.git/config)如下的内容
[branch “master”]
remote = origin
merge = refs/heads/master
这等于告诉git2件事:
1,当你处于master branch, 默认的remote就是origin。
2,当你在master branch上使用git pull时,没有指定remote和branch,那么git就会采用默认的remote(也就是origin)来merge在master branch上所有的改变
如果不想或者不会编辑config文件的话,可以在bush上输入如下命令行:

$ git config branch.master.remote origin
$ git config branch.master.merge refs/heads/master

之后再重新git pull下。最后git push你的代码吧。it works now~
解决github push错误The requested URL returned error: 403 Forbidden while accessing
解决方案:
vim .git/config
修改

[remote "origin"]
	url = https://github.com/wangz/example.git

为:

[remote "origin"]
	url = https://wangz@github.com/wangz/example.git

再次git push,弹出框输入密码,即可提交

tcpdump用法

第一种是关于类型的关键字,主要包括host,net,port, 例如 host 210.27.48.2,指明210.27.48.2是一台主机,net 202.0.0.0 指明 202.0.0.0是一个网络地址,port 23指明端口号是23。如果没有指定类型,缺省的类型是host.
第二种是确定传输方向的关键字,主要包括src , dst ,dst or src, dst and src, 这些关键字指明了传输的方向。举例说明,src 210.27.48.2 ,指明ip包中源地址是210.27.48.2 , dst net 202.0.0.0 指明目的网络地址是202.0.0.0 。如果没有指明方向关键字,则缺省是src or dst关键字。
第三种是协议的关键字,主要包括fddi,ip,arp,rarp,tcp,udp等类型。Fddi指明是在FDDI(分布式光纤数据接口网络)上的特定的网络协议,实际上它是”ether”的别名,fddi和ether具有类似的源地址和目的地址,所以可以将fddi协议包当作ether的包进行处理和分析。其他的几个关键字就是指明了监听的包的协议内容。如果没有指定任何协议,则tcpdump将会监听所有协议的信息包。
除了这三种类型的关键字之外,其他重要的关键字如下:gateway, broadcast,less,greater,还有三种逻辑运算,取非运算是 ‘not ‘ ‘! ‘, 与运算是’and’,’&&’;或运算是’or’ ,’││’;这些关键字可以组合起来构成强大的组合条件来满足人们的需要,下面举几个例子来说明。
普通情况下,直接启动tcpdump将监视第一个网络界面上所有流过的数据包。
# tcpdump
tcpdump: listening on fxp0
11:58:47.873028 202.102.245.40.netbios-ns > 202.102.245.127.netbios-ns: udp 50
11:58:47.974331 0:10:7b:8:3a:56 > 1:80:c2:0:0:0 802.1d ui/C len=43
0000 0000 0080 0000 1007 cf08 0900 0000
0e80 0000 902b 4695 0980 8701 0014 0002
000f 0000 902b 4695 0008 00
11:58:48.373134 0:0:e8:5b:6d:85 > Broadcast sap e0 ui/C len=97
ffff 0060 0004 ffff ffff ffff ffff ffff
0452 ffff ffff 0000 e85b 6d85 4008 0002
0640 4d41 5354 4552 5f57 4542 0000 0000
0000 00
使用-i参数指定tcpdump监听的网络界面,这在计算机具有多个网络界面时非常有用,
使用-c参数指定要监听的数据包数量,
使用-w参数指定将监听到的数据包写入文件中保存
A想要截获所有210.27.48.1 的主机收到的和发出的所有的数据包:
#tcpdump host 210.27.48.1
B想要截获主机210.27.48.1 和主机210.27.48.2 或210.27.48.3的通信,使用命令:(在命令行中适用   括号时,一定要#tcpdump host 210.27.48.1 and (210.27.48.2 or 210.27.48.3 )
C如果想要获取主机210.27.48.1除了和主机210.27.48.2之外所有主机通信的ip包,使用命令:
#tcpdump ip host 210.27.48.1 and ! 210.27.48.2
D如果想要获取主机210.27.48.1接收或发出的telnet包,使用如下命令:
#tcpdump tcp port 23 host 210.27.48.1
E 对本机的udp 123 端口进行监视 123 为ntp的服务端口
# tcpdump udp port 123
F 系统将只对名为hostname的主机的通信数据包进行监视。主机名可以是本地主机,也可以是网络的任何一台计算机。下面的命令可以读取主机hostname发送的所有数据:
#tcpdump -i eth0 src host hostname
G 下面的命令可以监视所有送到主机hostname的数据包:
#tcpdump -i eth0 dst host hostname
H 我们还可以监视通过指定网关的数据包:
#tcpdump -i eth0 gateway Gatewayname
I 如果你还想监视编址到指定端口的TCP或UDP数据包,那么执行以下命令:
#tcpdump -i eth0 host hostname and port 80
J 如果想要获取主机210.27.48.1除了和主机210.27.48.2之外所有主机通信的ip包,使用命令:
#tcpdump ip host 210.27.48.1 and ! 210.27.48.2
K 想要截获主机210.27.48.1 和主机210.27.48.2 或210.27.48.3的通信,使用命令:(在命令行中适用括号时,一定要#tcpdump host 210.27.48.1 and (210.27.48.2 or 210.27.48.3 )
L 如果想要获取主机210.27.48.1除了和主机210.27.48.2之外所有主机通信的ip包,使用命令:
#tcpdump ip host 210.27.48.1 and ! 210.27.48.2
M 如果想要获取主机210.27.48.1接收或发出的telnet包,使用如下命令:
#tcpdump tcp port 23 host 210.27.48.1
第三种是协议的关键字,主要包括fddi,ip ,arp,rarp,tcp,udp等类型除了这三种类型的关键字之外,其他重要的关键字如下:gateway, broadcast,less, greater,还有三种逻辑运算,取非运算是 ‘not ‘ ‘! ‘, 与运算是’and’,’&&’;或运算 是’or’ ,’||’;
第二种是确定传输方向的关键字,主要包括src , dst ,dst or src, dst and src ,如果我们只需要列出送到80端口的数据包,用dst port;如果我们只希望看到返回80端口的数据包,用src port。
#tcpdump –i eth0 host hostname and dst port 80 目的端口是80
或者
#tcpdump –i eth0 host hostname and src port 80 源端口是80 一般是提供http的服务的主机如果条件很多的话 要在条件之前加and 或 or 或 not
#tcpdump -i eth0 host ! 211.161.223.70 and ! 211.161.223.71 and dst port 80
如果在ethernet 使用混杂模式 系统的日志将会记录
May 7 20:03:46 localhost kernel: eth0: Promiscuous mode enabled.
May 7 20:03:46 localhost kernel: device eth0 entered promiscuous mode
May 7 20:03:57 localhost kernel: device eth0 left promiscuous mode
tcpdump 对截获的数据并没有进行彻底解码,数据包内的大部分内容是使用十六进制的形式直接打印输出的。显然这不利于分析网络故障,通常的解决办法是先使用带-w参数的tcpdump截获数据并保存到文件中,然后再使用其他程序进行解码分析。当然也应该定义过滤规则,以避免捕获的数据包填满整个硬盘。

time,gettimeofday,clock_gettime,_ftime

time()提供了秒级的精确度
1、头文件 <time.h>
2、函数原型
time_t time(time_t * timer)
函数返回从TC1970-1-1 0:0:0开始到现在的秒数
用time()函数结合其他函数(如:localtime、gmtime、asctime、ctime)可以获得当前系统时间或是标准时间。
#include <time.h>
#include <stdio.h>
int main(void)
{
    time_t t;
    t = time(NULL);
    printf("The number of seconds since January 1, 1970 is %ld",t);
    return 0;
}
#include <stdio.h>
#include <stddef.h>
#include <time.h>
int main(void)
{
    time_t timer;//time_t就是long int 类型
    struct tm *tblock;
    timer = time(NULL);//这一句也可以改成time(&timer);
    tblock = localtime(&timer);
    printf("Local time is: %s/n",asctime(tblock));
    return 0;
}

 

gettimeofday()提供了微秒级的精确度
1、头文件 <time.h>
2、函数原型
int gettimeofday(struct timeval *tv, struct timezone *tz);
gettimeofday()会把目前的时间由tv所指的结构返回,当地时区的信息则放到tz所指的结构中(可用NULL)。
参数说明:
    timeval结构定义为:
    struct timeval
    {
        long tv_sec; /*秒*/
        long tv_usec; /*微秒*/
    };
    timezone 结构定义为:
    struct timezone
    {
        int tz_minuteswest; /*和Greenwich 时间差了多少分钟*/
        int tz_dsttime; /*日光节约时间的状态*/
    };
    上述两个结构都定义在/usr/include/sys/time.h。tz_dsttime 所代表的状态如下
        DST_NONE /*不使用*/
        DST_USA /*美国*/
        DST_AUST /*澳洲*/
        DST_WET /*西欧*/
        DST_MET /*中欧*/
        DST_EET /*东欧*/
        DST_CAN /*加拿大*/
        DST_GB /*大不列颠*/
        DST_RUM /*罗马尼亚*/
        DST_TUR /*土耳其*/
        DST_AUSTALT /*澳洲(1986年以后)*/
返回值: 成功则返回0,失败返回-1,错误代码存于errno。附加说明EFAULT指针tv和tz所指的内存空间超出存取权限。
#include<stdio.h>
#include<time.h>
int main(void)
{
    struct timeval tv;
    struct timezone tz;
    gettimeofday (&tv , &tz);
    printf(“tv_sec; %d/n”, tv,.tv_sec) ;
    printf(“tv_usec; %d/n”,tv.tv_usec);
    printf(“tz_minuteswest; %d/n”, tz.tz_minuteswest);
    printf(“tz_dsttime, %d/n”,tz.tz_dsttime);
    return 0;
}

 

clock_gettime( ) 提供了纳秒级的精确度
1、头文件 <time.h>
2、编译&链接。在编译链接时需加上 -lrt ;因为在librt中实现了clock_gettime函数
3、函数原型
int clock_gettime(clockid_t clk_id, struct timespect *tp);
    参数说明:
    clockid_t clk_id 用于指定计时时钟的类型,有以下4种:
        CLOCK_REALTIME:系统实时时间,随系统实时时间改变而改变,即从UTC1970-1-1 0:0:0开始计时,中间时刻如果系统时间被用户该成其他,则对应的时间相应改变
        CLOCK_MONOTONIC:从系统启动这一刻起开始计时,不受系统时间被用户改变的影响
        CLOCK_PROCESS_CPUTIME_ID:本进程到当前代码系统CPU花费的时间
        CLOCK_THREAD_CPUTIME_ID:本线程到当前代码系统CPU花费的时间
    struct timespect *tp用来存储当前的时间,其结构如下:
        struct timespec
        {
            time_t tv_sec; /* seconds */
            long tv_nsec; /* nanoseconds */
        };
    返回值。0成功,-1失败
#include<stdio.h>
#include<time.h>
int main()
{
    struct timespec ts;
    clock_gettime(CLOCK_REALTIME, &ts);
    printf("CLOCK_REALTIME: %d, %d", ts.tv_sec, ts.tv_nsec);
    clock_gettime(CLOCK_MONOTONIC, &ts);//打印出来的时间跟 cat /proc/uptime 第一个参数一样
    printf("CLOCK_MONOTONIC: %d, %d", ts.tv_sec, ts.tv_nsec);
    clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ts);
    printf("CLOCK_PROCESS_CPUTIME_ID: %d, %d", ts.tv_sec, ts.tv_nsec);
    clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts);
    printf("CLOCK_THREAD_CPUTIME_ID: %d, %d", ts.tv_sec, ts.tv_nsec);
    printf("/n%d/n", time(NULL));
    return 0;
}
/proc/uptime里面的两个数字分别表示:
the uptime of the system (seconds), and the amount of time spent in idle process (seconds).
把第一个数读出来,那就是从系统启动至今的时间,单位是秒

 

_ftime()提供毫秒级的精确度
1、头文件 <sys/types.h> and <sys/timeb.h>
2、函数原型
void _ftime(struct _timeb *timeptr);
参数说明:
    struct _timeb
    {
        time_t time;
        unsigned short millitm;
        short timezone;
        short dstflag;
    };
#include <stdio.h>
#include <sys/timeb.h>
#include <time.h>
void main( void )
{
    struct _timeb timebuffer;
    char *timeline;
    _ftime( &timebuffer );
    timeline = ctime( & ( timebuffer.time ) );
    printf( "The time is %.19s.%hu %s", timeline, timebuffer.millitm, &timeline[20] );
}

 
reference:
http://blog.csdn.net/wind19/article/details/12975173
http://blog.csdn.net/sunlylorn/article/details/6313278

程序在内存中的分布

在现代的操作系统中,当我们说到内存,往往需要分两部分来讲:物理内存和虚拟内存。从硬件上讲,虚拟空间是CPU内部的寻址空间,位于MMU之前,物理空间是总线上的寻址空间,是经过MMU转换之后的空间。
一般我们所说的程序在内存中的分布指的就是程序在虚拟内存中的存储方式。
从低地址到高地址,可分为下面几段:
预留内存地址
(操作系统维护的内存地址,不可访问)
程序代码区(只读,存代码和一些其他的东西);
data段(存初始化的全局变量和static变量,另外还有文字常量区,常量字符串就是放在这里,程序结束后有系统释放);
bss段(存未初始化的全局变量和static变量);
(由低地址向高地址增长,一般new和malloc分配,由程序员分配释放);
共享库文件(调用的库文件,位于堆和栈之间);
(由高地址向低地址增长,和堆的增长方式相对,对不同的OS来说,栈的初始大小有规定,可以修改,目前默认一般为2M,由编译器自动分配释放);
再上面存的都是操作系统和内核调用的一些内存地址
如图所示:

vim 命令

1、切换到下一个文件
假设要切换的文件名为 ne.c

:ex ne.c

2、文档内复制命令

yy   "复制光标所在行。或大写一个Y。
2yy  "复制两行。
y^   "复制至行首,或y0。不含游标所在处字元。
y$   "复制至行尾。含游标所在处字元。(注意跟y^的区别)
yw   "复制一个单词(word)。
y2w  "复制两个字(单词)。
yG   "复制至档尾。
y1G  "复制至档首。
p    "小写p,贴至光标后(下)。
P    "大写P,贴至光标前(上)。

3、文档间复制命令
需要复制到粘贴板a
第2条中的命令在后面加一个英文双引号和一个a即可,例如:

"ayy    "复制一行
"a3yy   "复制3行
"ap     "粘贴至光标后(下)