1. 程式人生 > >Linux_訊號與訊號量

Linux_訊號與訊號量

訊號:

訊號機制是類UNIX系統中的一種重要的程序間通訊手段之一。我們經常使用訊號來向一個程序傳送一個簡短的訊息。例如:假設我們啟動一個程序通過socket讀取遠端主機發送過來的網路資料包,此時由於網路因素當前主機還沒有收到相應的資料,當前程序被設定為可中斷等待狀態(TASK_INTERRUPTIBLE),此時我們已經失去耐心,想提前結束這個程序,於是可以通過kill命令想這個程序傳送KILL訊號,核心會喚醒該程序,執行它的訊號處理函式,KILL訊號的預設處理是退出該程序。

另外應用程式可以通過signal()等函式來為一個訊號設定預設處理函式。例如當用戶按下CTRL+C時,shell將會發出SIGINT訊號,SIGINT的預設處理函式是執行程序的退出程式碼,如下所示:


可以通過類似下面的命令顯式的給一個程序傳送一個訊號:

kill -2 pid
事實上,程序也不知道訊號到底什麼時候到達。訊號是非同步的,一個程序不可能等待訊號的到來,也不知道訊號會到來,那麼,程序是如何發現和接受訊號呢?實際上,訊號的接收不是由使用者程序來完成的,而是由核心代理。當一個程序P2向另一個程序P1傳送訊號後,核心接受到訊號,並將其放在P1的訊號隊列當中。當P1再次陷入核心態時,會檢查訊號佇列,並根據相應的訊號調取相應的訊號處理函式。

訊號檢測和響應時機

剛才我們說,當P1再次陷入核心時,會檢查訊號佇列。那麼,P1什麼時候會再次陷入核心呢?陷入核心後在什麼時機會檢測訊號佇列呢?

  1. 當前程序由於系統呼叫、中斷或異常而進入系統空間以後,從系統空間返回到使用者空間的前夕。
  2. 當前程序在核心中進入睡眠以後剛被喚醒的時候(必定是在系統呼叫中),或者由於不可忽略訊號的存在而提前返回到使用者空間。

進入訊號處理函式

發現訊號後,根據訊號向量,知道了處理函式,那麼該如何進入訊號處理程式,又該如何返回呢?

我們知道,使用者程序提供的訊號處理函式是在使用者態裡的,而我們發現訊號,找到訊號處理函式的時刻處於核心態中,所以我們需要從核心態跑到使用者態去執行訊號處理程式,執行完畢後還要返回核心態。


#include <signal.h>
#include <stdio.h>

void int_handler(int signum)
{
 printf("\nSIGINT signal handler.\n");
 printf("exit.\n");
 exit(-1);
}

int main()
{
 signal(SIGINT, int_handler);
 printf("int_handler set for SIGINT\n");

  while(1)
 {
  printf("go to sleep.\n");
  sleep(60);
 }

  return 0;
}


訊號量:

一.什麼是訊號量訊號量的使用主要是用來保護共享資源,使得資源在一個時刻只有一個程序(執行緒)所擁有。
訊號量的值為正的時候,說明它空閒。所測試的執行緒可以鎖定而使用它。若為0,說明它被佔用,測試的執行緒要進入睡眠佇列中,等待被喚醒。

二.訊號量的分類


在學習訊號量之前,我們必須先知道——Linux提供兩種訊號量
(1) 核心訊號量,由核心控制路徑使用
(2)使用者態程序使用的訊號量,這種訊號量又分為POSIX訊號量和SYSTEM V訊號量。
1、POSIX訊號量又分為有名訊號量和無名訊號量:

(1)有名訊號量,其值儲存在檔案中,所以它既可以用於執行緒,也可以用於相關程序間,甚至是不相關程序

(2)無名訊號量,其值儲存在記憶體中。無名訊號量常用於多執行緒間的同步,同時也用於相關程序間的同步。也就是說,無名信號量必須是多個程序(執行緒)的共享變數,無名訊號量要保護的變數也必須是多個程序(執行緒)的共享變數,這兩個條件是缺一不可的。

倘若對訊號量沒有以上的全面認識的話,你就會很快發現自己在訊號量的森林裡迷失了方向。

