epoll

来源:互联网 发布:图纸画图软件 编辑:程序博客网 时间:2024/04/29 02:36
Mysql - 解决Access denied for user ''@'localhost' to database 'mysql'问题

来源:

首先我想说一句话:

我极度鄙视国内搞IT的人,简直无语,同样是解决这个问题,几乎一模一样,都是抄来的。更有甚者,抛出的错误是:Access denied for user 'root'@'localhost' to database 'mysql',竟然也把别人的解决方法照搬过来,还恬不知耻的说:问题解决了,我估计都没有真正去操作过。

下面是本人在参考别人的基础之上得到的解决方法:

问题的引出:

我 在CentOS上装完mysql后,用navicat链接的时候,抛出MySql - SQL Error (1130): Host IP is not allowed to connect to this MySQL server 错误,具体解决方法请参考本人日志http://361324767.blog.163.com/blog/static/114902525201232832621498/。

该问题解决后,本人登录mysql ,输mysql -u root时无错,但是执行如下语句时:

mysql> use mysql

抛出该错误,参考网上各位大抄的大作如下:

 

----------------------------------------------------------------------------------------------------------------------------------------------

如先输入mysql,成功启动后输入use mysql,出现如下错误:Access denied for user ''@'localhost' to database 'mysql' 
还有,输mysql可以,输mysql -u root就出错了:
Access denied for user 'root'@'localhost' (using password: NO).
The reason is:
是昨日更新ROOT密码时出错
update user set password = '123456' where user ="root" //这样写是错的,密码其实不是123456
应该为update user set password = password ('123456') where user = "root"
具体操作步骤:
关闭mysql:
# service mysqld stop
然后:
# mysqld_safe --skip-grant-tables
启动mysql:
# service mysqld start
mysql -u root
mysql> use mysql
mysql> UPDATE user SET Password=PASSWORD('xxx') WHERE user='root';
mysql> flush privileges;
mysql>\q

---------------------------------------------------------------------------------------------------------------------------------------------------------

按照各位大抄的步骤一步一步做,没有任何效果,仔细研究错误信息中的 user ''@'localhost' ,说明user表中的user=‘’的信息有误,然后执行UPDATE user SET Password=PASSWORD('') WHERE user='';

果断没有作用。

最后不允许匿名登录,执行以下语句

mysql> delete from user where user='';

mysql > flush privileges;

mysql> \q

# service mysqld restart

问题解决。

------------------------------

在此忠告各位搞IT的同仁,你们有点专业精神好不?每个问题都搞清楚什么意思好不?别抄来抄去有意思么?都中国IT人的脸,中国的IT行业都毁在你们这群人手里了。

posted @ 2013-06-27 14:02 yunlong3727 阅读(2) 评论(0) 编辑
进程间通讯- 信号量函数 semget() semop() semctl()
semget()


     可以使用系统调用semget()创建一个新的信号量集,或者存取一个已经存在的信号量集:

系统调用:semget();
原型:intsemget(key_t key,int nsems,int semflg);
返回值:如果成功,则返回信号量集的IPC标识符。如果失败,则返回-1:errno=EACCESS(没有权限)
EEXIST(信号量集已经存在,无法创建)
EIDRM(信号量集已经删除)
ENOENT(信号量集不存在,同时没有使用IPC_CREAT)
ENOMEM(没有足够的内存创建新的信号量集)
ENOSPC(超出限制)
    系统调用semget()的第一个参数是关键字值(一般是由系统调用ftok()返回的)。系统内核将此值和系统中存在的其他的信号量集的关键字值进行比较。打开和存取操作与参数semflg中的内容相关。IPC_CREAT如果信号量集在系统内核中不存在,则创建信号量集。IPC_EXCL当和 IPC_CREAT一同使用时,如果信号量集已经存在,则调用失败。如果单独使用IPC_CREAT,则semget()要么返回新创建的信号量集的标识符,要么返回系统中已经存在的同样的关键字值的信号量的标识符。如果IPC_EXCL和IPC_CREAT一同使用,则要么返回新创建的信号量集的标识符,要么返回-1。IPC_EXCL单独使用没有意义。参数nsems指出了一个新的信号量集中应该创建的信号量的个数。信号量集中最多的信号量的个数是在linux/sem.h中定义的:
#defineSEMMSL32/*<=512maxnumofsemaphoresperid*/
下面是一个打开和创建信号量集的程序:
intopen_semaphore_set(key_t keyval,int numsems)
{
intsid;
if(!numsems)
return(-1);
if((sid=semget(mykey,numsems,IPC_CREAT|0660))==-1)
{
return(-1);
}
return(sid);
}
};
==============================================================
semop()


系统调用:semop();
调用原型:int semop(int semid,struct sembuf*sops,unsign ednsops);
返回值:0,如果成功。-1,如果失败:errno=E2BIG(nsops大于最大的ops数目)
EACCESS(权限不够)
EAGAIN(使用了IPC_NOWAIT,但操作不能继续进行)
EFAULT(sops指向的地址无效)
EIDRM(信号量集已经删除)
EINTR(当睡眠时接收到其他信号)
EINVAL(信号量集不存在,或者semid无效)
ENOMEM(使用了SEM_UNDO,但无足够的内存创建所需的数据结构)
ERANGE(信号量值超出范围)

    第一个参数是关键字值。第二个参数是指向将要操作的数组的指针。第三个参数是数组中的操作的个数。参数sops指向由sembuf组成的数组。此数组是在linux/sem.h中定义的:
