Linux--Signal

黄舟
黄舟original
2017-01-18 10:28:001192parcourir

1. Signal
Le signal est utilisé pour avertir le processus qu'un événement asynchrone s'est produit. Le noyau peut également envoyer des signaux au processus en raison d'événements internes, l'informant qu'un événement s'est produit. Notez que les signaux ne sont utilisés que pour informer un processus des événements qui se sont produits et ne transmettent aucune donnée au processus.

**Utilisez la commande kill-l pour afficher la liste des signaux définis par le système

2 Comment les signaux sont générés
① Envoyez un signal au premier plan via une touche de combinaison du clavier ( ajouter & après une commande) Peut être envoyé en arrière-plan pour exécuter)

a. L'action par défaut du signal est de terminer le processus. L'action par défaut de SIGQUIT est de terminer le processus et le Core Dump (. Le vidage de mémoire se produit lorsque le processus se termine anormalement, vous pouvez choisir de vider l'espace utilisateur du processus. Toutes les données de la mémoire sont enregistrées sur le disque. Le nom du fichier est généralement core. Cela signifie généralement que le programme a. un BUG. Vous pouvez utiliser un débogueur pour vérifier le fichier core afin de connaître la cause de l'erreur.) Par défaut, la génération des fichiers core n'est pas autorisée. Parce que le fichier core peut contenir des mots de passe utilisateur, ce qui n'est pas sûr, vous pouvez le faire. Vous pouvez utiliser la commande ulimit pour modifier cette limite pendant le développement et le débogage afin de permettre la génération de fichiers principaux.
Utilisez la commande ulimit -c 1024

La commande ulimit modifie la limite de ressources du processus Shell. Le PCB du processus de test est copié du processus Shell, il a donc également la même valeur de limite de ressources. comme le processus Shell. De cette façon, un Core Dump peut être généré
② Envoyer un signal au processus en appelant la fonction système

