C 语言 信号量

发布于 2024-07-08 00:02:53 字数 4866 浏览 14 评论 0

操作系统用信号量控制程序,当 ctrl+z 向程序发送中断信号。

信号是一条短消息,即一个整型值。当信号到来时,进程必须停止手中一切工作去处理信号。进程会查看信号映射表,表中每个信号都对应一个信号处理器函数。中断信号的默认信号处理器会调用 exit() 函数。

3.1 sigaction

sigaction 是一个函数包装器,是一个结构体,有一个函数指针。sigaction 告诉操作系统进程收到某个信号时应该调用哪个函数。如果想 在某人向进程发送中断信号时让操作系统调用 diediedie() 函 数,就需要把 diediedie() 函数包装成 sigaction。

3.2 处理器必须接收信号量参数

信号是一个整型值,如果你创建了一个自定义处理器函数,就 需要接收一个整型参数,像这样:

void diediedie(int sig)
{
	puts("end");
	exit(1);
}

3.3 用 sigaction() 函数注册 sigaction

创建 sigaction 以后,需要用 sigaction() 函数来让操作系统知道它的存在:

sigaction(signal_no, &new_action, &old_action);

如果 sigaction() 函数失败,会返回 -1,并设置 errno 变量。

实例:Ctrl-C:处理程序

#include <stdio.h>
#include <signal.h>       // 使用这个头文件
#include <stdlib.h>

//新的信号处理器
void diediedie(int sig) {     // 操作系统把信号传给处理器
  puts("goodbye!\n");
  exit(1);
  //处理器返回 void
}

// 注册处理器函数
int catch_signal(int sig, void (*handler)(int)) {
  struct sigaction action;    // 创建动作
  action.sa_handler = handler;  // 将动作处理器设置为我们传来的函数
  sigemptyset(&action.sa_mask);   // 使用一个空的掩码
  action.sa_flags = 0;
  return sigaction(sig, &action, NULL);
}

int main() {
  if (catch_signal(SIGINT, diediedie) == -1) {
    // SIGINT:表示我们要捕获的信号量
    // 将中断处理程序设为 diediedie()
    fprintf(stderr, "Can't map the handler");
    exit(2);
  }
  char name[30];
  printf("Enter your name:");
  fgets(name, 30, stdin);
  printf("Hello, %s\n", name);
  return 0;
}

程序要求用户输入名字,然后它会等待输入。如果用户没有输入名字而是按了 Ctrl-C ​,操作系统会自动向进程发送中断信号  SIGINT,然后用我们在 catch_signal() 函数中注册的 sigaction 来处理这个信号。sigaction 中有一个指向 diediedie() 函数的指针,程序会调用这个函数,显示消息并调用 exit()

3.4 发送信号

在类 Unix 操作系统中我们经常用 kill 命令,kill 向进程发送一个信号(默认: SIGTERM ),也可以用它发送其他信号,在 C 中可以用 raise() 函数让进程向自己发送信号。

raise(SIGTERM);

除了在发生错误时使用,有时进程也需要产生自己的信号,比如信号 SIGALRM。信号通常由进程的间隔定时器创建。间隔定时器就像一台:你可以定一个时间,其间程序就会去做 其他事情:

注意:不要同时使用 sleep()alarm() , 因为都使用了间隔计时器,同时使用会发生冲突

3.5 实例:计时数学运算大比拼

下面程序测试用户的数学水平,要求用户输出 a*b ​ 的值,涉及条件:

  • 控制用户的 ctrl-c
  • 用户要是在 5s 后没有写上答案则程序结束并把退出状态设为 0
#include <stdio.h>
#include <signal.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <stdlib.h>

int score=0;

//新的信号处理器
void end_game(int sig){
  printf("\nFinal score: %i\n", score);
  exit(0);
}

//注册处理器函数
int catch_signal(int sig, void (*handler)(int)) {
  struct sigaction action;
  action.sa_handler = handler;
  sigemptyset(&action.sa_mask);
  action.sa_flags = 0;
  return sigaction(sig, &action, NULL);
}

void time_up(int sig) {
  puts("\nTime's up!");
  raise(SIGINT);    // 引发 SIGINT 信号,让程序调用 end_game() 显示最后得分
}

void error(char *msg){
  fprintf(stderr, "%s, %s\n", msg, strerror(errno));
  exit(1);
}

int main(){
  catch_signal(SIGALRM, time_up);
  catch_signal(SIGINT, end_game);
  srandom(time(0));         // 保证每次得到不同的随机值
  while (1) {
    int a = random() % 11;    //a,b 是 0-10 随机数
    int b = random() % 11;
    char txt[4];
    alarm(5);           // 将闹钟信号设为 5s 后触发
    // 只要能在 5s 内再次回到这个地方,定时器就会重置,闹钟信号也不会触发
    printf("\nWhat is %i * %i? ", a, b);
    fgets(txt, 4, stdin);
    int answer = atoi(txt);
    if (answer == a*b) {
      score++;
    } else
      printf("\nWrong!Score:%i\n", score);
  }
  return 0;
}

运行:

➜  intoC git:(master) ✗ ./test_sig        
What is 0 * 4? 4

Wrong!Score:0

What is 1 * 3? 3

What is 9 * 10? 90

What is 0 * 2? 
Time's up!

Final score: 2

➜  intoC git:(master) ✗ ./test_sig

What is 5 * 2? 10

What is 8 * 7? ^C
Final score: 1

3.6 小结

  • system 用信号控制进程
  • 进程常用信号来结束
  • 进程收到信号后会进行信号处理器
  • 大部分错误信号的默认处理器会终止程序
  • 可以用 signation() 函数替换处理器
  • 可以用 raise() 向自己发送信号
  • 间隔定时器发送 SIGALRM 信号
  • alarm() 函数设置间隔定时器
  • 每个进程只能有一个定时器
  • 不要同时使用 sleep() 和 alarm()
  • kill 命令可以向进程发送信号
  • kill -KILL <进程号> 一定能让你的程序上西天

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

老街孤人

暂无简介

0 文章
0 评论
22 人气
更多

推荐作者

qq_E2Iff7

文章 0 评论 0

Archangel

文章 0 评论 0

freedog

文章 0 评论 0

Hunk

文章 0 评论 0

18819270189

文章 0 评论 0

wenkai

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文