信号:
一、EAGAIN信号
在Linux环境下开发经常会碰到很多错误(设置errno),其中EAGAIN是其中比较常见的一个错误(比如用在非阻塞操作中)。
从字面上来看,是提示再试一次。这个错误经常出现在当应用程序进行一些非阻塞(non-blocking)操作(对文件或socket)的时候。例如,以 O_NONBLOCK的标志打开文件/socket/FIFO,如果你连续做read操作而没有数据可读,此时程序不会阻塞起来等待数据准备就绪返回,read函数会返回一个错误EAGAIN,提示你的应用程序现在没有数据可读请稍后再试。
又例如,当一个系统调用(比如fork)因为没有足够的资源(比如虚拟内存)而执行失败,返回EAGAIN提示其再调用一次(也许下次就能成功)。
Linux - 非阻塞socket编程处理EAGAIN错误
在linux进行非阻塞的socket接收数据时经常出现Resource temporarily unavailable,errno代码为11(EAGAIN),这是什么意思?
这表明你在非阻塞模式下调用了阻塞操作,在该操作没有完成就返回这个错误,这个错误不会破坏socket的同步,不用管它,下次循环接着recv就可以。对非阻塞socket而言,EAGAIN不是一种错误。在VxWorks和Windows上,EAGAIN的名字叫做EWOULDBLOCK。
另外,如果出现EINTR即errno为4,错误描述Interrupted system call,操作也应该继续。
最后,如果recv的返回值为0,那表明连接已经断开,我们的接收操作也应该结束。
//设置非阻塞
int flags = fcntl(fd,F_GETFL,NULL);
fcntl(fd,F_SETFL,flags|O_NONBLOCK);//设置非阻塞
二、EINTR
如果进程在一个慢系统调用(slow system call)中阻塞时,当捕获到某个信号且相应信号处理函数返回时,这个系统调用被中断,调用返回错误,设置errno为EINTR(相应的错误描述为“Interrupted system call”)。
发生在以下情况:
write
由于信号中断,没写成功任何数据。
The call was interrupted by a signal before any data was written.
open
由于信号中断,没读到任何数据。
The call was interrupted by a signal before any data was read.
recv
由于信号中断返回,没有任何数据可用。
The receive was interrupted by delivery of a signal before any data were available.
sem_wait
函数调用被信号处理函数中断。
The call was interrupted by a signal handler.
对于EINTR信号处理方法:
1、重启;
goto
2、屏蔽信号;
struct sigaction action;
action.sa_handler = SIG_IGN;
sigemptyset(&action.sa_mask);
sigaction(SIGALRM, &action, NULL);
3、安装信号时设置 SA_RESTART属性(该方法对有的系统调用无效);
struct sigaction action;
action.sa_handler = handler_func;
sigemptyset(&action.sa_mask);
action.sa_flags = 0;
/* 设置SA_RESTART属性 */
action.sa_flags |= SA_RESTART;
sigaction(SIGALRM, &action, NULL);
信号量:
信号量就是用来解决进程间的同步与互斥问题的一种进程间通信机制。
一、信号量的P(-)V(+)操作
struct sembuf
{
unsigned short sem_num; /*0-nsems-1*/
short sem_op; /*negative, 0, pasitive*/
short sem_flg; /*0 IPC_UNDO IPC_NOWAIT*/
};
注:在sembuf结构中,sem_num是相对应的信号量集中的某一个资源,所以其值是一个从0到相应的信号量集的资源总数(ipc_perm.sem_nsems)之间的整数。sem_op指明所要执行的操作,sem_flg说明函数semop的行为。sem_op的值是一个整数.释放相应的资源数,将sem_op的值加到信号量的值上.
union semun
{
int val; /*for SETVAL*/
struct semid_ds *buf; /*for IPC_STAT ans IPC_SET*/
unsigned short *array; /*for GETALL ans SET_ALL*/
};
/*sem P*/
int sem_p(int semid, int semnum)
{
/*int semop(int semid, struct sembuf semoparray[], size_t cnts); */
struct sembuf semOpr ;
semOpr.sem_num = semnum;
semOpr.sem_op = -1;
semOpr.sem_flg = SEM_UNDO;
return semop(semid, &semOpr, 1);
}
/*sem V*/
int sem_v(int semid, int semnum)
{
/*int semop(int semid, struct sembuf semoparray[], size_t cnts); */
struct sembuf semOpr ;
semOpr.sem_num = semnum;
semOpr.sem_op = 1;
semOpr.sem_flg = SEM_UNDO;
return semop(semid, &semOpr, 1);
}
/*get semval*/
int get_semval(int semid, int semnum)
{
return semctl(semid, semnum, GETVAL);
}
注:
PV原子操作的具体定义如下
● P操作:如果有可用的资源(信号量值>0),则此操作所在的进程占用一个资源(此时信号量值减1,进入临界区代码);如果没有可用的资源(信号量值=0),则此操作所在的进程被阻塞直到系统将资源分配给该进程(进入等待队列,一直等到资源轮到该进程)。
● V操作:如果在该信号量的等待队列中有进程在等待资源,则唤醒一个阻塞进程;如果没有进程等待它,则释放一个资源(即信号量值加1)。
int semget(key_t key,int nsems,int semflg);
其中,key:信号量的键值;nsems:创建信号量数目;semflg与shmflg相同,两个参数:IPC_ECXL|IPC_CREATE;返回值为信号量标识
int semop(int semid,struct sembuf *sops,size_t nsops);
其中,semid为信号量标识符,要操作的信号量;sops:指向信号量操作数组;nsops:操作数组sops中的操作格式(元素数目),通常取值为1;返回值成功为信号量标识符。对semum中的val值进行操作(+1或-1,一般先减再加,初始为1)。若为0阻塞。
int semctl(int semid,int semnum,int cmd,union semun arg);
其中,semid为semget返回的信号量标识符;semnum:信号量编号,通常为0;cmd是对信号量的操作,IPC_STAT获取该信号量的semid_ds结果,IPC_SETVAL设置arg的val值,IPC_GETVAL返回信号量的当前值;IPC_RMID从系统中删除信号量;根据cmd返回不同值
原文:
https://blog.csdn.net/poetteaes/article/details/80213391
https://blog.csdn.net/mybelief321/article/details/9086151