欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 财经 > 创投人物 > Linux进程间通信——信号量信号详解

Linux进程间通信——信号量信号详解

2025/2/22 2:04:44 来源:https://blog.csdn.net/2301_79532069/article/details/144173460  浏览:    关键词:Linux进程间通信——信号量信号详解

一、信号量semaphore

1.1概念

        也称信号灯,信号量本质上是一个计数器(非负整数),主要用来保护临界资源(共享资源),这种资源同一时刻仅供一个进程(线程)使用,它的值表示系统中该资源的数量(若为0当然不允许使用),因此可以实现进程(线程)间的同步与互斥。对于共享内存,所有进程都可以访问,就容易导致读写冲突,此时就很需要信号量来配合使用。

同步在共享它的进程或线程间保持数据一致

互斥同一时刻仅供一个进程(线程)使用资源

信号量只能通过三种操作来访问:

  • 初始化

  • P操作(申请使用资源,属于"消费者")

    • 如果信号量>0,信号量-1返回;

    • 如果信号量==0,则睡眠等待直到信号量>0然后立即-1返回

  • V操作(释放资源,属于"生产者")

    • 无进程/线程等待时+1,

    • 有进程/线程等待资源时(说明信号量为0)则唤醒并让其返回

linux用户态进程提供三种信号量,POSIX有名信号量、POSIX无名信号量、SYSTEM V信号量。

        有名信号量的值保存在文件中(具体是保存在/dev/shm/目录下,使用时需要链接库),它既可以用于进程(包括不相关进程)也可以用于线程;

        无名信号量的值保存在内存中,Linux只支持线程同步。

除了创建和删除不同,PV操作对应的API都相同

1.2创建

#include <semaphore.h>
#include <pthread.h>//创建有名信号量
sem_t *sem_open(char *name, int oflag, mode_t mode, unsigned int value)@param: name     信号量文件名字,不要带路径,因为有默认存放的目录oflag    常用O_CREATmode     常用0666value    信号量初始值(二值信号量为1,普通信号量表示资源数目)@return: 成功返回信号量指针,失败返回SEM_FAILED with errno值
#include <semaphore.h>
#include <pthread.h>//创建无名信号量
int sem_init(sem_t *sem,int pshared,unsigned int value);@param:  sem        信号量指针  pshared    0value      信号量初始值(二值信号量为1,普通信号量表示资源数目)@return: 成功返回0,失败返回-1

1.3PV操作

#include <semaphore.h>
#include <pthread.h>sem_wait(sem_t *sem)
申请使用资源,如果信号量>0,信号量-1返回;如果信号量==0,则睡眠等待直到信号量>0然后立即-1返回sem_post(sem_t *sem)
释放资源,无进程/线程等待时+1,有进程/线程等待资源时(说明信号量为0)则唤醒并让其返回

1.4关闭(有名信号量专用)

#include <semaphore.h>
#include <pthread.h>//关闭有名信号量的引用
sem_close(sem_t *sem)

一旦打开使用了有名信号量,关闭它的引用就很重要,因为后续的删除需要等系统中所有进程或线程关闭了引用才能生效,每个有名信号量都有一个引用计数器记录它的打开次数,sem_unlink必须等待最后一个sem_close完成!

1.5删除

#include <semaphore.h>
#include <pthread.h>//删除有名信号量
sem_unlink(char *name)
#include <semaphore.h>
#include <pthread.h>//删除无名信号量
int sem_destroy(sem_t *sem)

二、信号signal

2.1概念

        信号是软中断(软件层次上对中断机制的一种模拟),linux操作系统中用于进程间通信、处理异常情况的一种机制。它是由操作系统向一个进程或者线程发送的一种异步通知,用于通知该进程或线程某种事件已经发生,需要做出相应的处理

        每个进程收到的所有信号,都是由内核负责发送的每个信号都有编号名称默认处理动作,可通过命令kill -l显示查看所有信号

1-31为常规信号(也称标准信号或普通信号),34-64为实时信号,驱动编程与硬件相关

2.2常用信号

常用信号:

信号名含义默认动作
SIGHUP终端关闭时产生,发给和该终端关联的会话内所有进程终止
SIGINT终端输入ctrl C时产生,发给当前终端的所有前台进程终止
SIGQUIT终端输入ctrl \ 时产生,和SIGINT类似终止
SIGILL该信号在一个进程企图执行一条非法指令时产生终止
SIGSEV该信号在非法访问内存时产生,如野指针、缓冲区溢出终止
SIGPIPE当进程往一个没有读端的管道中写入时产生,代表“管道断裂”终止
SIGKILL该信号用来结束进程,并且不能被捕捉和忽略终止
SIGSTOP该信号用于暂停进程,并且不能被捕捉和忽略暂停进程
SIGALRM该信号用于通知某进程定时器时间已到终止
SIGCHLD子进程状态改变时发给父进程忽略

2.3信号的处理方式

  • 忽略(SIGKILL、SIGSTOP除外)

  • 默认动作

  • 捕捉将默认动作改为用户调用的处理函数,同样地,SIGKILL、SIGSTOP除外)

2.4信号的发送

1)shell命令 :kill -[信号编号] [pid]

