一.上集回顾
建议先学上篇博客,再向下学习,上篇博客的链接如下:




将系统调用号,放到寄存器中,然后触发系统调用(int $0x80),然后自动查表,执行对应方法
本质上系统调用就是为了提供安全的访问环境
二.内核态和用户态
1.映射


在用户区这一部分,我们可以直接进行使用,不用任何的系统调用


我们根本就不关系系统调用的虚拟地址,我们直接在用户区调用系统调用号,这样可以了

无论多少的进程,我们对应的[3,4DB]的空间都是一样的

如果我们的不同进程的[3,4GB]区域映射的是不一样的区域,那么我们电脑里面可以存在不太的操作系统
不管是通过哪一个进程的地址空间,进入内核,都是通过软中断进入操作系统的

只有代码段地址的CPL的值是对应的0,才允许我们做[3,4GB]的映射

进入内核态之后,操作系统就直接拿寄存器的值了,就直接进行找对应的系统调用表了


所以我们的操作系统是一直在用户态和内核态进行切换的(时钟中断)
2.内核态和用户态的切换

a.信号捕捉方法执行的时候,为啥要做权限的切换呢?直接内核执行不就完了吗?
有安全风险,因为你不知道sighandler()处理函数里面是否有其他的非法操作(删除用户等),所以要从内核态切换回用户态,所有的风险操作都要交由用户来执行
b.为啥sighandler处理完之后,不直接返回main,还要再返回内核再进行返回main?

main函数和sighandler不一定有系统调用关系,而main和系统调用有调用关系,所以必须先返回内核,再进入我们的main

三.信号捕捉的操作



cpp
#include <iostream>
#include <signal.h>
#include <unistd.h>
void handler(int signo)
{
std::cout << "get a sig: " << signo << std::endl;
exit(1);
}
int main()
{
struct sigaction act,oact;
act.sa_handler = handler;
::sigaction(2,&act,&oact);
while(true)
{
pause();
}
return 0;
}


这里也符合我们的预期
//上面只填充了我们对应的handler字段,那我们对应的sa_mask字段呢?

我们这里将代码进行修改一下:
cpp
#include <iostream>
#include <signal.h>
#include <unistd.h>
void handler(int signo)
{
static int cnt = 0;
cnt++;
while(true)
{
std::cout << "get a sig: " << signo << ", cnt: " << cnt << std::endl;
sleep(1);
}
exit(1);
}
int main()
{
struct sigaction act,oact;
act.sa_handler = handler;
::sigaction(2,&act,&oact);
while(true)
{
pause();
}
return 0;
}



代码证明,如下:
cpp
#include <iostream>
#include <signal.h>
#include <unistd.h>
void PrintBlock()
{
sigset_t set,oset;
sigemptyset(&set);
sigemptyset(&oset);
sigprocmask(SIG_BLOCK,&set,&oset);
std::cout << "block: ";
for(int signo = 31;signo > 0;signo--)
{
if(sigismember(&oset,signo))
{
std::cout << 1;
}
else
{
std::cout << 0;
}
}
std::cout << std::endl;
}
void handler(int signo)
{
static int cnt = 0;
cnt++;
while(true)
{
std::cout << "get a sig: " << signo << ", cnt: " << cnt << std::endl;
PrintBlock();
sleep(1);
}
exit(1);
}
int main()
{
struct sigaction act,oact;
act.sa_handler = handler;
::sigaction(2,&act,&oact);
while(true)
{
PrintBlock();
pause();
}
return 0;
}

那我们只想自定义屏蔽我们对应的信号呢?

cpp
#include <iostream>
#include <signal.h>
#include <unistd.h>
void PrintBlock()
{
sigset_t set,oset;
sigemptyset(&set);
sigemptyset(&oset);
sigprocmask(SIG_BLOCK,&set,&oset);
std::cout << "block: ";
for(int signo = 31;signo > 0;signo--)
{
if(sigismember(&oset,signo))
{
std::cout << 1;
}
else
{
std::cout << 0;
}
}
std::cout << std::endl;
}
void handler(int signo)
{
static int cnt = 0;
cnt++;
while(true)
{
std::cout << "get a sig: " << signo << ", cnt: " << cnt << std::endl;
PrintBlock();
sleep(1);
}
exit(1);
}
int main()
{
struct sigaction act,oact;
act.sa_handler = handler;
sigemptyset(&act.sa_mask);
sigaddset(&act.sa_mask,3);
sigaddset(&act.sa_mask,4);
sigaddset(&act.sa_mask,5);
sigaddset(&act.sa_mask,6);
sigaddset(&act.sa_mask,7);
::sigaction(2,&act,&oact);
while(true)
{
PrintBlock();
pause();
}
return 0;
}


信号结束之后,屏蔽信号又会变回去
cpp
#include <iostream>
#include <signal.h>
#include <unistd.h>
void PrintBlock()
{
sigset_t set,oset;
sigemptyset(&set);
sigemptyset(&oset);
sigprocmask(SIG_BLOCK,&set,&oset);
std::cout << "block: ";
for(int signo = 31;signo > 0;signo--)
{
if(sigismember(&oset,signo))
{
std::cout << 1;
}
else
{
std::cout << 0;
}
}
std::cout << std::endl;
}
void handler(int signo)
{
static int cnt = 0;
cnt++;
while(true)
{
std::cout << "get a sig: " << signo << ", cnt: " << cnt << std::endl;
PrintBlock();
sleep(1);
break;
}
}
int main()
{
struct sigaction act,oact;
act.sa_handler = handler;
sigemptyset(&act.sa_mask);
sigaddset(&act.sa_mask,3);
sigaddset(&act.sa_mask,4);
sigaddset(&act.sa_mask,5);
sigaddset(&act.sa_mask,6);
sigaddset(&act.sa_mask,7);
::sigaction(2,&act,&oact);
while(true)
{
PrintBlock();
pause();
}
return 0;
}

pending位图的1是执行完之前制成0,还是执行之后再清0呢?
执行之前就清0,执行之后在清理,我们就无法区分是之后来的,还是之前没有处理干净的
cpp
#include <iostream>
#include <signal.h>
#include <unistd.h>
void PrintBlock()
{
sigset_t set,oset;
sigemptyset(&set);
sigemptyset(&oset);
sigprocmask(SIG_BLOCK,&set,&oset);
std::cout << "block: ";
for(int signo = 31;signo > 0;signo--)
{
if(sigismember(&oset,signo))
{
std::cout << 1;
}
else
{
std::cout << 0;
}
}
std::cout << std::endl;
}
void PrintPending()
{
sigset_t pending;
::sigpending(&pending);
std::cout << "Pending: ";
for(int signo = 31;signo > 0;signo--)
{
if(sigismember(&pending,signo))
{
std::cout << 1;
}
else
{
std::cout << 0;
}
}
std::cout << std::endl;
}
void handler(int signo)
{
static int cnt = 0;
cnt++;
while(true)
{
std::cout << "get a sig: " << signo << ", cnt: " << cnt << std::endl;
// PrintBlock();
PrintPending();
sleep(1);
break;
}
}
int main()
{
struct sigaction act,oact;
act.sa_handler = handler;
sigemptyset(&act.sa_mask);
sigaddset(&act.sa_mask,3);
sigaddset(&act.sa_mask,4);
sigaddset(&act.sa_mask,5);
sigaddset(&act.sa_mask,6);
sigaddset(&act.sa_mask,7);
::sigaction(2,&act,&oact);
while(true)
{
// PrintBlock();
PrintPending();
pause();
}
return 0;
}

四.可重入函数





stl是不能进行重入的

五.volatile
cpp
#include <iostream>
#include <signal.h>
#include <unistd.h>
int flag = 0;
void change(int signo)//信号捕捉的执行流
{
(void)signo;
flag = 1;
printf("change flag 0->1, getpid: %d\n",getpid());
}
int main()
{
printf("I am main process, pid is : %d\n",getpid());
signal(2,change);
while(!flag)//主执行流
{
}
printf("我是正常退出的!\n");
return 0;
}

如果我们进行代码优化




所以他一直用的就是我们对应的寄存器中的值,内存中的值进行修改之后,但是寄存器中的值还没进行修改

cpp
#include <iostream>
#include <signal.h>
#include <unistd.h>
volatile int flag = 0;
void change(int signo)//信号捕捉的执行流
{
(void)signo;
flag = 1;
printf("change flag 0->1, getpid: %d\n",getpid());
}
int main()
{
printf("I am main process, pid is : %d\n",getpid());
signal(2,change);
while(!flag)//主执行流
{
}
printf("我是正常退出的!\n");
return 0;
}


六.SIGCHLD信号

cpp
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h> // 注意:原代码中使用了sleep函数,需包含此头文件
#include <wait.h>
void handler(int sig)
{
pid_t id;
while( (id = waitpid(-1, NULL, WNOHANG)) > 0) {
printf("wait child success: %d\n", id);
}
printf("child is quit! %d\n", getpid());
}
int main()
{
signal(SIGCHLD, handler);
pid_t cid;
if((cid = fork()) == 0){//child
printf("child : %d\n", getpid());
sleep(3);
exit(1);
}
while(1){
printf("father proc is doing some thing!\n");
sleep(1);
}
return 0;
}


