English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
Durante o processo de desenvolvimento de programas, usamos frequentemente alguns temporizadores, geralmente se a precisão do tempo não for alta, podemos usar a função sleep, uslepp para que o processo durma por um tempo para implementar a temporização;
O primeiro tem unidade de segundos (s), o segundo de microsegundos (us); mas às vezes não queremos que o processo fique bloqueado em sono, precisamos que o processo execute normalmente, e execute a operação correspondente quando atingir o tempo especificado;
No Linux, geralmente usamos as funções alarm e setitimer para implementar funções de temporização;
A seguir, analisaremos detalhadamente essas duas funções:
(1) função campainha
alarm também é conhecido como função campainha, ele pode configurar um temporizador no processo, quando o temporizador especificado pelo tempo atingir, ele enviará o sinal SIGALRM para o processo;
O原型 da função alarm é como follows:
unsigned int alarm(unsigned int seconds); //seconds: especifica o número de segundos
Arquivo de cabeçalho necessário
#include<unistd.h>
原型 da função
unsigned int alarm(unsigned int seconds)
Parâmetros da função
seconds: especifica o número de segundos
Valor de retorno da função
Sucesso: Se o processo já tiver configurado o tempo da campainha antes da chamada deste alarm(), ele retornará o tempo restante da campainha anterior, caso contrário, retornará 0.
Erro:-1
A seguir está um exemplo simples da função alarm():
void sigalrm_fn(int sig) { printf("alarm!\n"); alarm(2; return; } int main(void) { signal(SIGALRM, sigalrm_fn); //A função seguinte deve ter um parâmetro int alarm(1; while(1) pause(); }
(2) setitimer()
No Linux, se a precisão do temporizador não for muito alta, pode-se usar alarm() e signal(), mas para implementar uma função de temporização com alta precisão, é necessário usar a função setitimer().
setitimer() é uma API do Linux, não da Biblioteca Padrão de C, setitimer() possui duas funções, uma é especificar um tempo após o qual executar alguma função, e a outra é executar alguma função a cada intervalo de tempo;
O Linux agende para cada tarefa:3Um temporizador interno:
ITIMER_REAL: Temporizador real-time, independentemente do modo de execução do processo (mesmo quando o processo está suspenso), ele sempre conta. Após a chegada programada, envia o sinal SIGALRM para o processo.
ITIMER_VIRTUAL: Este não é um temporizador real-time, ele calcula o tempo de execução do processo em modo de usuário (isto é, durante a execução do programa). Após a chegada programada, envia o sinal SIGVTALRM para o processo.
ITIMER_PROF: O processo conta em modo de usuário (isto é, durante a execução do programa) e em modo nuclear (isto é, durante a programação do processo). A chegada programada gera o sinal SIGPROF. O tempo registrado por ITIMER_PROF é maior do que o tempo gasto na programação do processo em relação a ITIMER_VIRTUAL.
O temporizador é atribuído um valor inicial no inicialização, diminui ao longo do tempo, diminui para 0 e emite um sinal, ao mesmo tempo restaurando o valor inicial. Durante a tarefa, podemos usar um ou todos os três tipos de temporizadores, mas no mesmo momento, apenas um temporizador do mesmo tipo pode ser usado.
原型 do setitimer função é como follows:
#include <sys/time.h> int setitimer(int which, const struct itimerval *new_value, struct itimerval *old_value); Os valores do temporizador são definidos pelas seguintes estruturas: struct itimerval { struct timeval it_interval; /* next value */ struct timeval it_value; /* current value */ }; struct timeval { time_t tv_sec; /* seconds */ suseconds_t tv_usec; /* microseconds */ };
it_interval é usado para especificar quanto tempo passa antes de executar a tarefa, it_value é usado para armazenar quanto tempo falta até a execução da tarefa. Por exemplo, você pode especificar it_interval como2segundos (microsegundos são 0), no início também definimos o it_value para2segundos (microsegundos são 0), quando passar um segundo, it_value diminui uma unidade para1depois de1segundos, então it_value diminui1passa a 0, neste momento é emitido um sinal (informa ao usuário que o tempo acabou e a tarefa pode ser executada), e o sistema reinicializa o it_value para o valor de it_interval, ou seja2segundos, após o que a contagem é redefinida
Aqui está um exemplo simples de setitimer:
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <signal.h> #include <sys/time.h> void test_func() { static count = 0; printf("count is %d\n", count++; } void init_sigaction() { struct sigaction act; act.sa_handler = test_func; //Configurar a função de tratamento do sinal act.sa_flags = 0; sigemptyset(&act.sa_mask); sigaction(SIGPROF, &act, NULL);//Quando o tempo atingir, enviar o sinal SIGROF } void init_time() { struct itimerval val; val.it_value.tv_sec = 1; //1segundos após ativar o temporizador val.it_value.tv_usec = 0; val.it_interval = val.it_value; //O intervalo do temporizador é1s setitimer(ITIMER_PROF, &val, NULL); } int main(int argc, char **argv) { init_sigaction(); init_time(); while(1; return 0; }
Pode ser visto que cada segundo é impresso um valor de count:
A seguir estão os resultados da execução:
[root@localhost 5th]# ./test
count é 0
count é 1
count é 2
count é 3
count é 4
count é 5
count é 6
count é 7
count é 8
count é 9
Anexo:
signal
1. Arquivo de cabeçalho
#include <signal.h>
2. Função
. Definir a ação correspondente do sinal
3. Protótipo da função
void (*signal(int signum,void(* handler)(int)))(int);
Dividindo em detalhes:
typedef void (*sig_t) (int);
sig_t signal(int sig, sig_t func);
O primeiro parâmetro é o sinal de destino. O parâmetro func é um ponteiro que aponta para uma função que trata esse sinal. Essa função de tratamento de sinal tem um parâmetro do tipo int e deve retornar void.
O parâmetro func também pode ser configurado para os seguintes valores:
SIG_IGN: Se o parâmetro func for configurado como SIG_IGN, o sinal será ignorado.
SIG_DFL: Se o parâmetro func for configurado como SIG_DFL, o sinal será tratado de acordo com o comportamento determinado.
4. Tipos possíveis de sinal sig
1) #define SIGHUP 1 /* hangup */
SIGHUP é um sinal muito usado pelos administradores de sistemas Unix. Muitos serviços de processo em segundo plano irão ler novamente seus arquivos de configuração após receberem esse sinal. No entanto, a função real desse sinal é notificar o processo de que seu terminal de controle foi desligado. O comportamento padrão é terminar o processo.
2) #define SIGINT 2 /* interrupt */
Para os usuários do Unix, SIGINT é outro sinal comum. Muitos shells CTRL-composição em C torna este sinal conhecido. O nome oficial deste sinal é sinal de interrupção. O comportamento padrão é terminar o processo.
3) #define SIGQUIT 3 /* sair */
O sinal SIGQUIT é usado para receber o CTRL-/composição. Além disso, ele também é usado para informar a saída do processo. Este é um sinal comum usado para notificar o aplicativo de fechar de maneira suave (notação: executar algumas ações de saída antes de encerrar). O comportamento padrão é terminar o processo e criar um dump de núcleo.
4) #define SIGILL 4 /* instrução ilegal (não resetada quando capturada) */
Se o processo em execução contiver instruções ilegais, o sistema operacional enviará o sinal SIGILL para o processo. Se seu programa usar threads ou funções de ponteiros, tente capturar este sinal, se possível, para ajudar na depuração. (Atenção: A frase original é: "Se seu programa fizer uso de threads ou funções de ponteiros, tente capturar este sinal, se possível, para ajudar na depuração.". As duas palavras "use of use of" podem ser um erro de formatação do livro ou eu realmente não entendi o significado; Além disso, ouço frequentemente falar de "functions pointer", para "pointer functions", o google diz que é uma coisa do fortran, de qualquer forma, realmente não sei o significado exato, por favor, corrijam se souberem./comportamento padrão é terminar o processo e criar um dump de núcleo.
5) #define SIGTRAP 5 /* trapa de rastreamento (não resetada quando capturada) */
SIGTRAP é um sinal definido pelo padrão POSIX, usado para fins de depuração. Quando um processo sendo depurado recebe este sinal, significa que ele atingiu um ponto de interrupção de depuração. Assim que este sinal é entregue, o processo a ser depurado parará e seu pai será notificado. O comportamento padrão é terminar o processo e criar um dump de núcleo.
6) #define SIGABRT 6 /* abort() */
SIGABRT oferece um método para criar um dump de núcleo ao abortar um processo de maneira anormal, no entanto, se o sinal for capturado e o manipulador de sinal não retornar, o processo não será terminado. O comportamento padrão é terminar o processo e criar um dump de núcleo.
7) #define SIGFPE 8 /* exceção de ponto flutuante */
Quando um processo ocorre um erro de ponto flutuante, o sinal SIGFPE é enviado para o processo. Para programas que lidam com operações matemáticas complexas, geralmente é recomendado capturar esse sinal. O comportamento padrão é encerrar o processo e criar um dump de núcleo.
8) #define SIGKILL 9 /* kill (não pode ser capturado ou ignorado) */
SIGKILL é o sinal mais difícil de lidar desses sinais. Como você pode ver nos seus comentários adjacentes, esse sinal não pode ser capturado ou ignorado. Assim que o sinal for entregue a um processo, esse processo será terminado. No entanto, há algumas situações raras em que o SIGKILL não termina o processo. Essas raras situações ocorrem ao lidar com uma "operação não interrompível" (como disco I/O) ocorre. Embora essas situações sejam extremamente raras, uma vez que ocorrem, podem causar deadlock de processo. A única maneira de encerrar o processo é reiniciá-lo. O comportamento padrão é encerrar o processo.
9) #define SIGBUS 10 /* erro de bus */
Como seu nome sugere, o sinal SIGBUS é gerado quando o CPU detecta um erro no bus de dados. Quando um programa tenta acessar um endereço de memória não alinhado, esse sinal é gerado. O comportamento padrão é encerrar o processo e criar um dump de núcleo.
10) #define SIGSEGV 11 /* violação de segmentação */
SIGSEGV é outro sinal de C/C++Os sinais são muito conhecidos pelos programadores. Quando um programa não tem direitos de acesso a um endereço de memória protegida ou acessa um endereço de memória virtual inválido (pontos sujos, dirty pointers, nota: devido à falta de sincronização com o conteúdo do armazenamento de backup. Sobre os pontos selvagens, veja http://en.wikipedia.org/wiki/A explicação de Wild_pointer (ponto selvagem) gera esse sinal. O comportamento padrão é encerrar o processo e criar um dump de núcleo.
11) #define SIGSYS 12 /* non-chamada de sistema existente invocada */
O sinal SIGSYS é entregue quando um processo executa uma chamada de sistema inexistente. O sistema operacional entregará o sinal e o processo será terminado. O comportamento padrão é encerrar o processo e criar um dump de núcleo.
12) #define SIGPIPE 13 /* escrever em uma tubulação sem ninguém para lê-la */
O papel da tubulação é semelhante ao de um telefone, permitindo a comunicação entre processos. Se um processo tentar executar uma operação de escrita na tubulação e, no entanto, não houver ninguém do outro lado para responder, o sistema operacional entregará o sinal SIGPIPE a esse processo irritante (aqui é o processo que pretendia escrever). O comportamento padrão é encerrar o processo.
13) #define SIGALRM 14 /* ) #define SIGALRM */
relógio de alarme
ao expirar o temporizador do processo, o sinal SIGALRM será entregue ao processo. Esses temporizadores serão mencionados mais à frente no capítulo.
14definidos por setitimer e alarm. O comportamento padrão é encerrar o processo. 15 /* ) #define SIGTERM */
sinal de terminação de software do comando kill
15O sinal SIGTERM é enviado para o processo, notificando o processo de que é hora de encerrar e fazer algumas atividades de limpeza antes de encerrar. O sinal SIGTERM é o sinal padrão do comando kill do Unix, e também é o sinal padrão enviado pelo sistema operacional para o processo ao fechar. O comportamento padrão é encerrar o processo. 16 /* ) #define SIGURG */
condição urgente no canal de E/S
16Em alguns casos, quando ocorre algo em um socket aberto pelo processo, o sinal SIGURG será enviado para o processo. Se o processo não capturar esse sinal, ele será descartado. O comportamento padrão é descartar o sinal. 17 /* ) #define SIGSTOP */
sinal de parada enviável não da tty
O sinal não pode ser capturado ou ignorado. Assim que o processo receber o sinal SIGSTOP, ele parará imediatamente (stop) até receber outro SIGCONT
17) #define SIGTSTP 18 /* sinal de parada da tty */
SIGSTP é semelhante a SIGSTOP, mas a diferença é que o sinal SIGSTP pode ser capturado ou ignorado. Quando o shell recebe CTRL-Quando Z for atingida, esse sinal será entregue ao processo. O comportamento padrão é parar o processo até que seja recebido um sinal SIGCONT.
18) #define SIGCONT 19 /* continuar um processo parado */
SIGCONT é um sinal interessante. Como mencionado anteriormente, quando um processo é interrompido, esse sinal é usado para informar o processo de retomar a execução. O que é interessante sobre esse sinal é que ele não pode ser ignorado ou bloqueado, mas pode ser capturado. Isso tem um significado: porque um processo provavelmente não deseja ignorar ou bloquear o sinal SIGCONT, o que faria se o processo recebesse um SIGSTOP ou SIGSTP? O comportamento padrão é descartar o sinal.
19) #define SIGCHLD 20 /* para o pai quando o filho para ou sai */
SIGCHLD foi introduzido pelo Unix de Berkeley e é melhor que o SRV 4 A implementação no Unix é melhor em termos de interface. (Se o sinal não for um processo sem capacidade de retroação, a implementação do sinal SIGCHID no BSD é melhor. Na implementação do Unix System V, se o processo requer a captura desse sinal, o sistema operacional verifica se há qualquer subprocesso não concluído (esses subprocessos já saíram do exit) cujos filhos estão esperando por um chamada wait do pai para coletar seus estados). Se o subprocesso sair com algumas informações de terminação, o gerenciador de sinais será chamado. Portanto, simplesmente exigir a captura desse sinal levará ao chamado do gerenciador de sinais (nota de tradução: isso é o que se chama de 'capacidade de retroação do sinal'), o que é uma situação bastante confusa. Assim que o estado de um subprocesso de um processo muda, o sinal SIGCHLD é enviado para o processo. Como mencionei em capítulos anteriores, embora o pai possa fork subprocessos, não é necessário esperar que eles saiam. Geralmente, isso não é muito bom, porque, uma vez que o processo sair, pode se tornar um processo zumbi. No entanto, se o pai capturar o sinal SIGCHLD, ele pode usar uma chamada da série wait para coletar o estado do subprocesso ou determinar o que aconteceu. Quando o sinal SIGSTOP, SIGSTP ou SIGCONF é enviado para o subprocesso, o sinal SIGCHLD também é enviado para o pai. O comportamento padrão é descartar o sinal.
20) #define SIGTTIN 21 /* para leitores pgrp no tty em segundo plano */
Quando um processo em segundo plano tenta realizar uma operação de leitura, o sinal SIGTTIN é enviado para o processo. O processo ficará bloqueado até receber o sinal SIGCONT. O comportamento padrão é parar o processo até que receba o sinal SIGCONT.
21) #define SIGTTOU 22 /* como TTIN se (tp->t_local<OSTOP) */
O sinal SIGTTOU é semelhante ao SIGTTIN, a diferença está no fato de que o sinal SIGTTOU é gerado quando um processo em segundo plano tenta executar uma operação de escrita em um tty que foi configurado com a propriedade TOSTOP. No entanto, se o tty não tiver essa propriedade configurada, o sinal SIGTTOU não será enviado. O comportamento padrão é parar o processo até que receba o sinal SIGCONT.
22) #define SIGIO 23 /* input/output possible signal */
如果进程在一个文件描述符上有I/O操作的话,SIGIO信号将被发送给这个进程。进程可以通过fcntl调用来设置。缺省行为是丢弃该信号。
23) #define SIGXCPU 24 /* exceeded CPU time limit */
如果一旦进程超出了它可以使用的CPU限制(CPU limit),SIGXCPU信号就被发送给它。这个限制可以使用随后讨论的setrlimit设置。缺省行为是终止进程。
24) #define SIGXFSZ 25 /* exceeded file size limit */
如果一旦进程超出了它可以使用的文件大小限制,SIGXFSZ信号就被发送给它。稍后我们会继续讨论这个信号。缺省行为是终止进程。
25) #define SIGVTALRM 26 /* virtual time alarm */
如果一旦进程超过了它设定的虚拟计时器计数时,SIGVTALRM信号就被发送给它。缺省行为是终止进程。
26) #define SIGPROF 27 /* profiling time alarm */
当设置了计时器时,SIGPROF是另一个将会发送给进程的信号。缺省行为是终止进程。
27) #define SIGWINCH 28 /* window size changes */
当进程调整了终端的行或列时(比如增大你的xterm的尺寸),SIGWINCH信号被发送给该进程。缺省行为是丢弃该信号。
28) #define SIGUSR1 29 /* user defined signal 1 */
29) #define SIGUSR2 30 /* user defined signal 2 */
SIGUSR1和SIGUSR2这两个信号被设计为用户指定。它们可以被设定来完成你的任何需要。换句话说,操作系统没有任何行为与这两个信号关联。缺省行为是终止进程。(译注:按原文的意思翻译出来似乎这两句话有点矛盾。)
5. 例子
5.1. Linux下的Ctrl+C在Windows下的实现一
Linux下通常的做法:
signal(SIGINT, sigfunc); // 设置信号 void sigfunc(int signo) { ... //处理信号相关的操作 }
以下是Linux下的Ctrl+C在Windows下的实现
#include <stdio.h> #include <windows.h> static is_loop = 1; // 捕获控制台Ctrl+C事件的函数 BOOL CtrlHandler( DWORD fdwCtrlType ) { switch (fdwCtrlType) { /* Manuseie o CTRL-Sinal C */ case EVENTO_DE_C: printf("EVENTO_DE_C \n"); break; case EVENTO_DE_FECHAMENTO: printf("EVENTO_DE_FECHAMENTO \n"); break; case EVENTO_DE_SINAL_DE_BRAKE: printf("EVENTO_DE_SINAL_DE_BRAKE \n"); break; case EVENTO_DE_LOGOFF: printf("EVENTO_DE_LOGOFF \n"); break; case EVENTO_DE_SINALIZAÇÃO_DE_SISTEMA: printf("EVENTO_DE_SINALIZAÇÃO_DE_SISTEMA \n"); break; default: return FALSE; } is_loop = 0; return (TRUE); } int main(int argc, char *argv[]) { printf("Definir Manipulador de Console Ctrl\n"); SetConsoleCtrlHandler((PHANDLER_ROUTINE)CtrlHandler, TRUE); while (is_loop); return 0; }
5.2no Linux+Implementação de C no Windows
#include <stdio.h> #include <windows.h> #define CONTRL_C_HANDLE() signal(3, exit) int main(int argc, char *argv[]) { printf("Definir Manipulador de Console Ctrl\n"); CONTRL_C_HANDLE(); while (1; system("PAUSE"); return 0; }
Isso é tudo o que o editor trouxe para você sobre uma breve discussão do uso de várias funções de agendamento no Linux. Esperamos que você apóie e clamore pelo tutorial!