Android C++系列:Linux信号(三)

简介: 例如:strtok就是一个不可重入函数,因为strtok内部维护了一个内部静态指针,保存上一 次切割到的位置,如果信号的捕捉函数中也去调用strtok函数,则会造成切割字符串混乱, 应用strtok_r版本,r表示可重入。

可重入函数


image.png


  • 不含全局变量和静态变量是可重入函数的一个要素
  • 可重入函数见man 7 signal
  • 在信号捕捉函数里应使用可重入函数
  • 在信号捕捉函数里禁止调用不可重入函数


例如:strtok就是一个不可重入函数,因为strtok内部维护了一个内部静态指针,保存上一 次切割到的位置,如果信号的捕捉函数中也去调用strtok函数,则会造成切割字符串混乱, 应用strtok_r版本,r表示可重入。


信号引起的竞态和异步I/O


时序竞态


  • int pause(void)


  • 使调用进程挂起,直到有信号递达,如果递达信号是忽略,则继续挂起


  • int sigsuspend(const sigset_t *mask)


  • 以通过指定mask来临时解除对某个信号的屏蔽,
  • 然后挂起等待,
  • 当被信号唤醒sigsuspend返回时,进程的信号屏蔽字恢复为原来的值


mysleep实现,这种实现方式是否存在BUG?


#include <unistd.h> 
#include <signal.h> 
#include <stdio.h>
void sig_alrm(int signo) {
  /* nothing to do */ 
}
unsigned int mysleep(unsigned int nsecs) {
  struct sigaction newact, oldact; 
  unsigned int unslept;
  newact.sa_handler = sig_alrm; 
  sigemptyset(&newact.sa_mask); 
  newact.sa_flags = 0; 
  sigaction(SIGALRM, &newact, &oldact);
  alarm(nsecs); 
  pause();
  unslept = alarm(0); 
  sigaction(SIGALRM, &oldact, NULL);
  return unslept; 
  }
int main(void) {
  while(1){
    mysleep(2);
    printf("Two seconds passed\n"); 
  }
  return 0; 
}


mysleep改进版


unsigned int mysleep(unsigned int nsecs) {
  struct sigaction newact, oldact;
  sigset_t newmask, oldmask, suspmask;
  unsigned int unslept;
  /* set our handler,save previous information */
  newact.sa_handler = sig_alrm;
  sigemptyset(&newact.sa_mask); 
  newact.sa_flags = 0; 
  sigaction(SIGALRM, &newact, &oldact);
  /* block SIGALRM and save current signal mask */ 
  sigemptyset(&newmask);
  sigaddset(&newmask, SIGALRM); 
  sigprocmask(SIG_BLOCK, &newmask, &oldmask);
  alarm(nsecs);
  suspmask = oldmask; 
  sigdelset(&suspmask, SIGALRM); /* make sure SIGALRM isn't blocked */ 
  sigsuspend(&suspmask);/* wait for any signal to be caught */
  /* some signal has been caught,SIGALRM is now blocked */
  unslept = alarm(0);
  sigaction(SIGALRM, &oldact, NULL);/* reset previous action */
  /* reset signal mask, which unblocks SIGALRM */ 
  sigprocmask(SIG_SETMASK, &oldmask, NULL); 
  return(unslept);
}


全局变量异步I/O


可重入函数


  1. 不含全局变量和静态变量是可重入函数的一个要素
  2. 可重入函数见man 7 signal
  3. 在信号捕捉函数里应使用可重入函数


避免异步I/O的类型


  • sig_atomic_t 平台下的原子类型


  • volatile 防止编译器开启优化选项时,优化对内存的读写


SIGCHLD信号处理


SIGCHLD的产生条件


  • 子进程终止时
  • 子进程接收到SIGSTOP信号停止时
  • 子进程处在停止态,接受到SIGCONT后唤醒时


代码实现


