进程间通信3

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( )来删除他,并释放系统资源。

进程间通信3_第1张图片

进程间通信3_第2张图片

不像 system-V 的信号量可以申请或者释放超过 1 个资源,对于 POSIX 信号量而言,每 次申请和释放的资源数都是 1 。其中调用sem_wait( )在资源为 0 时会导致阻塞,如果不想 阻塞等待,可以使用 sem_trywait( )来替代。

进程间通信3_第3张图片

4.2 代码

posix_w.c

#include 
#include               
#include 
#include 
#include 
#include 
#include            
#include 
#include 
#include 
#include 
#include 
#include 

//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

#include 
#include               
#include 
#include 
#include 
#include 
#include            
#include 
#include 
#include 
#include 
#include 
#include 

//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

进程间通信3_第4张图片

进程间通信3_第5张图片

但是我们的代码还有个错误

如果我们运行完一次并且ctrl+c停止了(其实时发送了一个SIGINT的中断信号,中止了程序),再次运行就没有效果了,需要去到 cd /dev/shm中删除我们创建的posix信号量,然乎再运行才行

进程间通信3_第6张图片这个bug我们在信号中改进

5.  信号

kiil -l 查看信号

信号是一种比较特别的 IPC,大部分的信号是异步的,换句话讲:一般情况下,进程什 么时候会收到信号、收到什么信号是无法事先预料的 (除了某几个特殊的信号之外) ,信号 的到来就像你家门铃的响起一样,你不知道他什么时候会响。

进程间通信3_第7张图片

可以看见,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 不可靠信号对应的意思

进程间通信3_第8张图片

进程间通信3_第9张图片

1,“备注”中注明的事件发生时会产生相应的信号,但并不是说该信号的产生就一定 发生了这个事件。事实上,任何进程都可以使用函数 kill( )来产生任何信号。

2 ,信号 SIGKILL 和 SIGSTOP 是两个特殊的信号,他们不能被忽略、阻塞或捕捉只 能按缺省动作来响应。换句话说,除了这两个信号之外的其他信号,接收信号的目标进程按照如下顺序来做出反应:

A) 如果该信号被阻塞,那么将该信号挂起,不对其做任何处理,等到解除对其阻塞为止。否则进入 B。

B) 如果该信号被捕捉,那么进一步判断捕捉的类型: 

B1) 如果设置了响应函数,那么执行该响应函数。

B2) 如果设置为忽略,那么直接丢弃该信号。否则进入 C。

C) 执行该信号的缺省动作。

5.4 kill函数和signal函数

进程间通信3_第10张图片

很多人对 kill( )抱有偏见,以为他就是要“杀死”某人,但这并非他的初衷,除非他发 送的是一个致命的信号,否则他只是“发送一个信号”的行为并不一定会致对方于死地。

可以在代码中使用kill函数

还可以在linux终端这样使用命令

kill -SIGSTOP 进程pid     这个意思就是停止某一个进程

kill -SIGCONT 进程id    继续某一个进程

还可以使用信号编号

kill -19 进程pid    这个意思就是停止某一个进程

kill -18 进程pid    继续某一个进程

进程间通信3_第11张图片

这个函数一般是跟 kill( )配套使用的, 目标进程必须先使用 signal( )来为某个信号设置 一个响应函数,或者设置忽略某个信号,才能改变信号的缺省行为,这个过程称为“信号的 捕捉” 。注意,对一个信号的“捕捉”可以重复进行,signal( )函数将会返回前一次设置的 信号响应函数指针。对于所谓的信号响应函数的接口,规定必须是:void (*)(int);

5.5 捕获信号练习

buhuo_w.c

#include 
#include               
#include 
#include 
#include 
#include 
#include            
#include 
#include 
#include 
#include 
#include 
#include 
#include

//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

#include 
#include               
#include 
#include 
#include 
#include 
#include            
#include 
#include 
#include 
#include 
#include 
#include 
#include

//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;
}

进程间通信3_第12张图片

编译运行

进程间通信3_第13张图片

再次运行依然可以

进程间通信3_第14张图片

6. 精灵进程

daemon.c

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

//精灵进程
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;
}

你可能感兴趣的:(进程线程,linux,c语言,算法)