Linux应用程序开(day16) ── 共享内存、信号

内容:

共享内存

共享内I存也是两个进程间通讯的一种方式,是进程间共享数据的最快的一种方法。

获取共享内存标识符

int shmget(key_t key,size_t size,int shmflg)

依赖的头文件:

#include <sys/ipc.h>
#include <sys/shm.h>

功能:创建或者打开一块共享内存
参数:
key:IPC键值
size:指定的共享内存的长度
shflg:控制条件

名称作用
IPC_CREAT如果不存在就创建
IPC_EXCL如果已经存在则返回失败
IPC_NOWAIT调用进程会立即返回。若发生错误则返回-1。
SHM_R可读
SHM_W可写

返回值:成功返回共享内存标识符,失败返回-1

共享内存映射

void *shmat(int shmid, const void *shmaddr,int shmflg);

依赖的头文件:

#include <sys/ipc.h>
#include <sys/shm.h>

功能:将一个共享内存段映射到调用进程的数据段中。
参数:
shmid:共享内存标识符。
shmaddr:共享内存映射地址(若为NULL则由系统自动指定),推荐使用NULL。
shmflg:控制条件

名称作用
0共享内存具有可读可写权限(推荐使用)。
SHM_RDONLY只读。
SHM_RND(shmaddr非空时才有效)没有指定SHM_RND则此段连接到shmaddr所指定的地址上(shmaddr必需页对齐)。指定了SHM_RND则此段连接到shmaddr-shmaddr%SHMLAB 所表示的地址上。

返回值:成功返回共享内存段首地址,失败返回-1

解除共享内存映射

int shmdt(const void *shmaddr);

依赖的头文件:

#include <sys/types.h>
#include <sys/shm.h>

功能:将共享内存和当前进程断开联系。
参数:
shmaddr:共享内存映射地址。
返回值:成功返回0,失败返回-1

共享内存控制

int shmctl(int shmid, int cmd,struct shmid_ds *buf);

依赖的头文件:

#include <sys/ipc.h>
#include <sys/shm.h>

功能:共享内存空间的控制。
参数:
shmid:共享内存标识符
buf:shmid_ds数据类型的地址,用来存放或更改消息队列的属性。可以使用NULL。
cmd:控制条件

名称作用
IPC_RMID删除。
IPC_SET设置shmid_ds参数。
IPC_STAT保存shmid_ds参数。
SHM_LOCK锁定共享内存段(超级用户)。
SHM_UNLOCK解锁共享内存段

返回值:成功返回0,失败返回-1

例程

写一个正常读写的例程,为了使代码更加易读,这里删掉了判断错误的条件判断,然后再读程序中读两次来证明读取内容并不会讲共享内存的内容删除。
读程序:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>

void main(){
        int shmid,ret;
        key_t key;
        char *shmadd;

        key = ftok(".",2012);
        //创建共享内存
        shmid = shmget(key,2048, SHM_R|SHM_W|IPC_CREAT);
        //映射
        shmadd = shmat(shmid,NULL,0);
        printf("key:%d , shmid %d \n",key,shmid);
        bzero(shmadd,2048);
        //写数据
        strcpy(shmadd,"hello");
        printf("写数据成功\n");
}

写程序:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
void main(){
        int shmid;
        key_t key;
        char *shmadd;

        key = ftok(".",2012);
        system("ipcs -m");
        shmid = shmget(key,2048,SHM_R|SHM_W);
        shmadd = shmat(shmid,NULL,0);
        printf("读到的数据【%s】\n",shmadd);
        printf("读到的数据【%s】\n",shmadd);
        //将进程和共享内存分离
        shmdt(shmadd);
        //将共享内存删除
        shmctl(shmid,IPC_RMID,NULL);
        system("ipcs -m");

}

运行结果:
在这里插入图片描述

信号

介绍

信号是一种软件层次上的对中断机制的一种模拟,一般都是让某进程中止,可以使用kill -l命令来查看当前系统的信号和相应的编码。在这里插入图片描述
最常用的信号:

按键信号
Ctrl + cSIGINT
Ctrl + \SIGQUIT
Ctrl + zSIGSTOP

基本操作

kill()函数
int kill(pid_t pid, int signum);

依赖的头文件:

#include <sys/types.h>
#include <signal.h>

功能:给指定的进程发送信号
参数:
pid:目标进程的pid
signum:信号的编号

pid取值效果
pid>0将信号传送给进程ID为pid的进程。
pid=0将信号传送给当前进程所在进程组中的所有进程。
pid=-1将信号传送给系统内所有的进程。
pid<-1将信号传给指定进程组的所有进程。这个进程组号等于pid的绝对值。

返回值:成功返回0,失败返回-1
例程:

void main(){
        pid_t pid;
        pid = fork();
        if(pid == 0)
        {
                printf("子进程创建成功\n");
                while(1)
                {
                        printf("子进程正在运行\n");
                        sleep(1);
                }
        }else
        {
                sleep(2);
                kill(pid,SIGINT);
        }
}
alarm()函数
unsigned int alarm(unsigned int seconds);

依赖的头文件:

#include <unistd.h>

功能:到达指定时间以后向调用进程发送一个SIGALRM信号,该信号默认杀死调用进程
参数:指定的时间
返回值:若以前没有设置过定时器或者以前设置的定时器已经到达时间,则返回0,。否则返回定时器剩余秒数,并且重置秒数。
例程:

void main()
{
        printf("程序开始运行\n");
        alarm(2);
        printf("已经设置两秒的定时\n");
        while(1);
}
raise()函数
int raise(int signum);