/*semop systemcall takes an array of these*/
structsembuf{
ushortsem_num;/*semaphore index in array*/
shortsem_op;/*semaphore operation*/
shortsem_flg;/*operation flags*/
sem_num将要处理的信号量的个数。
sem_op要执行的操作。
sem_flg操作标志。
    如果sem_op是负数,那么信号量将减去它的值。这和信号量控制的资源有关。如果没有使用IPC_NOWAIT,那么调用进程将进入睡眠状态,直到信号量控制的资源可以使用为止。如果sem_op是正数,则信号量加上它的值。这也就是进程释放信号量控制的资源。最后,如果sem_op是0,那么调用进程将调用sleep(),直到信号量的值为0。这在一个进程等待完全空闲的资源时使用。
===============================================================
semctl()


系统调用:semctl();
原型:int semctl(int semid,int semnum,int cmd,union semunarg);
返回值:如果成功,则为一个正数。
如果失败,则为-1:errno=EACCESS(权限不够)
EFAULT(arg指向的地址无效)
EIDRM(信号量集已经删除)
EINVAL(信号量集不存在,或者semid无效)
EPERM(EUID没有cmd的权利)
ERANGE(信号量值超出范围)

    系统调用semctl用来执行在信号量集上的控制操作。这和在消息队列中的系统调用msgctl是十分相似的。但这两个系统调用的参数略有不同。因为信号量一般是作为一个信号量集使用的,而不是一个单独的信号量。所以在信号量集的操作中,不但要知道IPC关键字值,也要知道信号量集中的具体的信号量。这两个系统调用都使用了参数cmd,它用来指出要操作的具体命令。两个系统调用中的最后一个参数也不一样。在系统调用msgctl中,最后一个参数是指向内核中使用的数据结构的指针。我们使用此数据结构来取得有关消息队列的一些信息,以及设置或者改变队列的存取权限和使用者。但在信号量中支持额外的可选的命令,这样就要求有一个更为复杂的数据结构。
系统调用semctl()的第一个参数是关键字值。第二个参数是信号量数目。
    参数cmd中可以使用的命令如下:
    ·IPC_STAT读取一个信号量集的数据结构semid_ds,并将其存储在semun中的buf参数中。
    ·IPC_SET设置信号量集的数据结构semid_ds中的元素ipc_perm,其值取自semun中的buf参数。
    ·IPC_RMID将信号量集从内存中删除。
    ·GETALL用于读取信号量集中的所有信号量的值。
    ·GETNCNT返回正在等待资源的进程数目。
    ·GETPID返回最后一个执行semop操作的进程的PID。
    ·GETVAL返回信号量集中的一个单个的信号量的值。
    ·GETZCNT返回这在等待完全空闲的资源的进程数目。
    ·SETALL设置信号量集中的所有的信号量的值。
    ·SETVAL设置信号量集中的一个单独的信号量的值。
    参数arg代表一个semun的实例。semun是在linux/sem.h中定义的:
/*arg for semctl systemcalls.*/
unionsemun{
intval;/*value for SETVAL*/
structsemid_ds*buf;/*buffer for IPC_STAT&IPC_SET*/
ushort*array;/*array for GETALL&SETALL*/
structseminfo*__buf;/*buffer for IPC_INFO*/
void*__pad;
    val当执行SETVAL命令时使用。buf在IPC_STAT/IPC_SET命令中使用。代表了内核中使用的信号量的数据结构。array在使用GETALL/SETALL命令时使用的指针。
    下面的程序返回信号量的值。当使用GETVAL命令时,调用中的最后一个参数被忽略:
intget_sem_val(intsid,intsemnum)
{
return(semctl(sid,semnum,GETVAL,0));
}
    下面是一个实际应用的例子:
#defineMAX_PRINTERS5
printer_usage()
{
int x;
for(x=0;x<MAX_PRINTERS;x++)
printf("Printer%d:%d\n\r",x,get_sem_val(sid,x));
}
    下面的程序可以用来初始化一个新的信号量值:
void init_semaphore(int sid,int semnum,int initval)
{
union semunsemopts;
semopts.val=initval;
semctl(sid,semnum,SETVAL,semopts);
}
    注意系统调用semctl中的最后一个参数是一个联合类型的副本,而不是一个指向联合类型的指针。

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/shm.h>

#define KEY1 1492
#define KEY2 1493
#define KEY3 1494
#define IFLAGS (IPC_CREAT|IPC_EXCL)
#define N 1 

#define SEMKEY1 (key_t)0x2000
#define SEMKEY2 (key_t)0x2001
#define SEMKEY3 (key_t)0x2002

union semun{
    int val;
    struct semid_ds *buf;
    unsigned short * ary;
};

int ctr_sem(key_t key,int inival)
{
    union semun argument;
    int id;
    //if ((id=semget(key,1,IPC_CREAT))<0)
    if ((id=semget(key,1,IPC_CREAT))<0)
    {
        printf("semget error\n");
    } 
    argument.val=inival;
    if (semctl(id,0,SETVAL,argument)<0)
    {
        printf("semctrl error\n");
    }
    return id;
}

int sem_init(key_t key, int inival)
{
  int semid; 
  union semun arg;
  semid=semget(key,1,0660|IFLAGS);
  arg.val=inival;
  semctl(semid, 0, SETVAL, arg);
  return semid;
}

void P(int semid)
{
    struct sembuf sb; 
    sb.sem_num=0;
    sb.sem_op=-1;
    sb.sem_flg=0;
    semop(semid,&sb,1);
}

void V(int semid)
{
    struct sembuf sb; 
    sb.sem_num=0;
    sb.sem_op=1;
    sb.sem_flg=0;
    semop(semid,&sb,1); 
}

int productItem()
{
    static int i=1;
    printf("Produce a product %d\n",i);
    return i++;
}

void consumeItem(int item)
{
    printf("Consume a product %d\n",item);
}

int main(void)
{
    int nshm=shmget(ftok("/root",'a'),1024,IPC_CREAT);
    int *buffer=(int *)shmat(nshm,0,0);

//     int products=ctr_sem(KEY1/*ftok("/home/jingenl",'p')*/,0);
//     int space=ctr_sem(KEY2/*ftok("/home/jingenl",'s')*/,N);
//     int mutex=ctr_sem(KEY3/*ftok("/home/jingenl",'m')*/,1);

    int products=sem_init(SEMKEY1,0);
    int space=sem_init(SEMKEY2,N);
    int mutex=sem_init(SEMKEY3,1);

    int i=0,j=0;

    if(fork()==0)
    {
          int item;
          while(1)
          {
                P(space);
                P(mutex);
                item=productItem();
                *(buffer + sizeof(int)*i)=item;
                i=(i+1)%N;
                V(mutex);
                V(products);
          }
    }
    else
    {
          int item;
          while(1)
          {
                P(products);
                P(mutex);
                item=*(buffer + sizeof(int)*j);
                j=(j+1)%N;
                consumeItem(item);
                V(mutex);
                V(space);
          }
                    
    }
    return 0;
}
 
来源:http://blog.chinaunix.net/uid-23193900-id-3221978.html
posted @ 2013-06-06 14:54 yunlong3727 阅读(3) 评论(0) 编辑
浅析Linux下core文件

来源:http://cooldatabase.javaeye.com/blog/637513

当我们的程序崩溃时,内核有可能把该程序当前内存映射到core文件里,方便程序员找到程序出现问题的地方。最常出现的,几乎所有C程序员都出现过的错误就是“段错误”了。也是最难查出问题原因的一个错误。下面我们就针对“段错误”来分析core文件的产生、以及我们如何利用core文件找到出现崩溃的地方。 

何谓core文件 

当一个程序崩溃时,在进程当前工作目录的core文件中复制了该进程的存储图像。core文件仅仅是一个内存映象(同时加上调试信息),主要是用来调试的。 

当程序接收到以下UNIX信号会产生core文件: 

名字 
说明 
ANSI C  POSIX.1 
SVR4  4.3+BSD 
缺省动作 

SIGABRT 
异常终止(abort) 
   .       . 
   .      . 
终止w/core 

SIGBUS 
硬件故障 
           . 
   .      . 
终止w/core 

SIGEMT 
硬件故障 
  
   .      . 
终止w/core 

SIGFPE 
算术异常 
   .       . 
   .      . 
终止w/core 

SIGILL 
非法硬件指令 
   .       . 
   .      . 
终止w/core 

SIGIOT 
硬件故障 
  
   .      . 
终止w/core 

SIGQUIT 
终端退出符 
           . 
   .      . 
终止w/core 

SIGSEGV 
无效存储访问 
   .       . 
   .      . 
终止w/core 

SIGSYS 
无效系统调用 
  
   .      . 
终止w/core 

SIGTRAP 
硬件故障 
  
   .      . 
终止w/core 

SIGXCPU 
超过CPU限制(setrlimit) 
  
   .      . 
终止w/core 

SIGXFSZ 
超过文件长度限制(setrlimit) 
  
   .      . 
终止w/core 


在系统默认动作列,“终止w/core”表示在进程当前工作目录的core文件中复制了该进程的存储图像(该文件名为core,由此可以看出这种功能很久之前就是UNIX功能的一部分)。大多数UNIX调试程序都使用core文件以检查进程在终止时的状态。 

core文件的产生不是POSIX.1所属部分,而是很多UNIX版本的实现特征。UNIX第6版没有检查条件 (a)和(b),并且其源代码中包含如下说明:“如果你正在找寻保护信号,那么当设置-用户-ID命令执行时,将可能产生大量的这种信号”。4.3 + BSD产生名为core.prog的文件,其中prog是被执行的程序名的前1 6个字符。它对core文件给予了某种标识,所以是一种改进特征。 

表中“硬件故障”对应于实现定义的硬件故障。这些名字中有很多取自UNIX早先在DP-11上的实现。请查看你所使用的系统的手册,以确切地确定这些信号对应于哪些错误类型。 

下面比较详细地说明这些信号。 

• SIGABRT 调用abort函数时产生此信号。进程异常终止。 

• SIGBUS  指示一个实现定义的硬件故障。 

• SIGEMT  指示一个实现定义的硬件故障。 

EMT这一名字来自PDP-11的emulator trap 指令。 

• SIGFPE  此信号表示一个算术运算异常,例如除以0,浮点溢出等。 

• SIGILL  此信号指示进程已执行一条非法硬件指令。 

4.3BSD由abort函数产生此信号。SIGABRT现在被用于此。 

• SIGIOT  这指示一个实现定义的硬件故障。 

IOT这个名字来自于PDP-11对于输入/输出TRAP(input/output TRAP)指令的缩写。系统V的早期版本,由abort函数产生此信号。SIGABRT现在被用于此。 

• SIGQUIT 当用户在终端上按退出键(一般采用Ctrl-\)时,产生此信号,并送至前台进 

程组中的所有进程。此信号不仅终止前台进程组(如SIGINT所做的那样),同时产生一个core文件。 

• SIGSEGV 指示进程进行了一次无效的存储访问。 

名字SEGV表示“段违例(segmentation violation)”。 

• SIGSYS  指示一个无效的系统调用。由于某种未知原因,进程执行了一条系统调用指令, 

但其指示系统调用类型的参数却是无效的。 

• SIGTRAP 指示一个实现定义的硬件故障。 

此信号名来自于PDP-11的TRAP指令。 

• SIGXCPU SVR4和4.3+BSD支持资源限制的概念。如果进程超过了其软C P U时间限制,则产生此信号。 

• SIGXFSZ 如果进程超过了其软文件长度限制,则SVR4和4.3+BSD产生此信号。 

摘自《UNIX环境高级编程》第10章 信号。 



使用core文件调试程序 

看下面的例子: 

/*core_dump_test.c*/ 
#include <stdio.h> 
const char *str = "test"; 
void core_test(){ 
    str[1] = 'T'; 


int main(){ 
    core_test(); 
    return 0; 


编译: 
gcc –g core_dump_test.c -o core_dump_test 

如果需要调试程序的话,使用gcc编译时加上-g选项,这样调试core文件的时候比较容易找到错误的地方。 

执行: 
./core_dump_test 
段错误 

运行core_dump_test程序出现了“段错误”,但没有产生core文件。这是因为系统默认core文件的大小为0,所以没有创建。可以用ulimit命令查看和修改core文件的大小。 
ulimit -c 0 
ulimit -c 1000 
ulimit -c 1000 

-c 指定修改core文件的大小,1000指定了core文件大小。也可以对core文件的大小不做限制,如: 

ulimit -c unlimited 
ulimit -c unlimited 

如果想让修改永久生效,则需要修改配置文件,如 .bash_profile、/etc/profile或/etc/security/limits.conf。 

再次执行: 
./core_dump_test 
段错误 (core dumped) 
ls core.* 
core.6133 

可以看到已经创建了一个core.6133的文件.6133是core_dump_test程序运行的进程ID。 

调式core文件 
core文件是个二进制文件,需要用相应的工具来分析程序崩溃时的内存映像。 

file core.6133 

core.6133: ELF 32-bit LSB core file Intel 80386, version 1 (SYSV), SVR4-style, from 'core_dump_test' 

在Linux下可以用GDB来调试core文件。 

gdb core_dump_test core.6133 

GNU gdb Red Hat Linux (5.3post-0.20021129.18rh) 
Copyright 2003 Free Software Foundation, Inc. 
GDB is free software, covered by the GNU General Public License, and you are 
welcome to change it and/or distribute copies of it under certain conditions. 
Type "show copying" to see the conditions. 
There is absolutely no warranty for GDB.  Type "show warranty" for details. 
This GDB was configured as "i386-redhat-linux-gnu"... 
Core was generated by `./core_dump_test'. 
Program terminated with signal 11, Segmentation fault. 
Reading symbols from /lib/tls/libc.so.6...done. 
Loaded symbols for /lib/tls/libc.so.6 
Reading symbols from /lib/ld-linux.so.2...done. 
Loaded symbols for /lib/ld-linux.so.2 
#0  0x080482fd in core_test () at core_dump_test.c:7 
7           str[1] = 'T'; 
(gdb) where 
#0  0x080482fd in core_test () at core_dump_test.c:7 
#1  0x08048317 in main () at core_dump_test.c:12 
#2  0x42015574 in __libc_start_main () from /lib/tls/libc.so.6 

GDB中键入where,就会看到程序崩溃时堆栈信息(当前函数之前的所有已调用函数的列表(包括当前函数),gdb只显示最近几个),我们很容易找到我们的程序在最后崩溃的时候调用了core_dump_test.c 第7行的代码,导致程序崩溃。注意:在编译程序的时候要加入选项-g。您也可以试试其他命令, 如 fram、list等。更详细的用法,请查阅GDB文档。 

core文件创建在什么位置 

在进程当前工作目录的下创建。通常与程序在相同的路径下。但如果程序中调用了chdir函数,则有可能改变了当前工作目录。这时core文件创建在chdir指定的路径下。有好多程序崩溃了,我们却找不到core文件放在什么位置。和chdir函数就有关系。当然程序崩溃了不一定都产生core文件。 

什么时候不产生core文件 

在下列条件下不产生core文件: 
( a )进程是设置-用户-ID,而且当前用户并非程序文件的所有者; 
( b )进程是设置-组-ID,而且当前用户并非该程序文件的组所有者; 
( c )用户没有写当前工作目录的许可权; 
( d )文件太大。core文件的许可权(假定该文件在此之前并不存在)通常是用户读/写,组读和其他读。 

利用GDB调试core文件,当遇到程序崩溃时我们不再束手无策。 

posted @ 2013-06-03 14:57 yunlong3727 阅读(3) 评论(0) 编辑
linux的线程同步机制

转载于 http://www.52rd.com/Blog/Detail_RD.Blog_renjwjx_17460.html

互斥锁

尽管在Posix Thread中同样可以使用IPC的信号量机制来实现互斥锁mutex功能,但显然semphore的功能过于强大了,在Posix Thread中定义了另外一套专门用于线程同步的mutex函数。

1. 创建和销毁

有两种方法创建互斥锁,静态方式和动态方式。POSIX定义了一个宏PTHREAD_MUTEX_INITIALIZER来静态初始化互斥锁,方法如下: pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER; 在LinuxThreads实现中,pthread_mutex_t是一个结构,而PTHREAD_MUTEX_INITIALIZER则是一个结构常量。

动态方式是采用pthread_mutex_init()函数来初始化互斥锁,API定义如下: int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr) 其中mutexattr用于指定互斥锁属性(见下),如果为NULL则使用缺省属性。

pthread_mutex_destroy()用于注销一个互斥锁,API定义如下: int pthread_mutex_destroy(pthread_mutex_t *mutex) 销毁一个互斥锁即意味着释放它所占用的资源,且要求锁当前处于开放状态。由于在Linux中,互斥锁并不占用任何资源,因此LinuxThreads中的pthread_mutex_destroy()除了检查锁状态以外(锁定状态则返回EBUSY)没有其他动作。

2. 互斥锁属性

互斥锁的属性在创建锁的时候指定,在LinuxThreads实现中仅有一个锁类型属性,不同的锁类型在试图对一个已经被锁定的互斥锁加锁时表现不同。当前(glibc2.2.3,linuxthreads0.9)有四个值可供选择:

  • PTHREAD_MUTEX_TIMED_NP,这是缺省值,也就是普通锁。当一个线程加锁以后,其余请求锁的线程将形成一个等待队列,并在解锁后按优先级获得锁。这种锁策略保证了资源分配的公平性。
  • PTHREAD_MUTEX_RECURSIVE_NP,嵌套锁,允许同一个线程对同一个锁成功获得多次,并通过多次unlock解锁。如果是不同线程请求,则在加锁线程解锁时重新竞争。
  • PTHREAD_MUTEX_ERRORCHECK_NP,检错锁,如果同一个线程请求同一个锁,则返回EDEADLK,否则与PTHREAD_MUTEX_TIMED_NP类型动作相同。这样就保证当不允许多次加锁时不会出现最简单情况下的死锁。
  • PTHREAD_MUTEX_ADAPTIVE_NP,适应锁,动作最简单的锁类型,仅等待解锁后重新竞争。

3. 锁操作

锁操作主要包括加锁pthread_mutex_lock()、解锁pthread_mutex_unlock()和测试加锁pthread_mutex_trylock()三个,不论哪种类型的锁,都不可能被两个不同的线程同时得到,而必须等待解锁。对于普通锁和适应锁类型,解锁者可以是同进程内任何线程;而检错锁则必须由加锁者解锁才有效,否则返回EPERM;对于嵌套锁,文档和实现要求必须由加锁者解锁,但实验结果表明并没有这种限制,这个不同目前还没有得到解释。在同一进程中的线程,如果加锁后没有解锁,则任何其他线程都无法再获得锁。

 

int pthread_mutex_lock(pthread_mutex_t *mutex)int pthread_mutex_unlock(pthread_mutex_t *mutex)int pthread_mutex_trylock(pthread_mutex_t *mutex)

 

 

pthread_mutex_trylock()语义与pthread_mutex_lock()类似,不同的是在锁已经被占据时返回EBUSY而不是挂起等待。

4. 其他

POSIX线程锁机制的Linux实现都不是取消点,因此,延迟取消类型的线程不会因收到取消信号而离开加锁等待。值得注意的是,如果线程在加锁后解锁前被取消,锁将永远保持锁定状态,因此如果在关键区段内有取消点存在,或者设置了异步取消类型,则必须在退出回调函数中解锁。

这个锁机制同时也不是异步信号安全的,也就是说,不应该在信号处理过程中使用互斥锁,否则容易造成死锁。


 

条件变量

条件变量是利用线程间共享的全局变量进行同步的一种机制,主要包括两个动作:一个线程等待"条件变量的条件成立"而挂起;另一个线程使"条件成立"(给出条件成立信号)。为了防止竞争,条件变量的使用总是和一个互斥锁结合在一起。

1. 创建和注销

条件变量和互斥锁一样,都有静态动态两种创建方式,静态方式使用PTHREAD_COND_INITIALIZER常量,如下: 
pthread_cond_t cond=PTHREAD_COND_INITIALIZER

动态方式调用pthread_cond_init()函数,API定义如下: 
int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr)

尽管POSIX标准中为条件变量定义了属性,但在LinuxThreads中没有实现,因此cond_attr值通常为NULL,且被忽略。

注销一个条件变量需要调用pthread_cond_destroy(),只有在没有线程在该条件变量上等待的时候才能注销这个条件变量,否则返回EBUSY。因为Linux实现的条件变量没有分配什么资源,所以注销动作只包括检查是否有等待线程。API定义如下: 
int pthread_cond_destroy(pthread_cond_t *cond)

2. 等待和激发

 

int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime)

 

 

等待条件有两种方式:无条件等待pthread_cond_wait()和计时等待pthread_cond_timedwait(),其中计时等待方式如果在给定时刻前条件没有满足,则返回ETIMEOUT,结束等待,其中abstime以与time()系统调用相同意义的绝对时间形式出现,0表示格林尼治时间1970年1月1日0时0分0秒。

无论哪种等待方式,都必须和一个互斥锁配合,以防止多个线程同时请求pthread_cond_wait()(或pthread_cond_timedwait(),下同)的竞争条件(Race Condition)。mutex互斥锁必须是普通锁(PTHREAD_MUTEX_TIMED_NP)或者适应锁(PTHREAD_MUTEX_ADAPTIVE_NP),且在调用pthread_cond_wait()前必须由本线程加锁(pthread_mutex_lock()),而在更新条件等待队列以前,mutex保持锁定状态,并在线程挂起进入等待前解锁。在条件满足从而离开pthread_cond_wait()之前,mutex将被重新加锁,以与进入pthread_cond_wait()前的加锁动作对应。

激发条件有两种形式,pthread_cond_signal()激活一个等待该条件的线程,存在多个等待线程时按入队顺序激活其中一个;而pthread_cond_broadcast()则激活所有等待线程。

3. 其他

pthread_cond_wait()和pthread_cond_timedwait()都被实现为取消点,因此,在该处等待的线程将立即重新运行,在重新锁定mutex后离开pthread_cond_wait(),然后执行取消动作。也就是说如果pthread_cond_wait()被取消,mutex是保持锁定状态的,因而需要定义退出回调函数来为其解锁。

以下示例集中演示了互斥锁和条件变量的结合使用,以及取消对于条件等待动作的影响。在例子中,有两个线程被启动,并等待同一个条件变量,如果不使用退出回调函数(见范例中的注释部分),则tid2将在pthread_mutex_lock()处永久等待。如果使用回调函数,则tid2的条件等待及主线程的条件激发都能正常工作。

 

#include <stdio.h>#include <pthread.h>#include <unistd.h>pthread_mutex_t mutex;pthread_cond_t  cond;void * child1(void *arg){        pthread_cleanup_push(pthread_mutex_unlock,&mutex);  /* comment 1 */        while(1){                printf("thread 1 get running \n");        printf("thread 1 pthread_mutex_lock returns %d\n",pthread_mutex_lock(&mutex));        pthread_cond_wait(&cond,&mutex);                    printf("thread 1 condition applied\n");        pthread_mutex_unlock(&mutex);                    sleep(5);    }        pthread_cleanup_pop(0);     /* comment 2 */}void *child2(void *arg){        while(1){                sleep(3);               /* comment 3 */                printf("thread 2 get running.\n");        printf("thread 2 pthread_mutex_lock returns %d\n",pthread_mutex_lock(&mutex));        pthread_cond_wait(&cond,&mutex);        printf("thread 2 condition applied\n");        pthread_mutex_unlock(&mutex);        sleep(1);        }}int main(void){        int tid1,tid2;        printf("hello, condition variable test\n");        pthread_mutex_init(&mutex,NULL);        pthread_cond_init(&cond,NULL);        pthread_create(&tid1,NULL,child1,NULL);        pthread_create(&tid2,NULL,child2,NULL);        do{        sleep(2);                   /* comment 4 */                pthread_cancel(tid1);       /* comment 5 */                sleep(2);                   /* comment 6 */        pthread_cond_signal(&cond);    }while(1);          sleep(100);        pthread_exit(0);}

 

 

如果不做注释5的pthread_cancel()动作,即使没有那些sleep()延时操作,child1和child2都能正常工作。注释3和注释4的延迟使得child1有时间完成取消动作,从而使child2能在child1退出之后进入请求锁操作。如果没有注释1和注释2的回调函数定义,系统将挂起在child2请求锁的地方;而如果同时也不做注释3和注释4的延时,child2能在child1完成取消动作以前得到控制,从而顺利执行申请锁的操作,但却可能挂起在pthread_cond_wait()中,因为其中也有申请mutex的操作。child1函数给出的是标准的条件变量的使用方式:回调函数保护,等待条件前锁定,pthread_cond_wait()返回后解锁。

条件变量机制不是异步信号安全的,也就是说,在信号处理函数中调用pthread_cond_signal()或者pthread_cond_broadcast()很可能引起死锁。


 

信号灯

信号灯与互斥锁和条件变量的主要不同在于"灯"的概念,灯亮则意味着资源可用,灯灭则意味着不可用。如果说后两中同步方式侧重于"等待"操作,即资源不可用的话,信号灯机制则侧重于点灯,即告知资源可用;没有等待线程的解锁或激发条件都是没有意义的,而没有等待灯亮的线程的点灯操作则有效,且能保持灯亮状态。当然,这样的操作原语也意味着更多的开销。

信号灯的应用除了灯亮/灯灭这种二元灯以外,也可以采用大于1的灯数,以表示资源数大于1,这时可以称之为多元灯。

1. 创建和注销

POSIX信号灯标准定义了有名信号灯和无名信号灯两种,但LinuxThreads的实现仅有无名灯,同时有名灯除了总是可用于多进程之间以外,在使用上与无名灯并没有很大的区别,因此下面仅就无名灯进行讨论。

int sem_init(sem_t *sem, int pshared, unsigned int value) 
这是创建信号灯的API,其中value为信号灯的初值,pshared表示是否为多进程共享而不仅仅是用于一个进程。LinuxThreads没有实现多进程共享信号灯,因此所有非0值的pshared输入都将使sem_init()返回-1,且置errno为ENOSYS。初始化好的信号灯由sem变量表征,用于以下点灯、灭灯操作。

int sem_destroy(sem_t * sem) 
被注销的信号灯sem要求已没有线程在等待该信号灯,否则返回-1,且置errno为EBUSY。除此之外,LinuxThreads的信号灯注销函数不做其他动作。

2. 点灯和灭灯

 

int sem_post(sem_t * sem)


点灯操作将信号灯值原子地加1,表示增加一个可访问的资源。

 

 

int sem_wait(sem_t * sem)int sem_trywait(sem_t * sem)


sem_wait()为等待灯亮操作,等待灯亮(信号灯值大于0),然后将信号灯原子地减1,并返回。sem_trywait()为sem_wait()的非阻塞版,如果信号灯计数大于0,则原子地减1并返回0,否则立即返回-1,errno置为EAGAIN。

 

3. 获取灯值

 

int sem_getvalue(sem_t * sem, int * sval)


读取sem中的灯计数,存于*sval中,并返回0。

 

4. 其他

sem_wait()被实现为取消点,而且在支持原子"比较且交换"指令的体系结构上,sem_post()是唯一能用于异步信号处理函数的POSIX异步信号安全的API。


 

异步信号

由于LinuxThreads是在核外使用核内轻量级进程实现的线程,所以基于内核的异步信号操作对于线程也是有效的。但同时,由于异步信号总是实际发往某个进程,所以无法实现POSIX标准所要求的"信号到达某个进程,然后再由该进程将信号分发到所有没有阻塞该信号的线程中"原语,而是只能影响到其中一个线程。

POSIX异步信号同时也是一个标准C库提供的功能,主要包括信号集管理(sigemptyset()、sigfillset()、sigaddset()、sigdelset()、sigismember()等)、信号处理函数安装(sigaction())、信号阻塞控制(sigprocmask())、被阻塞信号查询(sigpending())、信号等待(sigsuspend())等,它们与发送信号的kill()等函数配合就能实现进程间异步信号功能。LinuxThreads围绕线程封装了sigaction()何raise(),本节集中讨论LinuxThreads中扩展的异步信号函数,包括pthread_sigmask()、pthread_kill()和sigwait()三个函数。毫无疑问,所有POSIX异步信号函数对于线程都是可用的。

int pthread_sigmask(int how, const sigset_t *newmask, sigset_t *oldmask) 
设置线程的信号屏蔽码,语义与sigprocmask()相同,但对不允许屏蔽的Cancel信号和不允许响应的Restart信号进行了保护。被屏蔽的信号保存在信号队列中,可由sigpending()函数取出。

int pthread_kill(pthread_t thread, int signo) 
向thread号线程发送signo信号。实现中在通过thread线程号定位到对应进程号以后使用kill()系统调用完成发送。

int sigwait(const sigset_t *set, int *sig) 
挂起线程,等待set中指定的信号之一到达,并将到达的信号存入*sig中。POSIX标准建议在调用sigwait()等待信号以前,进程中所有线程都应屏蔽该信号,以保证仅有sigwait()的调用者获得该信号,因此,对于需要等待同步的异步信号,总是应该在创建任何线程以前调用pthread_sigmask()屏蔽该信号的处理。而且,调用sigwait()期间,原来附接在该信号上的信号处理函数不会被调用。

如果在等待期间接收到Cancel信号,则立即退出等待,也就是说sigwait()被实现为取消点。


 

其他同步方式

除了上述讨论的同步方式以外,其他很多进程间通信手段对于LinuxThreads也是可用的,比如基于文件系统的IPC(管道、Unix域Socket等)、消息队列(Sys.V或者Posix的)、System V的信号灯等。只有一点需要注意,LinuxThreads在核内是作为共享存储区、共享文件系统属性、共享信号处理、共享文件描述符的独立进程看待的。

posted @ 2013-05-25 13:26 yunlong3727 阅读(4) 评论(0) 编辑
.hpp文件和.h文件的区别
 hpp,顾名思义等于.h加上.cpp,在boost、Xerces等开源库中频繁出现,偶在机缘巧合之下,学得一招半式,遂记录如下,以供参考学习。
    hpp,其实质就是将.cpp的实现代码混入.h头文件当中,定义与实现都包含在同一文件,则该类的调用者只需要include该hpp文件即可,无需再将cpp加入到project中进行编译。而实现代码将直接编译到调用者的obj文件中,不再生成单独的obj,采用hpp将大幅度减少调用project中的cpp文件数与编译次数,也不用再发布烦人的lib与dll,因此非常适合用来编写公用的开源库。
    hpp的优点不少,但是编写中有以下几点要注意:
   
    1、不可包含全局对象和全局函数。
    由于hpp本质上是作为.h被调用者include,所以当hpp文件中存在全局对象或者全局函数,而该hpp被多个调用者include时,将在链接时导致符号重定义错误。要避免这种情况,需要去除全局对象,将全局函数封装为类的静态方法。
 
    2、类之间不可循环调用。
    在.h和.cpp的场景中,当两个类或者多个类之间有循环调用关系时,只要预先在头文件做被调用类的声明即可,如下:
    class B;
    class A{
    public:
         void someMethod(B b);
    };
    class B{
    public:
         void someMethod(A a);
    };
    在hpp场景中,由于定义与实现都已经存在于一个文件,调用者必需明确知道被调用者的所有定义,而不能等到cpp中去编译。因此hpp中必须整理类之间调用关系,不可产生循环调用。同理,对于当两个类A和B分别定义在各自的hpp文件中,形如以下的循环调用也将导致编译错误:
    //a.hpp
    #include "b.hpp"
    class A{
    public:
        void someMethod(B b);
    };
 
    //b.hpp
    #include "a.hpp"
    class B{
    public:
        void someMethod(A a);
    };
 
    3、不可使用静态成员。
    静态成员的使用限制在于如果类含有静态成员,则在hpp中必需加入静态成员初始化代码,当该hpp被多个文档include时,将产生符号重定义错误。唯一的例外是const static整型成员,因为在vs2003中,该类型允许在定义时初始化,如:
    class A{
    public:
       const static int intValue = 123;
    };
    由于静态成员的使用是很常见的场景,无法强制清除,因此可以考虑以下几种方式(以下示例均为同一类中方法)
    1.类中仅有一个静态成员时,且仅有一个调用者时,可以通过局域静态变量模拟
    //方法模拟获取静态成员
    someType getMember()
    {
       static someType value(xxx);//作用域内静态变量
       return value;
    }
    2.类中有多个方法需要调用静态成员,而且可能存在多个静态成员时,可以将每个静态成员封装一个模拟方法,供其他方法调用。
 
    someType getMemberA()
    {
       static someType value(xxx);//作用域内静态变量
       return value;
    }
    someType getMemberB()
    {
       static someType value(xxx);//作用域内静态变量
       return value;
    }
   void accessMemberA()
    {
       someType member = getMemberA();//获取静态成员
    };
    //获取两个静态成员
    void accessStaticMember()
    {
       someType a = getMemberA();//获取静态成员
       someType b = getMemberB();
    };
 
    3.第二种方法对于大部分情况是通用的,但是当所需的静态成员过多时,编写封装方法的工作量将非常巨大,在此种情况下,建议使用Singleton模式,将被调用类定义成普通类,然后使用Singleton将其变为全局唯一的对象进行调用。
    如原h+cpp下的定义如下:
    class A{
    public:
        type getMember(){
           return member;
        }
        static type member;//静态成员
    }
 
    采用singleton方式,实现代码可能如下(singleton实现请自行查阅相关文档)
    //实际实现类
    class Aprovider{
    public:
        type getMember(){
           return member;
        }
        type member;//变为普通成员
    }
 
    //提供给调用者的接口类
    class A{
    public:
        type getMember(){
           return Singleton<AProvider>::getInstance()->getMember();
        }
    }
 
转自http://blog.csdn.net/akumas/archive/2006/10/31/1357774.aspx
posted @ 2013-05-25 09:20 yunlong3727 阅读(3) 评论(0) 编辑
【转】struct epoll_event

转载:http://hi.baidu.com/jingweiyoung/item/ae9fc81714be67dbbf9042b9

这两天在看项目的数据结构定义及关系,遇到一些关于socket的知识点,还有一些C++的知识点,下面总结下:

1. struct epoll_event

   结构体epoll_event被用于注册所感兴趣的事件和回传所发生待处理的事件,定义如下:

    typedef union epoll_data {
        void *ptr;
         int fd;
         __uint32_t u32;
         __uint64_t u64;
     } epoll_data_t;//保存触发事件的某个文件描述符相关的数据

     struct epoll_event {
         __uint32_t events;      /* epoll event */
         epoll_data_t data;      /* User data variable */
     };

   其中events表示感兴趣的事件和被触发的事件,可能的取值为:
   EPOLLIN:表示对应的文件描述符可以读;
   EPOLLOUT:表示对应的文件描述符可以写;
   EPOLLPRI:表示对应的文件描述符有紧急的数可读;

   EPOLLERR:表示对应的文件描述符发生错误;
   EPOLLHUP:表示对应的文件描述符被挂断;
   EPOLLET:    ET的epoll工作模式;

  

 所涉及到的函数有:

1、epoll_create函数
    函数声明:int epoll_create(int size)
   
   功能:该函数生成一个epoll专用的文件描述符,其中的参数是指定生成描述符的最大范围;


2、epoll_ctl函数
    函数声明:int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)
   
    功能:用于控制某个文件描述符上的事件,可以注册事件,修改事件,删除事件。
   
    @epfd:由epoll_create生成的epoll专用的文件描述符;
   
     @op:要进行的操作,EPOLL_CTL_ADD注册、EPOLL_CTL_MOD修改、EPOLL_CTL_DEL删除;
    
     @fd:关联的文件描述符;
    
    @event:指向epoll_event的指针;
   
   成功:0;失败:-1


3、epoll_wait函数
   函数声明:int epoll_wait(int epfd,struct epoll_event * events,int maxevents,int timeout)
  
   功能:该函数用于轮询I/O事件的发生;
  
    @epfd:由epoll_create生成的epoll专用的文件描述符;
   
    @epoll_event:用于回传代处理事件的数组;
   
    @maxevents:每次能处理的事件数;
   
    @timeout:等待I/O事件发生的超时值;
   
   成功:返回发生的事件数;失败:-1


应用举例:

int main()
{
  int i, maxi, listenfd, new_fd, sockfd,epfd,nfds;
  ssize_t n;
  char line[MAXLINE];
  socklen_t clilen;

  struct epoll_event ev,events[20];//ev用于注册事件,数组用于回传要处理的事件

  struct sockaddr_in clientaddr, serveraddr;

  listenfd = socket(AF_INET, SOCK_STREAM, 0);//生成socket文件描述符

  setnonblocking(listenfd);//把socket设置为非阻塞方式
   
  epfd=epoll_create(256);//生成用于处理accept的epoll专用的文件描述符

  ev.data.fd=listenfd;//设置与要处理的事件相关的文件描述符

  ev.events=EPOLLIN|EPOLLET;//设置要处理的事件类型

  epoll_ctl(epfd,EPOLL_CTL_ADD,listenfd,&ev);//注册epoll事件

    //设置服务器端地址信息
  bzero(&serveraddr, sizeof(serveraddr));

  serveraddr.sin_family = AF_INET;

  char *local_addr= LOCAL_ADDR;

  inet_aton(local_addr,&(serveraddr.sin_addr));

  serveraddr.sin_port=htons(SERV_PORT);

  bind(listenfd,(sockaddr *)&serveraddr, sizeof(serveraddr));//绑定socket连接
  listen(listenfd, LISTENQ);//监听

  maxi = 0;
  for ( ; ; )
      {
         /* epoll_wait:等待epoll事件的发生,并将发生的sokct fd和事件类型放入到events数组中;
          * nfds:为发生的事件的个数。
          * 注:
         */
      nfds=epoll_wait(epfd,events,20,500);

      //处理所发生的所有事件
      for(i=0;i<nfds;++i)
      {
          if(events[i].data.fd==listenfd)//事件发生在listenfd上
          {
               /* 获取发生事件端口信息,存于clientaddr中;
               *new_fd:返回的新的socket描述符,用它来对该事件进行recv/send操作*/
              new_fd = accept(listenfd,(struct sockaddr *)&clientaddr, &clilen);
              if(new_fd<0)
                   {
                  perror("new_fd<0");
                  exit(1);
              }
              setnonblocking(new_fd);

              char *str = inet_ntoa(clientaddr.sin_addr);

              ev.data.fd=new_fd;//设置用于读操作的文件描述符

              ev.events=EPOLLIN|EPOLLET;//设置用于注测的读操作事件

              epoll_ctl(epfd,,,&ev);//注册ev
          }
          else if(events[i].events&EPOLLIN)
          {
              if ( (sockfd = events[i].data.fd) < 0)
                       continue;

              if ( (n = read(sockfd, line, MAXLINE)) < 0)
                   {
                  if (errno == ECONNRESET)
                      {
                      close(sockfd);
                      events[i].data.fd = -1;
                  }
                      else
                      std::cout<<"readline error"<<std::endl;
              }
                  else if (n == 0)
                  {
                  close(sockfd);
                  events[i].data.fd = -1;
             }
             ev.data.fd=sockfd;//设置用于写操作的文件描述符

             ev.events=EPOLLOUT|EPOLLET;//设置用于注测的写操作事件

             epoll_ctl(epfd,,sockfd,&ev);//修改sockfd上要处理的事件为EPOLLOUT
        }
       else if(events[i].events&EPOLLOUT)
       {
           sockfd = events[i].data.fd;
            write(sockfd, line, n);
  
            ev.data.fd=sockfd;//设置用于读操作的文件描述符

            ev.events=EPOLLIN|EPOLLET;//设置用于注测的读操作事件

            epoll_ctl(epfd,,sockfd,&ev);//修改sockfd上要处理的事件为EPOLIN
       }
   }
 }
}

 

posted @ 2013-05-24 13:52 yunlong3727 阅读(4) 评论(0) 编辑
epoll_create, epoll_ctl和epoll_wait 实例讲解

NAME
       epoll - I/O event notification facility

SYNOPSIS
       #include <sys/epoll.h>

DEscrīptION
       epoll is a variant of poll(2) that can be used either as Edge or Level
       Triggered interface and scales well to large numbers of watched fds.
       Three system calls are provided to set up and control an epoll set:
       epoll_create(2), epoll_ctl(2), epoll_wait(2).

       An epoll set is connected to a file descrīptor created by epoll_create(2).   Interest for certain file descrīptors is then registered via
       epoll_ctl(2). Finally, the actual wait is started by epoll_wait(2).

其实,一切的解释都是多余的,按照我目前的了解,EPOLL模型似乎只有一种格式,所以大家只要参考我下面的代码,就能够对EPOLL有所了解了,代码的解释都已经在注释中:

while (TRUE)
{
int nfds = epoll_wait (m_epoll_fd, m_events, MAX_EVENTS, EPOLL_TIME_OUT);//等待EPOLL事件的发生,相当于监听,至于相关的端口,需要在初始化EPOLL的时候绑定。
if (nfds <= 0)
   continue;
m_bOnTimeChecking = FALSE;
G_CurTime = time(NULL);
for (int i=0; i<nfds; i++)
{
   try
   {
    if (m_events[i].data.fd == m_listen_http_fd)//如果新监测到一个HTTP用户连接到绑定的HTTP端口,建立新的连接。由于我们新采用了SOCKET连接,所以基本没用。
    {
     OnAcceptHttpEpoll ();
    }
    else if (m_events[i].data.fd == m_listen_sock_fd)//如果新监测到一个SOCKET用户连接到了绑定的SOCKET端口,建立新的连接。
    {
     OnAcceptSockEpoll ();
    }
    else if (m_events[i].events & EPOLLIN)//如果是已经连接的用户,并且收到数据,那么进行读入。
    {
     OnReadEpoll (i);
    }

    OnWriteEpoll (i);//查看当前的活动连接是否有需要写出的数据。
   }
   catch (int)
   {
    PRINTF ("CATCH捕获错误\n");
    continue;
   }
}
m_bOnTimeChecking = TRUE;
OnTimer ();//进行一些定时的操作,主要就是删除一些断线用户等。
}

其实EPOLL的精华,按照我目前的理解,也就是上述的几段短短的代码,看来时代真的不同了,以前如何接受大量用户连接的问题,现在却被如此轻松的搞定,真是让人不得不感叹。

今天搞了一天的epoll,想做一个高并发的代理程序。刚开始真是郁闷,一直搞不通,网上也有几篇介绍epoll的文章。但都不深入,没有将一些注意的地方讲明。以至于走了很多弯路,现将自己的一些理解共享给大家,以少走弯路。

epoll用到的所有函数都是在头文件sys/epoll.h中声明,有什么地方不明白或函数忘记了可以去看一下。
epoll和select相比,最大不同在于:

1epoll返回时已经明确的知道哪个sokcet fd发生了事件,不用再一个个比对。这样就提高了效率。
2select的FD_SETSIZE是有限止的,而epoll是没有限止的只与系统资源有关。

1、epoll_create函数
函数声明:int epoll_create(int size)
该 函数生成一个epoll专用的文件描述符。它其实是在内核申请一空间,用来存放你想关注的socket fd上是否发生以及发生了什么事件。size就是你在这个epoll fd上能关注的最大socket fd数。随你定好了。只要你有空间。可参见上面与select之不同2.

22、epoll_ctl函数
函数声明:int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)
该函数用于控制某个epoll文件描述符上的事件,可以注册事件,修改事件,删除事件。
参数:
epfd:由 epoll_create 生成的epoll专用的文件描述符;
op:要进行的操作例如注册事件,可能的取值EPOLL_CTL_ADD 注册、EPOLL_CTL_MOD 修 改、EPOLL_CTL_DEL 删除

fd:关联的文件描述符;
event:指向epoll_event的指针;
如果调用成功返回0,不成功返回-1

用到的数据结构
typedef union epoll_data {
void *ptr;
int fd;
__uint32_t u32;
__uint64_t u64;
} epoll_data_t;

struct epoll_event {
__uint32_t events; /* Epoll events */
epoll_data_t data; /* User data variable */
};


如:
struct epoll_event ev;
//设置与要处理的事件相关的文件描述符
ev.data.fd=listenfd;
//设置要处理的事件类型
ev.events=EPOLLIN|EPOLLET;
//注册epoll事件
epoll_ctl(epfd,EPOLL_CTL_ADD,listenfd,&ev);


常用的事件类型:
EPOLLIN :表示对应的文件描述符可以读;
EPOLLOUT:表示对应的文件描述符可以写;
EPOLLPRI:表示对应的文件描述符有紧急的数据可读
EPOLLERR:表示对应的文件描述符发生错误;
EPOLLHUP:表示对应的文件描述符被挂断;
EPOLLET:表示对应的文件描述符有事件发生;


3、epoll_wait函数
函数声明:int epoll_wait(int epfd,struct epoll_event * events,int maxevents,int timeout)
该函数用于轮询I/O事件的发生;
参数:
epfd:由epoll_create 生成的epoll专用的文件描述符;
epoll_event:用于回传代处理事件的数组;
maxevents:每次能处理的事件数;
timeout:等待I/O事件发生的超时值(单位我也不太清楚);-1相当于阻塞,0相当于非阻塞。一般用-1即可
返回发生事件数。


用法如下:

/*build the epoll enent for recall */
struct epoll_event ev_read[20];
int nfds = 0; //return the events count
nfds=epoll_wait(epoll_fd,ev_read,20, -1);
for(i=0; i
{
if(ev_read[i].data.fd == sock)// the listener port hava data
......

epoll_wait运行的原理是
等侍注册在epfd上的socket fd的事件的发生,如果发生则将发生的sokct fd和事件类型放入到events数组中。
并 且将注册在epfd上的socket fd的事件类型给清空, 所以如果下一个循环你还要关注这个socket fd的话,则需要用epoll_ctl(epfd,EPOLL_CTL_MOD,listenfd,&ev)来重新设置socket fd的事件类型。这时不用EPOLL_CTL_ADD,因为socket fd并未清空,只是事件类型清空。这一步非常重要。
俺最开始就是没有加这个,白搞了一个上午。

4单个epoll并不能解决所有问题,特别是你的每个操作都比较费时的时候,因为epoll是串行处理的。
所以你还是有必要建立线程池来发挥更大的效能。

//////////////////////////////////////////////////////////////////////////////
man中给出了epoll的用法,example程序如下:
       for(;;) {
           nfds = epoll_wait(kdpfd, events, maxevents, -1);

           for(n = 0; n < nfds; ++n) {
               if(events[n].data.fd == listener) {
                   client = accept(listener, (struct sockaddr *) &local,
                                   &addrlen);
                   if(client < 0){
                       perror("accept");
                       continue;
                   }
                   setnonblocking(client);
                   ev.events = EPOLLIN | EPOLLET;
                   ev.data.fd = client;
                   if (epoll_ctl(kdpfd, EPOLL_CTL_ADD, client, &ev) < 0) {
                       fprintf(stderr, "epoll set insertion error: fd=%d\n",
                               client);
                       return -1;
                   }
               }
               else
                   do_use_fd(events[n].data.fd);
           }
       }
此时使用的是ET模式,即,边沿触发,类似于电平触发,epoll中的边沿触发的意思是只对新到的数据进行通知,而内核缓冲区中如果是旧数据则不进行通知,所以在do_use_fd函数中应该使用如下循环,才能将内核缓冲区中的数据读完。
        while (1) {
           len = recv(*******);
           if (len == -1) {
             if(errno == EAGAIN)
                break;
             perror("recv");
             break;
           }
           do something with the recved data........
        }

在 上面例子中没有说明对于listen socket fd该如何处理,有的时候会使用两个线程,一个用来监听accept另一个用来监听epoll_wait,如果是这样使用的话,则listen socket fd使用默认的阻塞方式就行了,而如果epoll_wait和accept处于一个线程中,即,全部由epoll_wait进行监听,则,需将 listen socket fd也设置成非阻塞的,这样,对accept也应该使用while包起来(类似于上面的recv),因为,epoll_wait返回时只是说有连接到来 了,并没有说有几个连接,而且在ET模式下epoll_wait不会再因为上一次的连接还没读完而返回,这种情况确实存在,我因为这个问题而耗费了一天多 的时间,这里需要说明的是,每调用一次accept将从内核中的已连接队列中的队头读取一个连接,因为在并发访问的环境下,有可能有多个连接“同时”到 达,而epoll_wait只返回了一次。

唯一有点麻烦是epoll有2种工作方式:LT和ET。

LT(level triggered)是缺省的工作方式,并且同时支持block和no-block socket.在这种做法中,内核告诉你一个文件描述符是否就绪了,然后你可以对这个就绪的fd进行IO操作。如果你不作任何操作,内核还是会继续通知你 的,所以,这种模式编程出错误可能性要小一点。传统的select/poll都是这种模型的代表.

ET (edge-triggered)是高速工作方式,只支持no-block socket。在这种模式下,当描述符从未就绪变为就绪时,内核通过epoll告诉你。然后它会假设你知道文件描述符已经就绪,并且不会再为那个文件描述 符发送更多的就绪通知,直到你做了某些操作导致那个文件描述符不再为就绪状态了(比如,你在发送,接收或者接收请求,或者发送接收的数据少于一定量时导致 了一个EWOULDBLOCK 错误)。但是请注意,如果一直不对这个fd作IO操作(从而导致它再次变成未就绪),内核不会发送更多的通知(only once),不过在TCP协议中,ET模式的加速效用仍需要更多的benchmark确认。

转自:http://hi.baidu.com/jrckkyy/blog/item/f3427f119fddaf04b8127b2b.html

posted @ 2013-05-24 13:50 yunlong3727 阅读(2) 评论(0) 编辑
关于Segmentation fault (core dumped)几个简单问题的整理
摘要: 有的程序可以通过编译,但在运行时会出现Segment fault(段错误)。这通常都是指针错误引起的。但这不像编译错误一样会提示到文件一行,而是没有任何信息。一种办法是用gdb的step, 一步一步寻找。但要step一个上万行的代码让人难以想象。 我们还有更好的办法,这就是core file。如果想让系统在信号中断造成的错误时产生core文件,我们需要在shell中按如下设置:#设置core大小为无限ulimit -c unlimited#设置文件大小为无限ulimit unlimited发生core dump之后,用gdb进行查看core文件的内容,以定位文件中引发core dump的行:g阅读全文
posted @ 2013-05-21 15:12 yunlong3727 阅读(15) 评论(0) 编辑
在Linux下执行一个文件时候提示“权限不够”的解决办法
摘要: 在Linux下执行一个文件时候提示“权限不够”的解决办法如下转到那个文件的目录下面执行下面命令: chmod 777 filename阅读全文
posted @ 2013-05-21 11:59 yunlong3727 阅读(4) 评论(0) 编辑
临界区和互斥锁的区别
摘要: 临界区和互斥锁的区别1、临界区只能用于对象在同一进程里线程间的互斥访问;互斥体可以用于对象进程间或线程间的互斥访问。2、临界区是非内核对象,只在用户态进行锁操作,速度快;互斥体是内核对象,在核心态进行锁操作,速度慢。3、临界区和互斥体在Windows平台都下可用;Linux下只有互斥体可用阅读全文
posted @ 2013-05-21 11:46 yunlong3727 阅读(0) 评论(0) 编辑
仅列出标题  下一页
原创粉丝点击