#include <stdio.h> 
#include <stdlib.h> 
#include <unistd.h> 
#include <errno.h> 
#include <sys/types.h> 
#include <sys/wait.h> 
#include <signal.h>
void sys_err(char *str) {
  perror(str);
  exit(1); 
}
void do_sig_child(int signo) {
  int status;
  pid_t pid;
  while ((pid = waitpid(0, &status, WNOHANG)) > 0) {
    if (WIFEXITED(status))
      printf("child %d exit %d\n", pid, WEXITSTATUS(status));
    else if (WIFSIGNALED(status))
      printf("child %d cancel signal %d\n", pid, WTERMSIG(status));
  }
}
int main(void) {
  pid_t pid;
  int i;
  //阻塞SIGCHLD
  for (i = 0; i < 10; i++) {
    if ((pid = fork()) == 0) 
      break;
    else if (pid < 0) 
      sys_err("fork");
  }
  if (pid == 0) {
    int n = 18; while (n--) {
      printf("child ID %d\n", getpid());
      sleep(1); 
    }
    return i; 
  }else if (pid > 0) { //先设置捕捉
    //再解除对SIGCHLD的阻塞 
    struct sigaction act;
    act.sa_handler = do_sig_child; 
    sigemptyset(&act.sa_mask); act.sa_flags = 0; 
    sigaction(SIGCHLD, &act, NULL); 
    while (1) {
      printf("Parent ID %d\n", getpid());
      sleep(1); 
    }
  }
  return 0; 
}


status处理方式


pid_t waitpid(pid_t pid, int *status, int options)


  • options
  • WNOHANG 没有子进程结束,立即返回
  • WUNTRACED
  • 如果子进程由于被停止产生的SIGCHLD, waitpid则立即返回
  • WCONTINUED 如果子进程由于被SIGCONT唤醒而产生的SIGCHLD, waitpid则立即返回
  • 获取status
  • WIFEXITED(status) 子进程正常exit终止,返回真 WEXITSTATUS(status)返回子进程正常退出值
  • WIFSIGNALED(status) 子进程被信号终止,返回真
  • WTERMSIG(status)返回终止子进程的信号值 WIFSTOPPED(status)
  • 子进程被停止,返回真 WSTOPSIG(status)返回停止子进程的信号值
  • WIFCONTINUED(status) 子进程由停止态转为就绪态,返回真


向信号捕捉函数传参


sigqueue


int sigqueue(pid_t pid, int sig, const union sigval value) union sigval {
int sival_int;
void *sival_ptr; };


sigaction


void (*sa_sigaction)(int, siginfo_t *, void *) 
siginfo_t {
  int si_int; 
  void *si_ptr; 
  sigval_t si_value;
  ...
}
sa_flags = SA_SIGINFO
/* POSIX.1b signal */ /* POSIX.1b signal */ /* Signal value */


实例


  • 进程自己收发信号,在同一地址空间
  • 不同进程间收发信号,不在同一地址空间,不适合传地址


信号中断系统调用


read阻塞时,信号中断系统调用:


  1. 返回部分读到的数据
  2. read调用失败,errno设成EINTER


总结


本文介绍了可重入函数,信号引起的竞态和异步I/O,SIGCHLD信号处理,向想好捕捉函数传参,信号中断系统调用。

