一、信号进阶:发送信号、pause、alarm定时器
1.1 kill() ------ 向指定进程发送信号
kill不只是杀死进程,本质功能:给指定PID的进程发送信号,可以发送任意合法信号。
头文件
#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid, int sig);
参数说明
-
pid:接收信号的进程ID
-
pid > 0:信号发送给pid对应的进程
-
pid = 0:发送给当前进程组所有进程
-
pid = -1:发送给系统内有权限的全部进程
-
sig:信号编号(如`SIGINT`、`SIGUSR1`)
返回值
- 成功返回0;失败返回1,设置errno。
示例:kill(pid, SIGKILL);强制杀死目标进程。
1.2 pause() ------ 进程休眠等待信号唤醒
#include <unistd.h>
int pause(void);
功能:将当前进程挂起休眠,直到捕获到一个信号,执行完信号处理函数之后pause才返回。
返回值:永远返回1,errno设置为EINTR,表示被信号中断。
使用场景:进程休眠等待外部信号触发业务逻辑。
注意:必须提前注册信号捕获处理函数,如果收到的信号是默认终止行为,进程直接退出,pause不会返回。
1.3 alarm() 定时信号
#include <unistd.h>
unsigned int alarm(unsigned int seconds);
功能:seconds秒之后,向自己进程发送SIGALRM信号。
-
SIGALRM默认动作:终止进程。
-
如果多次调用alarm,新调用会覆盖上一次定时器。
返回值:返回上一次剩余倒计时秒数;之前没有设置返回0。
简单示例:
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
void alarm_handler(int sig)
{
printf("收到定时SIGALRM信号!\n");
}
int main(void)
{
signal(SIGALRM, alarm_handler);
alarm(3); //3秒后发出SIGALRM
pause();
printf("程序继续往下执行\n");
return 0;
}
```
二、SystemV IPC通用知识点
2.1 ftok() 生成IPC对象key键值
SystemV IPC(消息队列、共享内存、信号量集)依靠`key_t`键值,多个进程使用同一个key,就可以访问同一个内核IPC对象。
#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok(const char *pathname, int proj_id);
-
pathname:必须是真实存在的文件路径;
-
proj_id:项目ID,0255的整数;
返回值:成功返回key;失败返回1。
原理:利用文件inode + proj_id计算出唯一key;同一个文件+同一个proj_id得到的key完全相同。
2.2 IPC命令行调试工具
ipcs -a # 查看系统所有SystemV IPC对象
删除消息队列
ipcrm -q 消息队列ID
ipcrm -Q key值
删除共享内存
ipcrm -m shmid
ipcrm -M key
删除信号量集
ipcrm -s semid
ipcrm -S key
三、消息队列 Message Queue
消息队列是**内核维护的消息链表**,每条消息带有消息类型`mtype`;进程可以按照消息类型选择性读取消息,不再像管道只能读取字节流。
特点:
-
消息有类型字段,可以按类型筛选接收;
-
收发进程不需要同时运行,消息缓存在内核;
-
进程结束消息队列不会自动销毁,需要手动删除。
3.1 核心结构体
struct msgbuf {
long mtype; /* 消息类型,必须大于0 */
char mtext1; /* 消息正文,长度自定义 */
};
3.2 全套API
① msgget() 创建/获取消息队列ID
#include <sys/msg.h>
int msgget(key_t key, int msgflg);
-
key:ftok生成的key;
-
msgflg:IPC_CREAT | 0664,不存在则创建,存在直接获取;
返回值:成功返回消息队列ID(msqid);失败1。
② msgsnd() 发送消息
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
-
msqid:msgget返回ID;
-
msgp:指向msgbuf消息结构体;
-
msgsz:消息正文mtext字节大小,不包含mtype;
-
msgflg:填0代表阻塞发送。
返回值:成功0,失败1。
③ msgrcv() 接收消息
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
-
msgtyp:消息类型
-
msgtyp = 0:读取队列第一条消息;
-
msgtyp >0:读取mtype等于msgtyp的消息;
-
msgflg:0阻塞读取;
返回值:成功返回读到正文字节数,失败1。
④ msgctl() 控制消息队列(删除/获取属性)
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
-
cmd:IPC_RMID删除消息队列,此时buf传NULL;
-
IPC_STAT获取队列属性。
四、共享内存 Shared Memory
SystemV共享内存:Linux速度最高效的IPC通信方式。
原理:内核开辟一块物理内存,多个进程把同一块内存映射到各自用户虚拟地址;进程直接读写这块内存,**减少用户态和内核态之间的数据拷贝**。
注意:共享内存本身不带同步互斥!多进程并发读写会出现竞争,一般搭配信号量做保护。
API接口
① shmget 创建/获取共享内存ID
#include <sys/shm.h>
int shmget(key_t key, size_t size, int shmflg);
-
size:共享内存字节大小,建议4096(一页)整数倍;
-
shmflg:IPC_CREAT | 0664;
返回:成功shmid,失败1。
② shmat 映射共享内存到进程用户空间
void *shmat(int shmid, const void *shmaddr, int shmflg);
-
shmaddr:填NULL,由内核自动选择映射地址;
-
shmflg:0读写;SHM_RDONLY只读;
返回值:成功返回映射后的虚拟内存首地址;失败返回(void *)-1。
拿到返回指针,进程就可以直接读写这块内存,就像普通数组一样。
③ shmdt 解除映射
int shmdt(const void *shmaddr);
功能:解除当前进程和共享内存的映射关系。
shmdt仅仅解除映射,不会删除内核里面的共享内存对象!
④ shmctl 删除共享内存对象
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
cmd传IPC_RMID,buf填NULL,删除内核共享内存对象。