Desenvolvimento remoto Linux - função de biblioteca sigaction() (envio e recebimento de sinal com parâmetros)

contente

I. Introdução

Dois, sigaction () introdução da função de biblioteca

1. Verifique o manual da função signation()

2. protótipo da função sigaction()

3. estrutura de sigaction

Três, introdução da função sigqueue()

1. Função

2. Protótipo de função

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

3. Função principal

Cinco, blindagem de sinal

Funções de manipulação do conjunto de sinais

função sigprocmask()

teste de código

6. Conflito de sinal

7. Resumo

Vantagens do sinal

Desvantagens do Sinal


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).

Acho que você gosta

Origin blog.csdn.net/wmcy123/article/details/123604822
Recomendado
Clasificación