linux信號和信號量(轉)

信號:
一、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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章