接着上一篇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机制剩下的全部内容。管道适合简单的数据流,信号适合异步通知,消息队列带类型可以做优先级,共享内存效率最高但需要额外同步,根据场景选就行。
网硕互联帮助中心



评论前必须登录!
注册