学习linux系统编程其实就是学习一对API函数,掌握一些进程线程网络的概念,为linux服务器编程和linux网络编程打基础。
本人也是linux小白,所以想记录一下学习过程中的想法:
相关知识点在代码下面的注释中
若我写的难以理解,可以参考那位博主的链接:已附在本博文最下面
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define ERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
}while(0)
void myHandler_forsigaction(int signum, siginfo_t *s_t, void *p)
{
int myint = 0;
printf("recv signum: %d \n", signum);
myint = s_t->si_value.sival_int;
printf("%d %d\n", myint, s_t->si_int);
}
int main()
{
pid_t pid;
int ret = 0;
struct sigaction act;
act.sa_sigaction = myHandler_forsigaction;
sigemptyset(&act.sa_mask);
act.sa_flags = SA_SIGINFO;
if (sigaction(SIGINT, &act, NULL) < 0)
{
ERR_EXIT("sigaction error");
}
pid = fork();
if (pid == -1)
{
printf("fork err.....\n");
return 0;
}
if (pid == 0)
{
int i = 0;
union sigval mysigval;
mysigval.sival_int = 222;
for (i = 0; i < 10; i++)
{
ret = sigqueue(getppid(), SIGINT, mysigval);
if (ret != 0)
{
printf("sigqueue err....\n");
exit(0);
}
else
{
printf("sigqueue success....\n");
sleep(2);
}
}
}
else if (pid > 0)
{
for(;;)
pause();
}
return 0;
}
/****************************************************************************************
1、sigaction函数
#include
int sigaction(int signo, const struct sigaction *act, struct sigaction *oact);
sigaction函数可以读取和修改与指定信号相关联的处理动作。调用成功则返回0,出错则返回-1。
signo是指定信号的编号。若act指针非空,则根据act修改该信号的处理动作。
若oact指针非空,则通过oact传出该信号原来的处理动作。
act和oact指向sigaction结构体:
struct sigaction {
void (*sa_handler)(int);
void (*sa_sigaction)(int, siginfo_t *, void *);
sigset_t sa_mask;
int sa_flags;
void (*sa_restorer)(void);
};
void (*sa_handler)(int);
回调函数(带一个int参数),使用方法:
act.sa_handler = myHandler; //myHandler是自定义信号处理函数
igaction(SIGINT, &act, NULL);//相当于signal(SIGINT, myHandler); 其实signal函数内部就是由igaction实现的
void (*sa_sigaction)(int, siginfo_t *, void *);
回调函数,除了可以定义信号处理函数,还可以接受信号的额外信息,与sigqueue搭配着用
注意:当用户需要用第二个参数接受信号额外信息时,需设置sa_flags: act.sa_flags = SA_SIGINFO;
参数说明:第一个参数与sa_handler一样表示当前信号的编号,
第二个参数是一个siginfo_t 结构体,
第三个参数一般不用。
当使用sa_handler时sa_flags设置为0即可
注意:sa_handler 与 sa_sigaction不要同时使用
使用方法:
act.sa_sigaction = myHandler_forsigaction; //myHandler是自定义信号处理函数
//void myHandler_forsigaction(int signum, siginfo_t *s_t, void *p)
// s_t为 siginfo_t结构体,里面包含信号的额外信息(要想信号包含额外信息,需要用sigqueue函数发送信号)
siginfo_t 结构体
siginfo_t {
int si_signo; Signal number
int si_errno; An errno value
int si_code; Signal code
int si_trapno; Trap number that caused
hardware-generated signal
(unused on most architectures)
pid_t si_pid; Sending process ID
uid_t si_uid; Real user ID of sending process
int si_status; Exit value or signal
clock_t si_utime; User time consumed
clock_t si_stime; System time consumed
sigval_t si_value; Signal value //常用!!!
int si_int; POSIX.1b signal //常用!!!
void *si_ptr; POSIX.1b signal
int si_overrun; Timer overrun count; POSIX.1b timers
int si_timerid; Timer ID; POSIX.1b timers
void *si_addr; Memory location which caused fault
long si_band; Band event (was int in
glibc 2.3.2 and earlier)
int si_fd; File descriptor
short si_addr_lsb; Least significant bit of address
(since kernel 2.6.32)
}
sigset_t sa_mask;
当某个信号的处理函数被调用时,内核自动将当前信号加入进程的信号屏蔽字,
当信号处理函数返回时自动恢复原来的信号屏蔽字,这样就保证了在处理某个信号时,
如果这种信号再次产生,那么它会被阻塞到当前处理结束为止。如果在调用信号处理函数时,
除了当前信号被自动屏蔽之外,还希望自动屏蔽另外一些信号,
则用sa_mask字段说明这些需要额外屏蔽的信号,
当信号处理函数返回时自动恢复原来的信号屏蔽字。
int sa_flags;
sa_flags有几个选项,比较重要的有两个:SA_NODEFER 和 SA_SIGINFO
当SA_NODEFER设置时在信号处理函数执行期间不会屏蔽当前信号;
当SA_SIGINFO设置时与sa_sigaction 搭配出现,说明该进程要接受信号的附带信息
当使用sa_handler时sa_flags设置为0即可
void (*sa_restorer)(void);
已经被废弃不用
2、
sigqueue函数
int sigqueue(pid_t pid, int sig, const union sigval value);
新的发送信号系统调用(与kill类似),主要是针对实时信号提出的支持信号带有参数,与函数sigaction()配合使用。
参数说明:
第一个参数是指定接收信号的进程id,
第二个参数确定即将发送的信号,
第三个参数是一个联合数据结构union sigval,该结构体包含了改信号的额外信息
返回值:成功返回0,失败返回-1
union sigval结构体:
typedef union sigval
{
int sival_int;
void *sival_ptr;
}sigval_t;
int sival_int;
sival_int的值将被接受信号的进程 sigaction结构体中的 sa_sigaction回调函数的
第二个参数:siginfo_t 结构体中的si_value.sival_int 成员和 si_int成员接受
****************************************************************************************/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define ERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
} while(0)
void myhandle(int sig)
{
if (sig == SIGINT || sig == SIGRTMIN)
printf("recv a sig=%d\n", sig);
else if (sig == SIGUSR1)
{
sigset_t s;
sigemptyset(&s);
sigaddset(&s, SIGINT);
sigaddset(&s, SIGRTMIN);
sigprocmask(SIG_UNBLOCK, &s, NULL);
}
}
void main()
{
pid_t pid;
struct sigaction act;
act.sa_handler = myhandle;
act.sa_flags = 0;
//注册信号处理函数
if ( sigaction(SIGINT, &act, NULL) <0 )
{
ERR_EXIT("sigaction SIGINT");
}
if ( sigaction(SIGRTMIN, &act, NULL) <0 )
{
ERR_EXIT("sigaction SIGINT");
}
if ( sigaction(SIGUSR1, &act, NULL) <0 )
{
ERR_EXIT("sigaction SIGINT");
}
sigset_t bset;
sigemptyset(&bset);
sigaddset(&bset, SIGINT);
sigaddset(&bset, SIGRTMIN);
sigprocmask(SIG_BLOCK, &bset, NULL);
pid = fork();
if (pid == -1)
{
ERR_EXIT("fork err");
}
//子进程向父进程发送信号
if (pid == 0)
{
int i = 0;
int ret = 0;
union sigval v;
v.sival_int = 201;
//发送非实时信号 SIGINT
for (i=0; i<3; i++)
{
ret = sigqueue(getppid(), SIGINT, v);
if (ret == -1)
{
printf("sigqueue SIGINT err, ret: %d, errno:%d \n", ret, errno);
exit(0);
}
else
{
printf("sigqueue SIGINT success\n");
}
}
//发送实时信号 SIGRTMIN
v.sival_int = 301;
for (i=0; i<3; i++)
{
ret = sigqueue(getppid(), SIGRTMIN, v);
if (ret == -1)
{
printf("sigqueue SIGRTMIN err, ret: %d, errno:%d \n", ret, errno);
exit(0);
}
printf("sigqueue SIGRTMIN success\n");
}
//向父进程发送 SIGUSR1信号,接触阻塞
kill(getppid(), SIGUSR1);
}
while(1)
{
sleep(1);
}
printf("main....\n");
}
/*******************************************************************************************
输出结果:
sigqueue SIGINT success
sigqueue SIGINT success
sigqueue SIGINT success
sigqueue SIGRTMIN success
sigqueue SIGRTMIN success
sigqueue SIGRTMIN success
recv a sig=2
recv a sig=34
recv a sig=34
recv a sig=34
分析:
1、实时信号(可靠信号)和非实时信号(不可靠信号)说明区别:
实时信号支持排队不会丢失。(实时信号还有一个特点,即到达的顺序是可以保证的)
2、在主函数中将SIGINT和SIGRTMIN信号加入信号屏蔽字,只有当接收到SIGUSR1信号时才对前面两个信号unblock。
3、需要注意的是:
如果在信号处理函数中对某个信号进行解除阻塞时,则只是将pending位清0,
让此信号递达一次(同个实时信号产生多次进行排队都会抵达),
但不会将block位清0,即再次产生此信号时还是会被阻塞,处于未决状态。
4、
在子进程中连续向父进程各发送了SIGINT和SIGRTMIN信号3次,接着睡使用kill函数发送SIGUSR1信号给父进程
由输出结果可知:实时信号支持排队,3个信号都接收到了,而不可靠信号不支持排队,只保留一个信号。
*******************************************************************************************/
linux信号捕捉和sigaction函数和sigqueue函数的详细讲解