POSIX 訊號量與SYSTEM V訊號量的比較:
1. 對POSIX來說,訊號量是個非負整數。常用於執行緒間同步。而SYSTEM V訊號量則是一個或多個訊號量的集合,它對應的是一個訊號量結構體,這個結構體是為SYSTEM V IPC服務的,訊號量只不過是它的一部分。常用於程序間同步。
2.POSIX訊號量的引用標頭檔案是“<semaphore.h>”,而SYSTEM V訊號量的引用標頭檔案是“<sys/sem.h>”。
3.從使用的角度,System V訊號量是複雜的,而Posix訊號量是簡單。比如,POSIX信號量的建立和初始化或PV操作就很非常方便。

無名訊號量:

#include <pthread.h>
#include <semaphore.h>
#include <sys/types.h>
#include <stdio.h>
#include <unistd.h>
int number; // 被保護的全域性變數
sem_t sem_id;
void* thread_one_fun(void *arg)
{
sem_wait(&sem_id);
printf("thread_one have the semaphore\n");
number++;
printf("number = %d\n",number);
sem_post(&sem_id);
}
void* thread_two_fun(void *arg)
{
sem_wait(&sem_id);
printf("thread_two have the semaphore \n");
number--;
printf("number = %d\n",number);
sem_post(&sem_id);
}
int main(int argc,char *argv[])
{
number = 1;
pthread_t id1, id2;
sem_init(&sem_id, 0, 1);
pthread_create(&id1,NULL,thread_one_fun, NULL);
pthread_create(&id2,NULL,thread_two_fun, NULL);
pthread_join(id1,NULL);
pthread_join(id2,NULL);
printf("main,,,\n");
return 0;
}

有名訊號量:
//File1: server.c </u>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
#include <semaphore.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define SHMSZ 27
char SEM_NAME[]= "vik";
int main()
{
char ch;
int shmid;
key_t key;
char *shm,*s;
sem_t *mutex;
//name the shared memory segment
key = 1000;
//create & initialize semaphore
mutex = sem_open(SEM_NAME,O_CREAT,0644,1);
if(mutex == SEM_FAILED)
{
perror("unable to create semaphore");
sem_unlink(SEM_NAME);
exit(-1);
}
//create the shared memory segment with this key
shmid = shmget(key,SHMSZ,IPC_CREAT|0666);
if(shmid<0)
{
perror("failure in shmget");
exit(-1);
}
//attach this segment to virtual memory
shm = shmat(shmid,NULL,0);
//start writing into memory
s = shm;
for(ch='A';ch<='Z';ch++)
{
sem_wait(mutex);
*s++ = ch;
sem_post(mutex);
}
//the below loop could be replaced by binary semaphore
while(*shm != '*')
{
sleep(1);
}
sem_close(mutex);
sem_unlink(SEM_NAME);
shmctl(shmid, IPC_RMID, 0);
exit(0);
}

//File 2: client.c</u>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
#include <semaphore.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define SHMSZ 27
char SEM_NAME[]= "vik";
int main()
{
char ch;
int shmid;
key_t key;
char *shm,*s;
sem_t *mutex;
//name the shared memory segment
key = 1000;
//create & initialize existing semaphore
mutex = sem_open(SEM_NAME,0,0644,0);
if(mutex == SEM_FAILED)
{
perror("reader:unable to execute semaphore");
sem_close(mutex);
exit(-1);
}
//create the shared memory segment with this key
shmid = shmget(key,SHMSZ,0666);
if(shmid<0)
{
perror("reader:failure in shmget");
exit(-1);
}
//attach this segment to virtual memory
shm = shmat(shmid,NULL,0);
//start reading
s = shm;
for(s=shm;*s!=NULL;s++)
{
sem_wait(mutex);
putchar(*s);
sem_post(mutex);
}
//once done signal exiting of reader:This can be replaced by
another semaphore
*shm = '*';
sem_close(mutex);
shmctl(shmid, IPC_RMID, 0);
exit(0);
}


System V訊號量:

int sem_id = 0; /* semget的返回值,全域性 */
#define MUTEX 0 /* 用於返回臨界區的訊號量在集合中的序數 */
#define NUM_SEM 1 /* 集合中訊號量的個數 */
#define SEM_KEY 0x11223344 /*保證核心中的唯一性 */