依赖的头文件:

#include <signal.h>

功能:给调用进程本身一个信号
参数:信号的编号
返回值:成功返回0,失败返回-1.
例程:

void main()
{
        raise(SIGINT);
        while(1);
}
pause()函数
int pause(void);

依赖的头文件:

#include <unistd.h>

功能:将进程挂起,直到有信号发过来
返回值:成功返回0,失败返回-1.
例程:

void main()
{
        pause();
        while(1);
}
abort()函数
void abort(void);

依赖的头文件:

#include <stdlib.h>

功能:向进程发送SIGABRT信号,默认情况下进程会退出。
注意:
即使SIGABRT信号被加入阻塞集,一旦进程调用了abort函数,进程也还是会被终止,且在终止前会刷新缓冲区,关文件描述符。

信号处理函数

函数定义:

#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum,sighandler_t handler);

功能:注册信号处理函数,当信号来临的时候,执行注册的函数
参数:
signum:信号编号
handler的取值:
忽略该信号:SIG_IGN
执行系统默认动作:SIG_DFL
自定义信号处理函数:处理函数名
返回值:成功返回此信号上一次注册的信号处理函数的地址。失败返回SIG_ERR
例程:

typedef void (*sighandler_t)(int);

void fun1(int signo)
{
	printf("ctrl+c fun1\n");
}
void fun2(int signo)
{
	printf("ctrl+c fun2\n");
}

void fun3(int signo)
{
	printf("ctrl+/ fun3\n");
}
void fun4(int signo)
{
	printf("ctrl+/ fun4\n");
}

void main()
{
	sighandler_t previous = NULL;
	previous = signal(SIGINT,fun1);
	printf("fun1:%p\n",previous);

	previous = signal(SIGINT,fun2);
	printf("fun2:%p\n",previous);
	
	previous = signal(SIGQUIT,fun3);
	printf("fun3:%p\n",previous);

	previous = signal(SIGQUIT,fun4);
	printf("fun4:%p\n",previous);

	pause();
	pause();
	while(1)
	{
		sleep(1);
		printf("while循环正在运行\n");
	}

}
信号集

一个用户进程常常需要对多个信号做出处理。为了方便对多个信号进行处理,在Linux系统中引入了信号集。信号集是用来表示多个信号的数据类型。

sigemptyset()函数
#include <signal.h>
int sigemptyset(sigset_t *set)

功能:初始化一个空的信号集
参数:集合的地址
返回值:成功返回0,失败返回-1

sigfillset()函数
#include <signal.h>
int sigfillset(sigset_t *set)

功能:初始化一个信号集,该信号集包含所有信号。
参数:集合的地址
返回值:成功返回0,失败返回-1

sigismember()函数
#include <signal.h>
int sigismember(const sigset_t *set,intsignum);

功能:查询信号是否在信号集之中
参数:信号集,信号
返回值:在信号集中返回1,不在信号集中返回0,失败返回-1

sigaddset()函数
#include <signal.h>
int sigaddset(sigset_t *set, int signum);

功能:将信号加到信号集之中
参数:信号集,信号
返回值:成功返回0,失败返回-1

sigdelset()函数
#include <signal.h>
int sigdelset(sigset_t *set, int signum);

功能:将信号从信号集之中删除
参数:信号集,信号
返回值:成功返回0,失败返回-1

例程
void main()
{
        sigset_t set1,set2;
        int ret = 0;

        sigemptyset(&set1);
        sigaddset(&set1,SIGINT);
        sigaddset(&set1,SIGQUIT);
        ret = sigismember(&set1,SIGQUIT);
        printf("sigquit是否在信号集?【%d】\n",ret);
        sigdelset(&set1,SIGQUIT);
        ret = sigismember(&set1,SIGQUIT);
        printf("sigquit是否在信号集?【%d】\n",ret);


        sigfillset(&set2);
        ret = sigismember(&set2,SIGQUIT);
        printf("sigquit是否在信号集?【%d】\n",ret);
}

作业

作业一

1.编程实现子进程调用pause()函数暂停执行,主进程调用kill()函数杀死子进程的功能,并打印两个进程的进程号和父进程号。
过程:首先使用fork函数创建一个子进程,然后在子进程中输出pid和ppid,并且使用pause函数将子进程挂起。在父进程中延迟1秒,防止直接把子进程杀死了,然后输出父进程的pid和ppid,最后把子进程杀死。

#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <signal.h>
void main()
{
        pid_t pid;

        pid = fork();
        if(pid<0)
        {
                perror("fork函数创建失败\n");
        }
        if(pid == 0)
        {
                printf("子进程创建成功,进入挂起状态\n");
                printf("子进程的进程号:%d,子进程的父进程号:%d\n",getpid(),getppid());
                pause();
                printf("测试是否挂起成功\n");
        }else
        {
                printf("父进程创建成功\n");
                printf("父进程的进程号:%d,父进程的父进程号:%d\n",getpid(),getppid());
                sleep(1);
                kill(pid,SIGINT);
        }
}

作业二

调用signal()函数,实现接收到键盘输入的SININT信号后,打印输出"hello world"的功能。
解析:只需要简单的调用signal函数即可。关于signal详细内容可看上面【信号处理函数】部分。
代码:

void fun(int signo)
{
        printf("\n");
        printf("hello world\n");
}
void main()
{
        signal(SIGINT,fun);
        while(1);

}

源码下载

今天所用到的代码已上传GitHub,day16.zip 点此跳转


谢谢大家的观看,如有错误请指正,谢谢!CSDN记录成长!

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值