4. POSIX信号量
POSIX 有名信号量
这种有名信号量的名字由类似"/somename"这样的字符串组成,注意前面有一个正 斜杠,这样的信号量其实是一个特殊的文件,创建成功之后将会被放置在系统的一个特殊的 虚拟文件系统/dev/shm 之中,不同的进程间只要约定好一个相同的名字,他们就可以通过 这种有名信号量来相互协调。
4.1 POSIX 有名信号量的一般使用步骤是:
1,使用 sem_open( )来创建或者打开一个有名信号量。
2,使用 sem_wait( )和 sem_post( )来分别进行 P 操作和 V 操作。
3,使用 sem_close( )来关闭他。
4,使用 sem_unlink( )来删除他,并释放系统资源。
不像 system-V 的信号量可以申请或者释放超过 1 个资源,对于 POSIX 信号量而言,每 次申请和释放的资源数都是 1 。其中调用sem_wait( )在资源为 0 时会导致阻塞,如果不想 阻塞等待,可以使用 sem_trywait( )来替代。
4.2 代码
posix_w.c
cpp
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/msg.h>
#include <sys/sem.h>
#include <semaphore.h>
//POSIX信号量的使用
int main()
{
//创建一个共享内存对象,创建大小为2字节的共享内存
int shmid = shmget(ftok("/",3),2,IPC_CREAT | 0777);
//映射
char* p = shmat(shmid,NULL,0);
//创建posix信号量,代表空间 初始化大小为1
sem_t* space = sem_open("/myspace",O_CREAT,0777,1);
//创建posix信号量,代表数据 初始化大小为0
sem_t* data = sem_open("/mydata",O_CREAT,0777,0);
char* msg = "0123456789";
int i=0;
while(1){
sem_wait(space);//相当于p操作
memcpy(p,msg+i,1);//内存复制函数
sem_post(data);//相当于v操作
i = (i+1)%10;//循环的关键
}
return 0;
}
posix_r.c
cpp
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/msg.h>
#include <sys/sem.h>
#include <semaphore.h>
//POSIX信号量的使用
int main()
{
//创建一个共享内存对象,创建大小为2字节的共享内存
int shmid = shmget(ftok("/",3),2,IPC_CREAT | 0777);
//映射
char* p = shmat(shmid,NULL,0);
//创建posix信号量,代表空间 初始化大小为1
sem_t* space = sem_open("/myspace",O_CREAT,0777,1);
//创建posix信号量,代表数据 初始化大小为0
sem_t* data = sem_open("/mydata",O_CREAT,0777,0);
int i=0;
while(1){
sem_wait(data);//相当于p操作
fprintf(stderr,p);
sem_post(space);//相当于v操作
i = (i+1)%10;//循环的关键
}
return 0;
}
运行
因为posix涉及到别的库的使用
所以我们编译运行时使用
gcc posix_w.c -o pw -lpthread
gcc posix_r.c -o pr -lpthread
但是我们的代码还有个错误
如果我们运行完一次并且ctrl+c停止了(其实时发送了一个SIGINT的中断信号,中止了程序),再次运行就没有效果了,需要去到 cd /dev/shm中删除我们创建的posix信号量,然乎再运行才行
这个bug我们在信号中改进
5. 信号
kiil -l 查看信号
信号是一种比较特别的 IPC,大部分的信号是异步的,换句话讲:一般情况下,进程什 么时候会收到信号、收到什么信号是无法事先预料的 (除了某几个特殊的信号之外) ,信号 的到来就像你家门铃的响起一样,你不知道他什么时候会响。
可以看见,Linux 系统中有许多信号,其中前面 31 个信号都有一个特殊的名字,对应
一个特殊的事件,比如 1 号信号 SIGHUP (Signal Hang UP) ,表示每当系统中的一个控制 终端被关闭 (即挂断,hang up) 时,即会产生这个信号,有时会将他们称为非实时信号, 这些信号都是从 Unix 系统继承下来的,他们还有个名称叫"不可靠信号"
5.1 非实时信号特点(不可靠信号)
1,非实时信号不排队,信号的响应会相互嵌套。
2,如果目标进程没有及时响应非实时信号,那么随后到达的该信号将会被丢弃。
3,每一个非实时信号都对应一个系统事件,当这个事件发生时,将产生这个信号。
4,如果进程的挂起信号中含有实时和非实时信号,那么进程优先响应实时信号并且会 从大到小依此响应,而非实时信号没有固定的次序。
5.2 可靠信号特点
后面的 31 个信号 (从 SIGRTMIN[34] 到 SIGRTMAX[64]) 是 Linux 系统新增的实时信
号,也被称为"可靠信号",这些信号的特征是:
1,实时信号的响应次序按接收顺序排队,不嵌套。
2,即使相同的实时信号被同时发送多次,也不会被丢弃,而会依次挨个响应。
3,实时信号没有特殊的系统事件与之对应。
5.3 不可靠信号对应的意思
1,"备注"中注明的事件发生时会产生相应的信号,但并不是说该信号的产生就一定 发生了这个事件。事实上,任何进程都可以使用函数 kill( )来产生任何信号。
2 ,信号 SIGKILL 和 SIGSTOP 是两个特殊的信号,他们不能被忽略、阻塞或捕捉,只 能按缺省动作来响应。换句话说,除了这两个信号之外的其他信号,接收信号的目标进程按照如下顺序来做出反应:
A) 如果该信号被阻塞,那么将该信号挂起,不对其做任何处理,等到解除对其阻塞为止。否则进入 B。
B) 如果该信号被捕捉,那么进一步判断捕捉的类型:
B1) 如果设置了响应函数,那么执行该响应函数。
B2) 如果设置为忽略,那么直接丢弃该信号。否则进入 C。
C) 执行该信号的缺省动作。
5.4 kill函数和signal函数
很多人对 kill( )抱有偏见,以为他就是要"杀死"某人,但这并非他的初衷,除非他发 送的是一个致命的信号,否则他只是"发送一个信号"的行为并不一定会致对方于死地。
可以在代码中使用kill函数
还可以在linux终端这样使用命令
kill -SIGSTOP 进程pid 这个意思就是停止某一个进程
kill -SIGCONT 进程id 继续某一个进程
还可以使用信号编号
kill -19 进程pid 这个意思就是停止某一个进程
kill -18 进程pid 继续某一个进程
这个函数一般是跟 kill( )配套使用的, 目标进程必须先使用 signal( )来为某个信号设置 一个响应函数,或者设置忽略某个信号,才能改变信号的缺省行为,这个过程称为"信号的 捕捉" 。注意,对一个信号的"捕捉"可以重复进行,signal( )函数将会返回前一次设置的 信号响应函数指针。对于所谓的信号响应函数的接口,规定必须是:void (*)(int);
5.5 捕获信号练习
buhuo_w.c
cpp
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/msg.h>
#include <sys/sem.h>
#include <semaphore.h>
#include<signal.h>
//POSIX信号量的使用 和捕获信号
sem_t* space = NULL;
sem_t* data = NULL;
//关闭和删除文件
void wakeng(){
sem_close(space);
sem_close(data);
sem_unlink("/myspace");
sem_unlink("/mydata");
}
int main()
{
signal(SIGINT,wakeng);//捕获一个信号(这里我们捕获的是ctrl+c发送的SINGINT信号)
//创建一个共享内存对象,创建大小为2字节的共享内存
int shmid = shmget(ftok("/",3),2,IPC_CREAT | 0777);
//映射
char* p = shmat(shmid,NULL,0);
//创建posix信号量,代表空间 初始化大小为1
space = sem_open("/myspace",O_CREAT,0777,1);
//创建posix信号量,代表数据 初始化大小为0
data = sem_open("/mydata",O_CREAT,0777,0);
char* msg = "0123456789";
int i=0;
while(1){
sem_wait(space);//相当于p操作
memcpy(p,msg+i,1);//内存复制函数
sem_post(data);//相当于v操作
i = (i+1)%10;//循环的关键
}
return 0;
}
buhuo_r.c
cpp
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/msg.h>
#include <sys/sem.h>
#include <semaphore.h>
#include<signal.h>
//POSIX信号量的使用 和捕获信号
sem_t* space = NULL;
sem_t* data = NULL;
//关闭和删除文件
void wakeng(){
sem_close(space);
sem_close(data);
sem_unlink("/myspace");
sem_unlink("/mydata");
}
int main()
{
signal(SIGINT,wakeng);//捕获一个信号(这里我们捕获的是ctrl+c发送的SINGINT信号)
//创建一个共享内存对象,创建大小为2字节的共享内存
int shmid = shmget(ftok("/",3),2,IPC_CREAT | 0777);
//映射
char* p = shmat(shmid,NULL,0);
//创建posix信号量,代表空间 初始化大小为1
space = sem_open("/myspace",O_CREAT,0777,1);
//创建posix信号量,代表数据 初始化大小为0
data = sem_open("/mydata",O_CREAT,0777,0);
int i=0;
while(1){
sem_wait(data);//相当于p操作
fprintf(stderr,p);
sem_post(space);//相当于v操作
i = (i+1)%10;//循环的关键
}
return 0;
}
编译运行
再次运行依然可以
6. 精灵进程
daemon.c
cpp
#include <stdio.h>
#include <signal.h>
#include <sys/resource.h>
#include <sys/file.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
//精灵进程
int main()
{
pid_t a;
int max_fd,i;
/***************************************************
1.忽略SIGHUP信号(关闭终端),防止进程被CTTY控制终端关闭
*****************************************************/
signal(SIGHUP,SIG_IGN);
/***************************************************
2.生成第一个子进程,确保能够正确产生新的会话期
*****************************************************/
a = fork();
if(a > 0)
{
exit(0);
}
/***************************************************
3.调用seipid()函数,让第一个子进程产生新的没有控制终端的会话期
*****************************************************/
setsid();
/***************************************************
4.生成第二个子进程,防止精灵进程打开终端文件创建控制终端
*****************************************************/
a = fork();
if(a > 0)
{
exit(0);
}
/***************************************************
5.分离精灵进程的原生进程组,防止接收到任何控制进程组的信号
*****************************************************/
setpgrp();
/***************************************************
6.关闭所有的文件描述符,释放资源
*****************************************************/
max_fd = sysconf(_SC_OPEN_MAX);
for(i = 0; i < max_fd; i++)
{
close(i);
}
/***************************************************
7.文件权限掩码清零
*****************************************************/
umask(0);
/***************************************************
8.改变进程的工作路径,确保进程不会被卸载
*****************************************************/
chdir("/");
/***************************************************
9.精灵进程创建成功
*****************************************************/
//精灵进程要干的事
pause();
return 0;
}