void P(int sem_num)
{
    struct sembuf sem;
    sem.sem_num = MUTEX;
    sem.sem_op = -1;
    sem.sem_flg = 0;
     
    if( -1 == semop(sem_id, &sem, 1) )
    {
         /* 錯誤處理 */
    }
}


void V(int sem_num)
{
    struct sembuf sem;
    sem.sem_num = MUTEX;
    sem.sem_op = 1;
    sem.sem_flg = 0;
     
    if( -1 == semop(sem_id, &sem, 1) )
    {
         /* 錯誤處理 */
    }
}

主函式:

int main()
{
    ...
    int semid;
    ....
    semid = semget(SEM_KEY, 0, 0); /* panduan 判斷該型號量組是否已經存在 */
    if( -1 == semid )
    {
        semid = semget(SEM_KEY, NUM_SEM, IPC_CREAT | IPC_EXCL | 0666);
        if( -1 == semid)
        {
            /* 錯誤處理 */
        }
        else 
        {
           semctl(sem_id, MUTEX, SETVAL, 1); /* 初始值為1。錯誤處理略 */
        }
    }

    ...
    P(MUTEX);
    /* 臨界區程式碼段 */
    V(MUTEX);
    ...
}


最全面的linux訊號量解析:http://blog.csdn.net/qinxiongxu/article/details/7830537

訊號只是一個數字,數字為0-31表示不同的訊號,如下表所示。

  編號

  訊號名

  預設動作

  說明

  1

  SIGHUP

  程序終止

  終端斷開連線

  2

  SIGINT

  程序終止

  使用者在鍵盤上按下CTRL+C

  3

  SIGQUIT

  程序意外結束(Dump)

  使用者在鍵盤上按下CTRL+\

  4

  SIGILL

  程序意外結束(Dump)

  遇到非法指令

  5

  SIGTRAP

  程序意外結束(Dump)

  遇到斷電,用於除錯

  6

  SIGABRT/SIGIOT

  程序意外結束(Dump)

  7

  SIGBUS

  程序意外結束(Dump)

  匯流排錯誤

  8

  SIGFPE

  程序意外結束(Dump)

  浮點異常

  9

  SIGKILL

  程序終止

  其他程序傳送SIGKILL將導致目標程序終止

  10

  SIGUSR1

  程序終止

  應用程式可自定義使用

  11

  SIGSEGV

  程序意外結束(Dump)

  非法的記憶體訪問

  12

  SIGUSR2

  程序終止

  應用程式可自定義使用

  13

  SIGPIPE

  程序終止

  管道讀取端已經關閉,寫入端程序會收到該訊號

  14

  SIGALRM

  程序終止

  定時器到時

  15

  SIGTERM

  程序終止

  傳送該訊號使目標程序終止

  16

  SIGSTKFLT

  程序終止

  堆線錯誤

  17

  SIGCHLD

  忽略

  子程序退出時會向父程序傳送該訊號

  18

  SIGCONT

  忽略

  程序繼續執行

  19

  SIGSTOP

  程序暫停

  傳送該訊號會使目標程序進入TASK_STOPPED狀態

  20

  SIGTSTP

  程序暫停

  在終端上按下CTRL+Z

  21

  SIGTTIN

  程序暫停

  後臺程序從控制終端讀取資料

  22

  SIGTTOU

  程序暫停

  後臺程序從控制終端讀取資料

  23

  SIGURG

  忽略

  socket收到設定緊急指標標誌的網路資料包

  24

  SIGXCPU

  程序意外結束(Dump)

  程序使用CPU已經超過限制

  25

  SIGXFSZ

  程序意外結束(Dump)

  程序使用CPU已經超過限制

  26

  SIGVTALRM

  程序終止

  程序虛擬定時器到期

  27

  SIGPROF

  程序終止

  程序Profile定時器到期

  28

  SIGMNCH

  忽略

  程序終端視窗大小改變

  29

  SIGIO

  程序暫停

  用於非同步IO

  29

  SIGPOLL

  程序暫停

  用於非同步IO

  30

  SIGPWR

  程序暫停

  電源失效

  31

  SIGUNUSED

  程序暫停

  保留未使用