目录
相关文章
|
8月前
|
安全 Linux
【Linux】阻塞信号|信号原理
本教程从信号的基本概念入手,逐步讲解了阻塞信号的实现方法及其应用场景。通过对这些技术的掌握,您可以更好地控制进程在处理信号时的行为,确保应用程序在复杂的多任务环境中正常运行。
299 84
|
8月前
|
JavaScript Linux 网络安全
Termux安卓终端美化与开发实战:从下载到插件优化,小白也能玩转Linux
Termux是一款安卓平台上的开源终端模拟器,支持apt包管理、SSH连接及Python/Node.js/C++开发环境搭建,被誉为“手机上的Linux系统”。其特点包括零ROOT权限、跨平台开发和强大扩展性。本文详细介绍其安装准备、基础与高级环境配置、必备插件推荐、常见问题解决方法以及延伸学习资源,帮助用户充分利用Termux进行开发与学习。适用于Android 7+设备,原创内容转载请注明来源。
1997 77
|
存储 Linux C语言
Linux C/C++之IO多路复用(aio)
这篇文章介绍了Linux中IO多路复用技术epoll和异步IO技术aio的区别、执行过程、编程模型以及具体的编程实现方式。
618 1
Linux C/C++之IO多路复用(aio)
|
8月前
|
监控 Shell Linux
Android调试终极指南:ADB安装+多设备连接+ANR日志抓取全流程解析,覆盖环境变量配置/多设备调试/ANR日志分析全流程,附Win/Mac/Linux三平台解决方案
ADB(Android Debug Bridge)是安卓开发中的重要工具,用于连接电脑与安卓设备,实现文件传输、应用管理、日志抓取等功能。本文介绍了 ADB 的基本概念、安装配置及常用命令。包括:1) 基本命令如 `adb version` 和 `adb devices`;2) 权限操作如 `adb root` 和 `adb shell`;3) APK 操作如安装、卸载应用;4) 文件传输如 `adb push` 和 `adb pull`;5) 日志记录如 `adb logcat`;6) 系统信息获取如屏幕截图和录屏。通过这些功能,用户可高效调试和管理安卓设备。
|
9月前
|
Linux
Linux编程: 在业务线程中注册和处理Linux信号
通过本文,您可以了解如何在业务线程中注册和处理Linux信号。正确处理信号可以提高程序的健壮性和稳定性。希望这些内容能帮助您更好地理解和应用Linux信号处理机制。
174 26
|
9月前
|
Linux
Linux编程: 在业务线程中注册和处理Linux信号
本文详细介绍了如何在Linux中通过在业务线程中注册和处理信号。我们讨论了信号的基本概念,并通过完整的代码示例展示了在业务线程中注册和处理信号的方法。通过正确地使用信号处理机制,可以提高程序的健壮性和响应能力。希望本文能帮助您更好地理解和应用Linux信号处理,提高开发效率和代码质量。
183 17
|
9月前
|
消息中间件 Linux C++
c++ linux通过实现独立进程之间的通信和传递字符串 demo
的进程间通信机制,适用于父子进程之间的数据传输。希望本文能帮助您更好地理解和应用Linux管道,提升开发效率。 在实际开发中,除了管道,还可以根据具体需求选择消息队列、共享内存、套接字等其他进程间通信方
239 16
|
Java Linux Android开发
深入探索Android系统架构:从Linux内核到应用层
本文将带领读者深入了解Android操作系统的复杂架构,从其基于Linux的内核到丰富多彩的应用层。我们将探讨Android的各个关键组件,包括硬件抽象层(HAL)、运行时环境、以及核心库等,揭示它们如何协同工作以支持广泛的设备和应用。通过本文,您将对Android系统的工作原理有一个全面的认识,理解其如何平衡开放性与安全性,以及如何在多样化的设备上提供一致的用户体验。
|
Ubuntu Linux 编译器
Linux/Ubuntu下使用VS Code配置C/C++项目环境调用OpenCV
通过以上步骤,您已经成功在Ubuntu系统下的VS Code中配置了C/C++项目环境,并能够调用OpenCV库进行开发。请确保每一步都按照您的系统实际情况进行适当调整。
2339 3
|
Linux API 开发工具
FFmpeg开发笔记(五十九)Linux编译ijkplayer的Android平台so库
ijkplayer是由B站研发的移动端播放器,基于FFmpeg 3.4,支持Android和iOS。其源码托管于GitHub,截至2024年9月15日,获得了3.24万星标和0.81万分支,尽管已停止更新6年。本文档介绍了如何在Linux环境下编译ijkplayer的so库,以便在较新的开发环境中使用。首先需安装编译工具并调整/tmp分区大小,接着下载并安装Android SDK和NDK,最后下载ijkplayer源码并编译。详细步骤包括环境准备、工具安装及库编译等。更多FFmpeg开发知识可参考相关书籍。
438 0
FFmpeg开发笔记(五十九)Linux编译ijkplayer的Android平台so库

热门文章

最新文章

下一篇
oss云网关配置