une commande.kill est implémentée en appelant la fonction kill. La fonction kill peut envoyer un signal spécifié à un processus spécifié. La fonction raise peut envoyer un signal spécifié au processus en cours (s'envoyer un signal à lui-même). raise( int signo);
all return 0 en cas de succès et -1 en cas d'erreur ; la

fonction d'abandon fait que le processus en cours reçoit le signal SIGABRT et se termine anormalement.
void abort(void);

Comme la fonction exit, la fonction abort réussira toujours, il n'y a donc pas de valeur de retour.

③ Signaux générés par les conditions du logiciel

a.fonction d'alarme et signal SIGALRM
alarme int non signée (secondes int non signées peut définir un réveil, qui doit) ; dire au noyau Envoyer un signal SIGALRM au processus en cours après senconds secondes. L'action par défaut est de terminer le processus en cours. La valeur de retour de la fonction est 0 ou le nombre de secondes restantes dans l'heure d'alarme précédemment définie.
3. Comment gérer les signaux
① Ignorer ce signal

② Effectuer l'action de traitement par défaut du signal, mettant généralement fin au processus

③ Capturer le signal

IV, Livraison et blocage du signal
①, Blocage

L'exécution réelle de l'action de traitement du signal est appelée livraison du signal (Delivery), et l'état entre la génération du signal et la livraison est appelé signal en attente (En attente) . Un processus peut choisir de bloquer un signal. Le signal bloqué restera dans l'état en attente lorsqu'il sera généré, et l'action de livraison ne sera exécutée que lorsque le processus débloquera le signal. Le blocage et l'ignorance sont différents tant que le signal est bloqué, il ne sera pas délivré, tandis que l'ignorer est une action de traitement facultative après la délivrance.
Représentation des signaux dans le noyau

Linux--SignalChaque signal a deux bits de drapeau indiquant le blocage et l'attente, et il existe également une fonction Les pointeurs représentent les actions de traitement. Lorsqu'un signal est généré, le noyau définit l'indicateur en attente du signal dans le bloc de contrôle de processus et n'efface pas l'indicateur tant que le signal n'est pas délivré

Si ce signal est généré plusieurs fois avant que le processus débloque un signal, en Les signaux Linux réguliers générés plusieurs fois avant la livraison ne sont comptés qu'une seule fois, tandis que les signaux en temps réel générés plusieurs fois avant la livraison peuvent être placés dans une file d'attente dans l'ordre. Chaque signal n'a qu'un seul bit d'indicateur en attente, qui est soit 0, soit 1. Il n'enregistre pas combien de fois le signal a été généré. L'indicateur de blocage est également exprimé de cette manière. (le vide est un état, en attente indique la présence ou l'absence) Les indicateurs en attente et de blocage peuvent être stockés avec le même type de données sigset_t est appelé un ensemble de signaux, et un ensemble de signaux de blocage est également appelé le masque de signal du processus en cours. Le « bouclier » ici doit être compris comme bloquant plutôt que comme ignorant.

②, fonction d'opération d'ensemble de signaux

③, sigprocmask
#include <signal.h>
int sigemptyset(sigset_t *set);//初始化对应的信号集bit位为0
int sigfillset(sigset_t *set);//初始化对象的信号集bit位为1
int sigaddset(sigset_t *set, int signo);//添加有效信号 
int sigdelset(sigset_t *set, int signo);//删除有效信号
int sigismember(const sigset_t *set, int signo);//判断一个信号集的有效信号中是否包含某种信号,包含返回1,不包含返回0。

Appelez la fonction sigprocmask pour lire ou modifier le mot de masque de signal (ensemble de signaux de blocage) du processus.
int sigprocmask(int how, const sigset_t *set, sigset_t *oset); 0 en cas de succès, -1 si une erreur se produit
Si l'appel de sigprocmask débloque plusieurs signaux actuellement en attente, alors avant le retour de sigprocmask, au moins un des signaux doivent être délivrés.
La signification du paramètre how

L'ensemble SIG_BLOCK contient les signaux que nous voulons ajouter au masque de signal actuel, et

L'ensemble SIG_UNBLOCK contient Nous espérons pouvoir le faire. débloquer le signal bloqué dans le mot du signal actuel.

SIG_SETMASK définit le mot de masquage du signal actuel à la valeur indiquée par set,

④, sigending
int sigending(sigset_t * set );
sigending lit l'ensemble de signaux en attente du processus en cours et l'envoie via le paramètre défini. Si l'appel réussit, il renvoie 0, si une erreur se produit, il renvoie -1.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Instructions avec le programme

#include<stdio.h>  
  2 #include<unistd.h>  
  3 #include<signal.h>  
  4 void printsigset(sigset_t* sig)  
  5 {  
  6     int i=0;  
  7     for(;i<31;i++)  
  8     {  
  9         if(sigismember(sig,i))//判断指定信号是否在目标集合中  
 10         {  
 11             printf("1");  
 12         }  
 13         else  
 14         {  
 15             printf("0");  
 16         }  
 17     }  
 18     printf("\n");  
 19 }  
 20 int main()  
 21 {  
 22     sigset_t s,p;//定义信号集  
 23     sigemptyset(&s);//初始化  
 24     sigemptyset(&p);  
 25     sigaddset(&s,SIGINT);//设置信号ctrl+C  
 26     sigprocmask(SIG_BLOCK,&s,NULL);//设置阻塞信号集,阻塞 SIGINT信号  
 27     while(1)  
 28     {  
 29         sigpending(&p);//获取未决信号集  
 30         printsigset(&p);  
 31         sleep(1);  
 32     }  
 33     return 0;  
 34 }

结果分析:
程序运行时,每秒钟把各信号的未决状态打印一遍,直到按Ctrl-C将会使SIGINT信号处于未决状态,
五、捕捉信号
a.内核如何实现信号的捕捉

如果信号的处理动作是用户自定义函数,在信号递达时就调用这个函数,这称为捕捉信号,处理过程如下,举例来说明
1. 用户程序注册了SIGQUIT信号的处理函数sighandler。
2. 当前正在执行main函数,这时发生中断或异常切换到内核态。
3. 在中断处理完毕后要返回用户态的main函数之前检查到有信号SIGQUIT递达。
4. 内核决定返回用户态后不是恢复main函数的上下文继续执行,而是执行sighandler函 数,sighandler和main函数使用不同的堆栈空间,它们之间不存在调用和被调用的关系,是两个独立的控制流程。
5. sighandler函数返回后自动执行特殊的系统调用sigreturn再次进入内核态。
6. 如果没有新的信号要递达,这次再返回用户态就是恢复main函数的上下文继续执行了
**(先从用户态―>内核态->返回用户态之前检查有信号递达,返回用户态处理信号->处理完成后再进入内核态->如果没有新的信号递达,返回用户态恢复上下文继续执行)
b.sigaction 
int sigaction(int signo, const struct sigaction *act, struct sigaction *oact);
sigaction函数可以读取和修改与指定信号相关联的处理动作。调用成功则返回0,出错则返回- 1
将sa_handler赋值为常数SIG_IGN传给sigaction表示忽略信号,赋值为常数SIG_DFL表示执行系统默认动作,赋值为一个函数指针表示用自定义函数捕捉信号,或者说向内核注册了一个信号处理函 数,该函数返回值为void,可以带一个int参数,通过参数可以得知当前信号的编号,这样就可以用同一个函数处理多种信号。显然,这也是一个回调函数,不是被main函数调用,而是被系统所调用。
当某个信号的处理函数被调用时,内核自动将当前信号加入进程的信号屏蔽字,当信号处理函数返回时自动恢复原来的信号屏蔽字,这样就保证了在处理某个信号时,如果这种信号再次产生,那么它会被阻塞到当前处理结束为止。 
如果在调用信号处理函数时,除了当前信号被自动屏蔽之外,还希望自动屏蔽另外一些信号,则用sa_mask字段说明这些需要额外屏蔽的信号,当信号处理函数返回时自动恢复原来的信号屏蔽字。
c.pause
int pause(void); 
pause函数使调用进程挂起直到有信号递达。如果信号的处理动作是终止进程,则进程终止,pause函数没有机会返回;如果信号的处理动作是忽略,则进程继续处于挂起状态,pause不返回;如果信号的处理动作是捕捉,则调用了信号处理函数之后pause返回-1,
用alarm和pause实现sleep(3)的函数

1 #include<stdio.h>  
 2 #include<unistd.h>  
 3 #include<signal.h>  
 4 void sig_alarm(int signo)  
 5 {  
 6        //do nothing  
 7 }  
 8 unsigned int  my_sleep(unsigned int times)  
 9 {  
 10     struct sigaction new ,old;  
 11     unsigned int unslept=0;  
 12     new.sa_handler=sig_alarm;  
 13     sigemptyset(&new.sa_mask);  
 14     sigemptyset(&old.sa_mask);  
 15     new.sa_flags=0;  
 16     sigaction(SIGALRM,&new,&old);//注册信号处理函数  
 17     alarm(times); //设置闹钟  
 18     pause();  
 19     unslept=alarm(0);//取消闹钟  
 20     sigaction(SIGALRM,&old,NULL);//恢复默认信号处理动作  
 21     return unslept;  
 22 }  
 23 int main()  
 24 {  
 25     while(1)  
 26     {  
 27         my_sleep(5);  
 28         printf("5 senconds pass\n");  
 29     }  
 30     return 0;  
 31 }

六、可重入函数 
当捕捉到信号时,不论进程的主控制流程当前执行到哪儿,都会先跳到信号处理函数中执行,从信号处理函数返回后再继续执行主控制流程。信号处理函数是一个单独的控制流程,因为它和主控制流程是异步的,二者不存在调用和被调用的关系,并且使用不同的堆栈空间。引入了信号处理函数使得一个进程具有多个控制流程,如果这些控制流程访问相同的全局资源(全局变量、硬件资源等),就有可能出现冲突。

以上就是Linux--信号的内容,更多相关内容请关注PHP中文网(www.php.cn)!


Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn