营口工程建设信息网站,wordpress 404,昆明 做网站 vr,网页设计文献#x1f436;博主主页#xff1a;ᰔᩚ. 一怀明月ꦿ ❤️#x1f525;专栏系列#xff1a;线性代数#xff0c;C初学者入门训练#xff0c;题解C#xff0c;C的使用文章#xff0c;「初学」C#xff0c;linux #x1f525;座右铭#xff1a;“不要等到什么都没有了… 博主主页ᰔᩚ. 一怀明月ꦿ ❤️专栏系列线性代数C初学者入门训练题解CC的使用文章「初学」Clinux 座右铭“不要等到什么都没有了才下定决心去做” 大家觉不错的话就恳求大家点点关注点点小爱心指点指点 目录
核心转储
ulimit指令 信号的保存
sigset_t
sigprocmask()
动态观察指定信号的pending
sigpending
sigismember
信号的处理
sigaction
struct sigaction
信号的其他补充问题
volatile关键字
子进程退出的时候需要向父进程发送信号的SIGCHILD 核心转储 ulimit指令 ulimit 命令用于查看和修改当前用户的资源限制。Linux 系统中的资源限制指的是系统或进程所允许的最大资源使用量例如 CPU 时间、文件大小、进程数等。 使用 ulimit 命令可以查看当前用户的资源限制命令格式如下 ulimit [选项] [参数]
常用的选项包括* -a显示所有资源限制。
* -c设置或显示进程核心转储文件的大小限制。
* -f设置或显示可创建的文件的最大大小。
* -n设置或显示单个进程可以打开的文件描述符数量限制。
* -u设置或显示单个用户可以拥有的最大进程数量限制。例如要查看当前用户可以打开的最大文件描述符数量可以使用以下命令
ulimit -n
要将进程的最大文件大小限制设置为100MB可以使用以下命令
ulimit -f 100000 需要注意的是ulimit 命令只能影响当前 shell 中的进程及其子进程对于其他进程不会产生影响。如果需要在系统上全局修改资源限制需要修改系统配置文件 /etc/security/limits.conf 和 /etc/sysctl.conf 等。 当一个程序出现了严重的错误例如内存越界、除以零等导致程序无法正常继续执行时系统会将当前程序的内存映像保存到一个称为“核心转储文件core dump”的文件中以便进行后续的调试和分析。 在 Linux 系统中可以使用 ulimit 命令设置或显示进程核心转储文件的最大大小限制。通常情况下系统会将核心转储文件保存在当前目录下文件名以 core.[进程ID] 的形式命名。例如如果进程ID是1234则核心转储文件的文件名为 core.1234。 需要注意的是核心转储文件可能会包含敏感数据例如密码、私钥等因此在进行调试时需要格外小心确保不会泄露敏感信息。同时在生产环境中应该关闭核心转储功能以减少安全风险。 云服务器一般是关闭了核心转储 信号的保存 信号的处理的三种方式我们可以使用signal(signum,handler) 1. 如果 handler 为 SIG_DFL表示将信号的处理方式恢复为默认操作。 2. 如果 handler 为 SIG_IGN表示忽略该信号。 3. 如果 handler 不是 SIG_DFL 或 SIG_IGN则表示安装一个新的信号处理函数。进行自定义捕捉 忽略算不算处理了这个信号呢已经处理该信号结果就是忽略 信号从产生到递达之间的状态,称为信号未决(Pending)。 信号写入到位图时叫做信号未决 进程可以选择阻塞 (Block )某个信号。 被阻塞的信号产生时将保持在未决状态,直到进程解除对此信号的阻塞,才执行递达的动作. 未决之后暂时不进行递达直到解除对信号的阻塞 注意,阻塞和忽略是不同的,只要信号被阻塞就不会递达,而忽略是在递达之后可选的一种处理动作 进程PCB有三个字段和信号相关 pending未决位图表 handler对应信号的处理方法是一个函数指针数组数组的内容就是对应的信号处理方法 block信号阻塞表 比特位的位置表示信号编号 信号解除阻塞时将立即信号抵达 sigset_t sigset_t 是一个数据类型用于表示信号集。sigset_t 是一个位向量每个位对应一个可能的信号编号。使用 sigset_t 可以方便地管理和操作信号集合。 sigset_t 数据类型通常作为函数参数或返回值用于设置和获取进程的信号阻塞集合、修改信号处理函数的行为等。 以下是一些常用的与 sigset_t 相关的函数 1. sigemptyset()将信号集清空即将所有信号位设为 0。 2. sigfillset()将信号集填满即将所有信号位设为 1。 3. sigaddset()向信号集中添加指定的信号。 4. sigdelset()从信号集中删除指定的信号。 5. sigismember()检查指定的信号是否在信号集中。 6. sigprocmask()用于设置或获取进程的信号阻塞集合。可以阻塞或解除阻塞指定的信号。 sigprocmask() sigprocmask() 函数用于获取或设置进程的信号阻塞集合。信号阻塞集合是一组信号在阻塞期间不会递送给进程。 该函数的原型如下 int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
sigprocmask() 函数有三个参数* how表示要进行何种方式的操作可选值为 SIG_BLOCK、SIG_UNBLOCK 和 SIG_SETMASK。* SIG_BLOCK将 set 指向的信号集中的信号添加到进程的信号阻塞集合中。* SIG_UNBLOCK将 set 指向的信号集中的信号从进程的信号阻塞集合中移除。* SIG_SETMASK将进程的信号阻塞集合替换为 set 指向的信号集。
* set指向一个信号集包含要添加或删除的信号。
* oldset指向一个信号集用于保存原来的信号阻塞集合。如果该参数为 NULL则表示不需要保存原来的信号阻塞集合。
sigprocmask() 函数返回值为 0 表示成功返回值为 -1 表示出现错误 SIG_BLOCK 用于向当前的信号阻塞集合中添加新的信号而 SIG_SETMASK 用于完全替换当前的信号阻塞集合为新的信号集。 如果我们把该进程0-31信号全部在block表中设置为阻塞我们还能够杀死该进程吗 #includeiostream
#includeunistd.h
#includesignal.h
using namespace std;
int main(){coutgetpid()endl;sigset_t block;sigset_t oblock;sigemptyset(block);sigemptyset(oblock);int signo1;for(;signo31;signo)sigaddset(block,signo);//在这里设置了对1-31号信号的屏蔽吗没有,只是对这个block变量进行操作sigprocmask(SIG_SETMASK,block,oblock);//这一步才是对1-31号信号进行屏蔽while(1){cout信号已经被屏蔽你无法处理我endl;sleep(1);}} 我们可以发现尽管我们把1-31号信号在该进程全部在block中设置成阻塞我们向该进程发送其他终止信号时都不会终止该进程但是我们发送9号信号时仍然可以终止该进程说明9号信号不会被我们设置成阻塞就影响19信号也可以9号信号可以称为管理员信号 动态观察指定信号的pending 因为OS处理信号非常的快我们不容易看出pending的变化但我们可以让指定信号先阻塞然后去观察 sigpending sigpending 是一个函数用于获取当前被阻塞但是已经产生的信号集合。这个函数可以帮助程序判断哪些信号在阻塞状态下已经产生但是还未被处理。 下面是 sigpending 函数的原型 int sigpending(sigset_t *set);
sigpending 函数接受一个指向 sigset_t 类型的指针作为参数
该参数用于存储当前被阻塞但已经产生的信号集合。 调用 sigpending 函数将阻塞的信号集合存储到传入的 set 指针所指向的位置。如果有任何被阻塞的信号已经产生它们会在 set 中被设置为相应的位。 sigismember sigismember 是一个函数用于检查一个给定的信号是否已经设置在某个信号集中。 下面是 sigismember 函数的原型 int sigismember(const sigset_t *set, int signum);
sigismember 函数接受两个参数指向 sigset_t 类型的指针和一个整数参数 signum。
它会检查 signum 是否已经被设置在信号集合 set 中并返回 1表示是或 0表示否。 事例 #includeiostream
#includeunistd.h
#includesignal.h
using namespace std;void handler(int signum)
{coutsignumgetpid()endl;//exit(0);
}
void printpending(const sigset_t pending )
{for(int signo31;signo0;signo--){if(sigismember(pending,signo)){cout1;}else{cout0;}}coutendl;
}int main()
{signal(2,handler);coutgetpid()endl;//1.屏蔽2号信号sigset_t block;sigset_t oblock;sigemptyset(block); sigemptyset(oblock);sigaddset(block,2);//在这里设置了对2号信号的屏蔽吗没有,只是对这个block变量进行操作sigprocmask(SIG_BLOCK,block,oblock);//这一步才是对2号信号进行屏蔽//2.让进程不断获取当前进程的pendingint cnt0;sigset_t pending;while(true){sigpending(pending);printpending(pending);sleep(1);cnt;if(cnt15){cout解除对2号信号的屏蔽2号信号准备递达endl;sigprocmask(SIG_SETMASK,oblock,nullptr);}}
}[BCHhcss-ecs-6176 2_1]$ ./test_signal
13127
0000000000000000000000000000000
0000000000000000000000000000000
0000000000000000000000000000000
0000000000000000000000000000000
0000000000000000000000000000000
0000000000000000000000000000000
^C0000000000000000000000000000010//ctrlc向该进程发送2号信号时由于2号信号在block中阻塞所以2还信号在pending表中处于未决状态
0000000000000000000000000000010
0000000000000000000000000000010
0000000000000000000000000000010
0000000000000000000000000000010
0000000000000000000000000000010
0000000000000000000000000000010
0000000000000000000000000000010
0000000000000000000000000000010
解除对2号信号的屏蔽2号信号准备递达//当阻塞被解除2号信号马上就会递达该进程的2号信号就不会处于未决状态所以为0
213127
0000000000000000000000000000000
0000000000000000000000000000000
0000000000000000000000000000000
0000000000000000000000000000000
0000000000000000000000000000000
0000000000000000000000000000000
0000000000000000000000000000000
^C213127
0000000000000000000000000000000
0000000000000000000000000000000
0000000000000000000000000000000
^\Quit 信号的处理 信号在合适的时候被处理—什么时候 进程从内核态返回到用户态的时候进行信号的检测和信号的处理 用户态是一种受控的状态能够访问资源有限的 内核态一种操作系统的工作状态能够访问大部分系统资源 可以通过cs寄存器后两个比特位来表示系统处于状态1是内核态3用户态 进程从内核态返回用户态的时候进行信号的检测和信号处理 无论代码中是否使用系统调用整个进程生命周期里会有很多次进程间切换切换了一定会从内核态返回用户态当前进程就会有多次信号捕捉的机会 sigaction sigaction是一个用于设置信号处理器的系统调用函数它在Linux中被广泛使用。函数原型如下
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
参数说明* signum指定要设置处理器的信号编号。
* act指向struct sigaction结构的指针用于指定新的信号处理器和标志。
* oldact如果不为NULL则旧的信号处理器信息将被写入此结构中。
struct sigaction结构包含以下字段
* sa_handler函数指针指定信号处理函数。
* sa_flags指定信号处理的选项如SA_RESTART、SA_NODEFER等。
* sa_mask指定一个信号屏蔽集合用于阻塞其他信号。
通过调用sigaction函数可以为特定信号设置自定义的信号处理函数。当指定的信号发生时系统将执行该处理函数来处理信号。 struct 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); // 已经废弃用于提供旧的系统调用信号语义的函数指针
}; 在Linux中处理一个信号时默认情况下会自动屏蔽相同信号。也就是说如果当前进程正在处理某个信号而此时又收到了同样的信号则新的信号不会被立即处理而是被暂时屏蔽直到当前正在处理的信号处理完毕后才会处理 例如我们这里使用sigaction系统调用对2号信号的处理方法进行自定义我们在自定义处理方法中循环输出当前进程的未决信号集我们第一次发送2号信号时未决信号集中2号信号比特位将被置0但是第二次发送2号信号将不被处理因为第一次发送的信号还没有处理完。 #includeiostream
#includesignal.h
#includeunistd.h
using namespace std;void printsigpending(const sigset_t pending);void handler(int signo)
{coutget a sig: signoendl;while(true){sigset_t pengding;sigpending(pengding);printsigpending(pengding);sleep(1);}
}void printsigpending(const sigset_t pending)
{for(int signo31;signo0;signo--){if(sigismember(pending,signo))cout1;elsecout0;}coutendl;
}int main()
{coutpid: getpid()endl;struct sigaction act,oact;act.sa_handlerhandler;sigisemptyset(act.sa_mask);//sigaddset(act.sa_mask,3);//我们可以设置struct sigaction中的sa_mask这样可以处理这个信号时不仅能够屏蔽相同信号还能屏蔽其他信号sigaction(2,act,oact);while(true){sleep(1);}return 0;}信号的其他补充问题 重入不可重入是函数的特点我们平常遇到的大部分函数都是不可重入的 volatile关键字 保持内存的可见性告知编译器被该关键字修饰的变量不允许被优化对该变量的任何操作都必须在真实的内存中进行操作 #includeiostream
#includesignal.h
#includeunistd.h
using namespace std;volatile int flag0;//防止编译器过度优化
void handler(int signo){
coutsigno:signoendl;
flag1;
coutchange flag :flagendl;
}int main(){
signal(2,handler);
while(!flag);
coutnormal quitendl;
}优化情况下键入 CTRL-C ,2号信号被捕捉执行自定义动作修改 flag1 但是 while 条件依旧满足,进
程继续运行但是很明显flag肯定已经被修改了但是为何循环依旧执行很明显 while 循环检查的flag
并不是内存中最新的flag这就存在了数据二异性的问题。 while 检测的flag其实已经因为优化被放在了
CPU寄存器当中。如何解决呢很明显需要 volatile 子进程退出的时候需要向父进程发送信号的SIGCHILD wait和waitpid函数清理僵尸进程,父进程可以阻塞等待子进程结束,也可以非阻 塞地查询是否有子进程结束等待清理(也就是轮询的方式)。采用第一种方式,父进程阻塞了就不 能处理自己的工作了;采用第二种方式,父进程在处理自己的工作的同时还要记得时不时地轮询一 下,程序实现复杂。其实,子进程在终止时会给父进程发SIGCHLD信号,该信号的默认处理动作是忽略,父进程可以自 定义SIGCHLD信号的处理函数,这样父进程只需专心处理自己的工作,不必关心子进程了,子进程 终止时会通知父进程,父进程在信号处理函数中调用wait清理子进程即可。 进程是资源分配的基本单位线程是调度的基本单位 如果大家还有不懂或者建议都可以发在评论区我们共同探讨共同学习共同进步。谢谢大家