contente
Dois, sigaction () introdução da função de biblioteca
1. Verifique o manual da função signation()
2. protótipo da função sigaction()
Três, introdução da função sigqueue()
3. sigval de união da estrutura de dados de união
Quarto, o teste de transferência de dados entre processos
1. Encapsular funções para vinculação
2. Arquivos de cabeçalho usados
Funções de manipulação do conjunto de sinais
I. Introdução
Embora o segmento de código, segmento de dados e segmento de pilha sejam compartilhados entre o processo filho e o processo pai aberto por fork(), os dados entre os processos pai e filho não são compartilhados, ou seja , a transmissão de dados não pode ser realizada . Existem duas formas de comunicação IPC entre processos, uma é a vinculação de signal sigaction () com parâmetros, e o sinal é enviado através de sigqueue() . A outra é a ligação signal signal() sem argumentos, que envia o sinal via kill() .
Com parâmetros, significa que a transmissão do sinal também pode enviar um tipo de dados int, ou seja, para realizar a transferência de dados entre os processos pai e filho. Sinais com parâmetros podem passar dados do tipo int, enquanto sinais sem parâmetros não podem passar dados. Este artigo apresenta a ligação signal sigaction() com parâmetros e o envio de sinal sigqueue(), para verificar se os dados do tipo int são passados entre os dois processos.
Este artigo descreve a ligação signal sigaction() com parâmetros para enviar sinais por meio de sigqueue() .
Dois, sigaction () introdução da função de biblioteca
1. Verifique o manual da função signation()
Digite man sigaction no terminal em linux para visualizar o protótipo da função e a introdução relacionada da função.
2. protótipo da função sigaction()
A função sigaction() é utilizada para alterar o comportamento de um processo após receber um sinal específico.
arquivo de cabeça:
#include <sinal.h>
Protótipo de função:
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
parâmetro:
signum: qualquer sinal válido específico, exceto sigkill e sigstop
act: struct sigaction estrutura ponteiro
oldact: pode ser especificado como NULL
valor de retorno:
A função retorna 0 em caso de sucesso e -1 em caso de falha
3. estrutura de sigaction
Os dois primeiros tipos de dados da estrutura são ponteiros de função , não funções, e não destruirão a estrutura da estrutura. sa_flags é um bit de sinalização Quando sa_flags= SA_SIGINFO , o sinal é especificado com parâmetros e quando sa_flags= 0 , o sinal é especificado sem parâmetros.
Protótipo da estrutura:
struct sigaction { void (*sa_handler)(int); // ponteiro de função void (*sa_sigaction)(int, siginfo_t *, void *); // ponteiro de função sigset_t sa_mask; //sinal definido int sa_flags; // sinalizador de bit void (*sa_restaurador)(void); };
Três, introdução da função sigqueue()
1. Função
A função sigqueue() é uma nova chamada de sistema para envio de sinais, principalmente para os sinais de suporte propostos para sinais em tempo real com parâmetros e usados em conjunto com a função sigaction().
sigaction(): vincula o sinal à função.
sigqueue(): Após enviar um sinal com parâmetros, a função que executa a vinculação do sinal.
2. Protótipo de função
arquivo de cabeça:
#include <sinal.h>
Protótipo de função:
int sigqueue(pid_t pid, int sig, const union sigval value);
parâmetro:
pid: especifica o id do processo que recebe o sinal
sig: sinal enviado
valor: união estrutura de dados união sigval, especificando os parâmetros passados pelo sinal
valor de retorno:
Retorna 0 em caso de sucesso, -1 em caso de falha
3. sigval de união da estrutura de dados de união
union sigval A estrutura de dados union especifica os parâmetros da transmissão do sinal, ou seja, salva um tipo de dado int por meio de sival_int e passa o tipo de dado int por meio da função sigqueue.
Embora a transferência de dados entre processos seja possível, apenas dados do tipo int podem ser transferidos. Como a função de sival_ptr não foi desenvolvida, essa transferência de dados ainda é relativamente insípida.
Protótipo da estrutura:
union sigval { int sival_int; //salva dados do tipo int para transferência de dados void *sival_ptr; // salva ponteiros de vários tipos, mas este método não foi desenvolvido };
Quarto, o teste de transferência de dados entre processos
1. Encapsular funções para vinculação
Primeiro, encapsule uma função para sigaction() para vincular o sinal.
void signal_function(int signum, siginfo_t* info, void*);
void signal_function(int signum, siginfo_t* info, void*)
{
int x = info->si_int;
cout << "pid = " << getpid() << " 函数被调用了 signum = " << signum << "传递的信息info = " << x << endl;
}
2. Arquivos de cabeçalho usados
#include <iostream>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
3. Função principal
Teste se o parâmetro do sinal enviado pelo processo pai salva os dados inteiros 12345 e se o processo filho pode receber esses dados.
int main()
{
//带参数的信号
int pid = 0;
int status = 0;
struct sigaction act;
act.sa_sigaction = signal_function;//保存函数指针
act.sa_flags = SA_SIGINFO;//标志位,指定信号是带参数的
//带参数的信号绑定
sigaction(SIGRTMIN, &act, NULL);
pid = fork();
if (pid == 0)//子进程
{
while (1)
{
cout << "子进程运行中 pid = " << getpid() << endl;
sleep(1);
}
}
else if (pid > 0)//父进程
{
sleep(3);//先让子进程先处理业务,测试sigqueue函数
union sigval value;//联合结构体
value.sival_int = 12345; //保存传递的int类型的数据
for (int i = 1; i <= 5; i++)
{
cout << "父进程给子进程发送第 " << i << " 个信号" << endl;
//带参数发送信号
sigqueue(pid, SIGRTMIN, value);
}
sleep(3);//延时3秒
kill(pid, SIGKILL);//给子进程发送停止信号
waitpid(pid, &status, 0);//等待子进程结束,防止子进程托孤
cout << "子进程结束" << endl;
}
return 0;
}
Os resultados são os seguintes:
Pode-se verificar que a função onde o processo filho está localizado recebeu o sinal com parâmetros enviados pelo processo pai, e os dados 12345 salvos pelo parâmetro também são passados para o processo filho, ou seja, a transferência de dados entre o processo pai e os processos filho são bem-sucedidos.
Cinco, blindagem de sinal
Quando um processo recebe um sinal, o sinal fará com que o processo seja interrompido. Se o processo reconhecer o sinal, ele fará a função vinculada. Se não o reconhecer, o processo será encerrado.
Então, como evitá-lo? Neste momento, um sigset_t (conjunto de sinais) é necessário para blindar os sinais armazenados no conjunto de sinais, para que o processo não seja finalizado devido ao sinal aleatório, mas a premissa é que o sinal aleatório esteja no conjunto de sinais blindado. .
Funções de manipulação do conjunto de sinais
função | efeito |
int sigemptyset(sigset_t *set); | conjunto de sinal claro |
int sigfillset(sigset_t *set); | adicionar todos os sinais |
int sigaddset(sigset_t *set, int sinal); | aumentar o sinal |
int sigdelset(sigset_t *set, int sinal); | excluir um sinal de um conjunto de sinais |
int sigismember(const sigset_t *set, int sinal); | Se o sinal está no conjunto |
função sigprocmask()
Protótipo de função:
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
parâmetro:
como significado:
SIG_BLOCK set contém o sinal que queremos adicionar à máscara de sinal atual, equivalente a mask=mask|set (operação de bit OR)
O conjunto SIG_UNBLOCK contém o sinal que queremos desbloquear da máscara de sinal atual, equivalente a mask=mask^set (operação de bit XOR)
SIG_SETMASK Configura a palavra de máscara de sinal atual para o valor apontado por set, que é equivalente a mask=set
conjunto e conjunto antigo:
Se oldset for um ponteiro não nulo, a palavra de status de mascaramento de sinal atual do processo de leitura é passada através do parâmetro oldset.
Se set for um ponteiro não nulo, a palavra de status da máscara de sinal do processo é alterada e o parâmetro como é apenas como alterar.
Se oldset e set forem ponteiros não nulos, primeiro faça backup da palavra de máscara de sinal original para oldset e, em seguida, altere a palavra de máscara de sinal de acordo com os parâmetros set e how.
valor de retorno:
Retorna 0 em caso de sucesso, -1 em caso de erro
teste de código
Vamos testar a blindagem de sinal através de um pedaço de código
#include <iostream>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
using namespace std;
int main()
{
//准备两个信号集()
sigset_t arry;//存放需要屏蔽的信号
sigset_t temp_arry;//存放未决信号
//信号集初始化
sigemptyset(&arry);
sigemptyset(&temp_arry);
//添加屏蔽的信号
sigaddset(&arry, SIGUSR1);//屏蔽第10个信号
if (sigprocmask(SIG_BLOCK, &arry, NULL) < 0) //更改进程的信号屏蔽字
{
perror("signal error");
exit(0);
}
while(1)
{
cout << "程序运行中 pid = " << getpid() << endl;
sleep(3);
sigpending(&temp_arry);//读取当前进程的未决信号集
if (sigismember(&temp_arry, SIGUSR1)) //如果未决信号集里有第10个信号
{
cout << "有未决信号进来 SIGUSER1 发过来了" << endl;
sigdelset(&temp_arry, SIGUSR1);
}
}
}
Clique na barra de menus do Visual Studio: Build -> Rebuild Solution. Neste momento, as alterações feitas no código no Windows também serão sincronizadas no linux.
Encontre a pasta onde o diretório do projeto está localizado no linux, clique com o botão direito do mouse para abrir o terminal e digite: g++ main.cpp -o main
Após a compilação ser bem-sucedida, digite: ./main e execute-o
Envie um sinal para o processo principal através de kill em outro terminal
Pode-se verificar que o sinal mascarado nº 10 foi enviado no passado e entrará no conjunto de resultados pendentes, o que não afetará o processo. E no passado é enviado o sinal desmascarado nº 13. O processo não conhece este sinal e termina o processo diretamente.
Ao mesmo tempo, também confirma que, se um processo recebe um sinal desconhecido, o processo não sabe o que fazer (porque nenhuma função relacionada está vinculada) e o processo é encerrado diretamente.
6. Conflito de sinal
Suponha que o processo reconheça dois sinais (ambos os sinais estão vinculados a funções relacionadas). Quando o primeiro sinal é enviado, o processo executa a função de negócios vinculada ao sinal, e o negócio não foi concluído e o segundo sinal é enviado. Sinal . Neste momento, o negócio de função do primeiro processamento de sinal é interrompido e é transferido para o negócio de função da segunda ligação de sinal.Neste momento, ocorrerá um conflito de sinal.
Então, como resolvê-lo? Você pode vincular sinais através de sigaction(), e adicionar sinais de blindagem através de sigaddset() a funções que processam negócios por um longo tempo. Vamos testá-lo com um pedaço de código:
#include <iostream>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
using namespace std;
void test1(int i);
void test2(int i);
int main()
{
struct sigaction act1;
struct sigaction act2;
//保存函数指针
act1.sa_handler = test1;
act2.sa_handler = test2;
//指定信号不带参数
act1.sa_flags = 0;
act2.sa_flags = 0;
//信号绑定函数
sigaction(SIGUSR1, &act1, NULL);
sigaction(SIGUSR2, &act2, NULL);
//初始化信号集
sigemptyset(&(act1.sa_mask));
//屏蔽信号SIGUSR2
sigaddset(&(act1.sa_mask), SIGUSR2);
while (1)
{
cout << "进程运行中 pid = " << getpid() << endl;
sleep(2);
}
return 0;
}
void test1(int i)
{
cout << "函数 test1 被调用了 i = " << i << endl;
sleep(10);//延时10秒
cout << "函数 test1 结束了" << endl;
}
void test2(int i)
{
cout << "函数 test2 被调用了 i = " << i << endl;
}
Digite o mesmo no terminal em linux como acima: g++ main.cpp -o main
Após a compilação ser bem-sucedida, digite: ./main e execute-o
Pode-se ver que após o envio do sinal nº 10 (SIGUSER1), o sinal nº 12 (SIGUSER2) é enviado imediatamente, a função test1() não é interrompida, mas a função test2() é chamada após a função test1 () termina. , ou seja, sigaddset(&(act1.sa_mask), SIGUSR2) Este código resolve o problema de conflito de sinal.
Então comente o código sigaddset(&(act1.sa_mask), SIGUSR2) e teste novamente.
Pode-se observar que a função test2() é chamada antes do término da função test1(), ou seja, ocorre um conflito de sinal, e a função test1() é interrompida.
Nota :
Se o novo sinal e o sinal em processamento forem do mesmo tipo, eles serão enfileirados e não interromperão o negócio realizado pelo sinal anterior.
O novo sinal e o sinal em processamento são sinais heterogêneos, que interromperão o negócio realizado pelo sinal anterior.
7. Resumo
Vantagens do sinal
1. Processos e interprocessos podem desempenhar o papel de notificação.
2. A transferência de dados é possível.
Desvantagens do Sinal
1. Somente dados do tipo int podem ser passados.
2. Quando um processo recebe um sinal desconhecido, ele interrompe a execução do processo, o que precisa ser resolvido por blindagem de sinal.
3. Quando o processo estiver processando uma operação de acompanhamento de sinal e um novo sinal não tiver sido concluído, haverá um problema de conflito de sinal.
A originalidade não é fácil, por favor indique a fonte ao reimprimir.
Se for útil para você, você pode curtir, coletar + seguir, e será atualizado continuamente (hee hee).