2)程序发送: kill/raise/sigqueue函数

int kill(pid_t pid, int signum)
@param: pid >0 发送给指定进程=0 发送给同一进程组的所有进程<-1 发送给|pid|同一进程组的所有进程=-1 发送给有权限发送的系统中所有进程@return:成功返回0失败返回-1int raise(int signum) 给自己发信号 等价于 kill(getpid(),signum)sigqueue(pid_t pid, int signum, const union sigval value)
功能:比一般的信号发送还多传递了信号携带的数据,接收信号的进程可以根据数据做出不同响应
通常搭配sigaction使用@param: value 联合体sigval变量union sigval {int sival_int;  //这个常用一些void *sival_ptr;
};

 示例:

#include <signal.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>int main(int argc, char **argv)
{	if(argc < 3){perror("missing params\n");return 0;}pid_t pid;int signum;signum = atoi(argv[1]);pid = atoi(argv[2]);union sigval value;value.sival_int = 520;sigqueue(pid,signum,value);return 0;
}

2.5信号的捕捉

signal(int signum, sighandler_t handler)
 功能:捕捉指定的信号,修改其默认动作 为 自定义的处理函数

@param:   signum  信号编号,也可使用对应的信号名
                  handler 自定义函数名,SIG_DFL 为默认动作,SIG_IGN 为忽略 


(函数名是函数的入口地址,这里的sighandler_t 是函数指针类型名 通过typedef void(*sighandler_t)(int)实现,因此自定义函数也要设置成无返回值,参数为1个int型的  )  

 //由于signal在不同类unix系统的行为不完全一样,系统建议使用sigaction函数
sigaction(int signum, const struct sigaction *act, struct sigaction *oldact) 
@param: 信号名、新动作、旧动作(一般都给NULL,表示不关心)


struct sigaction{
    void (*sa_handler)(int); //处理函数指针
    void (*sa_sigaction)(int signum,siginfo_t *,void *); //与sigqueue搭配使用,获取它发送的信号携带的数据
    sigset sa_mask; //设置阻塞参数
    int sa_flags;   //使用sa_sigaction成员时需要设置为SA_SIGINFO 不使用时给0
    void (*sa_restore)(void);//废弃的数据域 
};
/******************************自定义函数的参数*****************************/
//siginfo_t xxx 成员如下
siginfo_t{
    pid_t si_pid; //哪个进程发送的
    int si_int;    //数据内容
    sigval_t si_value; //sigval联合体中的数据 和si_int没区别
};
union sigval {
    int sival_int;  //这个常用一些
    void *sival_ptr;
};
void * xxx //xxx为 NULL 无数据, 非NULL 有数据 

#include <signal.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdlib.h>void print_status(int status);void handler(int signum)
{int status;if(signum == SIGCHLD){wait(&status);print_status(status);}
}int main()
{struct sigaction act;	act.sa_handler = handler; //normal signalact.sa_flags = 0;sigemptyset(&act.sa_mask);	printf("before fork\n");pid_t pid;pid = fork();if(pid < 0){perror("fork");return 0;}else if(pid > 0) //father process{sigaction(SIGCHLD,&act,NULL);while(1){puts("this is father,i'm sleeping");sleep(1);}	}else if(pid == 0) //child process{sleep(5);exit(7);}return 0;
}void print_status(int status)
{if(WIFEXITED(status))  //exit normal{printf("child process finished and exit normal,exit status:%d\n",WEXITSTATUS(status));}else if(WIFSIGNALED(status)) //exit abnormal{printf("child process finished but exit abnormal,exit status:%d\n",WTERMSIG(status));}}

2.6定时器函数

unsigned int alarm(unsigned int seconds)  
//定时seconds发送信号SIGALRM给当前进程,默认动作是终止,返回值是上次定时剩余时间int setitimer(int which, const struct itimerval *new_value, struct itimerval *old_value)//循环定时发送SIGALRM
@param: which ITIMER_REAL 表示以真实逝去的时间递减发送SIGALRM信号 new_value 结构体指针,设定定时参数  old_value 旧定时参数 一般给NULLstruct itimerval{struct timerval it_interval; //闹钟触发周期 后续产生信号的间隔struct timerval it_value;    //闹钟触发时间 第一次产生信号的时间
};
struct timerval{time_t tv_sec;     //ssuseconds tv_usec; //us
};
#include <signal.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/time.h>void handler(int signum, siginfo_t *info, void * data)
{if(signum == SIGALRM){printf("my pid:%d,i caught SIGALRM\n",getpid());}if(data != NULL){printf("signal passed data:%d\n",info->si_int);}}int main()
{struct sigaction act;	
//	act.sa_handler = handler; //normal signal
//	act.sa_flags = 0;
//	sigemptyset(&act.sa_mask);	act.sa_sigaction = handler; //with data from signalact.sa_flags = SA_SIGINFO;sigaction(SIGALRM,&act,NULL);struct itimerval newvalue;newvalue.it_value.tv_sec = 2;newvalue.it_value.tv_usec = 0;newvalue.it_interval.tv_sec = 2;newvalue.it_interval.tv_usec = 0;setitimer(ITIMER_REAL,&newvalue,NULL);while(1);	return 0;
}

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词