云计算百科
云计算领域专业知识百科平台

Linux学习笔记:信号、消息队列与共享内存

接着上一篇IPC的内容,这篇把剩下的几种通信方式整理一下。进程间用户空间互相独立,要通信就得借助内核,这就是IPC。前面写了管道,这篇从信号开始。

 

一、信号

 

信号是进程间的通知机制,不携带数据,属于异步通知,也叫软中断。系统定义了很多信号,常用的几个得记住:

 

SIGINT(2):Ctrl+C,打断进程

SIGQUIT(3):Ctrl+\\,结束进程

SIGKILL(9):强制结束进程,这个不能被捕获和忽略

SIGSEGV(11):段错误

SIGPIPE(13):管道破裂

SIGALRM(14):定时时间到

SIGCHLD(17):子进程结束时发给父进程

SIGCONT(18):让停止态的进程继续执行

SIGSTOP(19):强制让运行态进程暂停

SIGTSTP(20):Ctrl+Z,进程进入暂停态

SIGUSR1(10)、SIGUSR2(12):预留给程序员自己用

 

信号有三种处理方式:忽略、缺省(按系统默认来,大多数默认是结束进程)、捕获(自己写处理函数)。不注册的话自动按缺省处理,信号尽量早注册。

 

相关函数就四个:

 

// 注册信号

sighandler_t signal(int signum, sighandler_t handler);

// handler: SIG_IGN忽略 / SIG_DFL缺省 / 自定义函数

 

// 给进程发信号

int kill(pid_t pid, int sig);

 

// 挂起进程,可被捕获的信号唤醒

void pause(void);

 

// 定时seconds秒后发SIGALRM

unsigned int alarm(unsigned int seconds);

 

写个简单例子,捕获Ctrl+C:

 

#include <stdio.h>

#include <signal.h>

#include <unistd.h>

 

void handler(int sig)

{

    printf("收到信号%d,退出\\n", sig);

    _exit(0);

}

 

int main(void)

{

    signal(SIGINT, handler);

    while (1)

    {

        printf("程序运行中…\\n");

        sleep(1);

    }

    return 0;

}

 

 

注意SIGKILL和SIGSTOP不能被捕获和忽略,所以kill -9一定能杀掉进程。

 

二、消息队列

 

消息队列是System V的IPC对象,思路是把通信数据封装成消息体来发送,还增加了消息类型,可以按类型设置优先级。

 

操作流程:ftok获取key → msgget创建队列 → msgsnd发消息 → msgrcv收消息 → msgctl删除。

 

// 产生IPC对象的key值

key_t ftok(const char *pathname, int proj_id);

 

// 创建消息队列,msgflg一般传 IPC_CREAT|0664

int msgget(key_t key, int msgflg);

 

// 发送消息

int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);

 

// 接收消息,msgtyp指定接收哪种类型

ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);

 

// 操作消息队列,删除用 IPC_RMID,buf传NULL

int msgctl(int msqid, int cmd, struct msqid_ds *buf);

 

消息体的结构是固定的,类型必须大于0:

 

struct msgbuf {

    long mtype; // 消息类型,必须 > 0

    char mtext[128]; // 消息正文,大小自己定

};

 

 

发送端大概这么写:

 

key_t key = ftok(".", 100);

int msgid = msgget(key, IPC_CREAT | 0664);

 

struct msgbuf msg;

msg.mtype = 1;

strcpy(msg.mtext, "hello message queue");

msgsnd(msgid, &msg, sizeof(msg.mtext), 0);

 

 

接收端:

 

struct msgbuf msg;

msgrcv(msgid, &msg, sizeof(msg.mtext), 1, 0); // 只收类型为1的

printf("收到: %s\\n", msg.mtext);

 

用完删掉: msgctl(msgid, IPC_RMID, NULL); 

 

System V的IPC对象不会随进程结束自动消失,得手动删。常用命令:

 

bash

ipcs -a # 查看系统中所有IPC对象

ipcrm -q msgid # 按ID删消息队列

ipcrm -Q msgkey # 按key删消息队列

 

三、共享内存

 

共享内存是进程间效率最高的通信方式,原理是利用内存映射,把同一段物理内存映射到多个进程的用户空间,避免了数据的反复拷贝。

 

操作就三步:shmget创建 → shmat映射 → shmctl删除。

 

// 创建共享内存,size一般是4096的整数倍

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

 

// 建立映射,shmaddr传NULL让系统选地址,返回映射后的首地址

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

 

// 操作共享内存,删除用 IPC_RMID

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

 

写个简单例子,一个进程写,一个进程读:

 

写端:

 

key_t key = ftok(".", 200);

int shmid = shmget(key, 4096, IPC_CREAT | 0664);

char *p = (char *)shmat(shmid, NULL, 0); // 映射到用户空间

 

strcpy(p, "hello shared memory"); // 直接写

 

读端:

 

key_t key = ftok(".", 200);

int shmid = shmget(key, 4096, IPC_CREAT | 0664);

char *p = (char *)shmat(shmid, NULL, SHM_RDONLY); // 只读映射

 

printf("读到: %s\\n", p);

 

用完解除映射、删除:

 

shmdt(p); // 解除映射

shmctl(shmid, IPC_RMID, NULL); // 删除共享内存

 

共享内存效率高,但本身没有同步机制,多个进程同时读写会出问题,一般要配合信号量或者互斥锁来用。删除命令和消息队列类似:

 

bash

ipcrm -m shmid # 按ID删共享内存

ipcrm -M shmkey # 按key删共享内存

 

四、信号量集

 

信号量集也就是信号量数组,主要用于进程间同步。和线程里学的信号量思路一样,通过P操作(申请)和V操作(释放)来控制资源访问,保证多个进程执行时有先后顺序。

 

以上就是IPC机制剩下的全部内容。管道适合简单的数据流,信号适合异步通知,消息队列带类型可以做优先级,共享内存效率最高但需要额外同步,根据场景选就行。

赞(0)
未经允许不得转载:网硕互联帮助中心 » Linux学习笔记:信号、消息队列与共享内存
分享到: 更多 (0)

评论 抢沙发

评论前必须登录!