文件与内存映射
将普通文件映射到内存中, 普通文件被映射到进程地址空间后,进程可以像访问普通内存一样对文件进行访问,不必再调用read 或 write 函数;
mmap
#include <sys/mman.h>
void *mmap(void *addr, size_t length, int prot, int flags,
int fd, off_t offset);
int munmap(void *addr, size_t length);
On success, mmap() returns a pointer to the mapped area.
On error, the value MAP_FAILED (that is, (void *) -1) is
returned, and errno is set to indicate the cause of the
error.
On success, munmap() returns 0. On failure, it returns
-1, and errno is set to indicate the cause of the error
(probably to EINVAL).
mmap机制:
在磁盘上建立一个文件,每个进程存储器里面单独开辟一个空间来进行映射。存储量大,但是进程间读写速度比主存慢;
shm 机制:
每个进程共享的内存都直接映射到实际物理存储器里面。shm 保存到物理存储器,实际的存储量直接反映到主存上;进程间访问速度较快,存储量不大;
同步进程
把异步环境下的一组并发进程因直接制约而互相发送消息、进行互相合作、互相等待,使得各进程按一定的速度执行的过程称为进程间的同步
信号量
获得共享资源,进程需执行以下操作:
-
测试控制该资源的信号量;
-
若此信号的值为正,进程可以使用该资源。进程将信号量值减一,表示它使用了一个资源单位;
-
若此信号量的为0, 则进程进入休眠状态, 直至信号量值大于0。进程被唤醒后,他返回至第一步;
为正确实实现信号量,信号量值的测试及减一操作应当是原子操作;
semget 获得信号量
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>int semget(key_t key, int nsems, int semflg);
成功,返回量信号id, 出错返回 -1;
nsems是该集合中的信号量数。如果创建新集合,必须指定nsems; 如果引用一个现存的集合,则将nsems指定为0;
semctl
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semctl(int semid, int semnum, int cmd, ...);
On failure, semctl() returns -1 with errno indicating the
error.
union semun {
int val; /* Value for SETVAL */
struct semid_ds buf; / Buffer for IPC_STAT
这是联合,而非指向联合的指针;
semop
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semop(int semid, struct sembuf *sops, size_t nsops);
If successful, semop() and semtimedop() return 0; otherwise they return -1 with errno indicating the error.
参数sops 是一个指针,指向一个信号量操作数组, 信号量操作由sembuff 结构表示;
struct sembuff{
unsigned short sem_num; /* semaphore number /
short sem_op; / semaphore operation /
short sem_flg; / operation flags */
}
nops规定该数组中操作的数量;
集合中的每个成员的操作有相应的sem_op值规定。可正可负也可为零。
- sem_op为正,对应进程释放占用的资源数,sem_op 加到信号量值上;
- sem_op 为负, 表示要获取该信号量控制的资源。
ex
#include "head.h"
#define NIS 3
union semun { // semctl 需定义的结构体;
int val; /* Value for SETVAL */
struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */
unsigned short *array; /* Array for GETALL, SETALL */
struct seminfo *__buf; /* Buffer for IPC_INFO
(Linux-specific) */
};
int creat_sem(const char *path, int pro_id, int sem_nu) { // 创建信号量
int sem_id;
key_t key = ftok(path, pro_id); // 生成key
if ((sem_id = semget(key, sem_nu, IPC_CREAT | 0666)) < 0) {
return -1;
}
return sem_id;
}
int init_sem(int sem_id, int sem_nu, int val) { // 初始化
union semun arg;
arg.val = val;
return semctl(sem_id, sem_nu, SETVAL, arg);
}
int P(int sem_id, int sem_nu) { // p操作 (-1)
struct sembuf sbuff;
sbuff.sem_num = sem_nu;
sbuff.sem_op = -1;
sbuff.sem_flg = SEM_UNDO;
if(semop(sem_id, &sbuff, 1) < 0) {
return -1;
}
return 0;
}
int V(int sem_id, int sem_nu) { // v 操作(+1)
struct sembuf sbuff;
sbuff.sem_num = sem_nu;
sbuff.sem_op = +1;
sbuff.sem_flg = SEM_UNDO;
if(semop(sem_id, &sbuff, 1) < 0) {
return -1;
}
return 0;
}
int main() {
int sem_id;
if (sem_id = creat_sem(".", 2021, 1)) {
perror("creat_sem");
exit(1);
}
int cunt = 0;
while(1) {
if (P(sem_id, 0) < 0) {
perror("P");
exit(1);
}
sleep(3);
cunt++;
if (V(sem_id, 0) < 0) {
perror("V");
exit(1);
printf("thins is %dth times!\n", cunt);
}
}
return 0;
}
消息队列
消息队列是消息的链接表,存放在内核中并由消息队列标识符标识。消息队列简称队列, 其他标识符为队列id。
msgget ---- 创建新队列或打开现存的队列;
msgsnd ---- 将新消息添加到队列尾端;
msgrcv ---- 从队列中取消息, 不一定以先进先出的次序,可以按消息类型字段取消息;
每个消息包含一个正长整型类型字段, 一个非负长度以及实际数据字节(对应长度),所有这些都将在将消息添加到队列时, 传送给msgsnd。
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgget(key_t key, int msgflg);
成功返回消息队列id, 出错返回 -1;
msgsnd
int msgsnd(int msqid, const void msgp, size_t msgsz, int msgflg);
成功返回0, 出错返回 -1;
ptr指向一个长整型数,包含了正的整形消息类型,其后跟着消息数据结构(nbytes是0,则无消息数据)若发送最长数据是512字节,可定义一下结构体:
struct msgbuf {
long mtype; / message type, must be > 0 /
char mtext[512]; / message data */
};
于是,ptr就是一个指向msgbuf 结构体的指针。
参数flag可指定为IPC_NOWAIT。若消息队列已满(或字节数以等于系统限制值),则指定NO_WAIT使得msgsnd立即出错返回EAGAIN;如果没有指定ipc_nowait,则进程阻塞直到出现以下情况:
有空间可以容纳要发送的消息;
从系统中删除了此队列, 此时返回eidrm;
捕捉到一个信号,并从信号处理程序返回;
msgrcv
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
ptr 和msgsnd 中一样,其后跟的是存放实际消息数据的缓冲区;
nbytes说明缓冲区的长度, 若返回的消息大于nbytes, 而且flag 中设置 MSG_NOERROR, 则消息被截短(不通知);如果没有这一设置,而消息又太长,则返回E2BIG;
type 指定哪一种消息:
type == 0 ----------返回队列中第一个消息;
type > 0 ---------- 返回队列中消息为type 的第一个消息;
type < 0 ---------- 返回队列中消息类型值小于或等于type 绝对值的消息,如果这种消息有若干,取类型类型值最小的消息;
type 非零用于以非先进先出次序读消息;
可指定flag 值为IPC_NOWAIT,使操作不阻塞。这样如果没有所指定类型的消息,msgrcv 返回-1, errno设置为ENOMSG。如果没有指定IPC_NOWAIT,进程阻塞直到出现以下情况:
有了指定类型的消息;
从系统中删了此队列,出错返回 -1,且设置errno 为 EIDRM,;
捕捉到一个信号并从信号处理程序返回,msgrcv 返回 -1, errno 设置为EINTR;
#include "head.h"
struct msgbuff {
long mtype;
char mtext[512];
};
void send_msg(int qid, int type, char *m_msg, int size) {
struct msgbuff msg;
msg.mtype = type;
strcpy(msg.mtext, m_msg);
if(msgsnd(qid, (void *)&msg, sizeof(msg.mtext), IPC_NOWAIT) == -1) {
perror("msgsnd");
exit(1);
}
}
void get_msg(int qid, int type) {
while (1) {
struct msgbuff msg;
bzero(&msg, sizeof(msg));
if (msgrcv(qid, (void *)&msg, sizeof(msg.mtext), type, MSG_NOERROR) == -1) {
perror("msgrcv");
exit(1);
}
}
printf("抢到资源!\n");
}
int main(int argc, char **argv) {
int msg_qid;
char mtext[512] = {0};
int opt, mode = 2, msgtype = 1; // mode == 1 -> send;
while((opt = getopt(argc, argv, "st:rm:")) != -1) { // 命令解析;
switch(opt) {
case 's':
mode = 1;
break;
case 'r':
mode = 2;
break;
case 't':
msgtype = atoi(optarg);
break;
case 'm':
strcpy(mtext, optarg);
break;
default:
fprintf(stderr, "Usage : %s -[s|r] -t type -m mtex \n", argv[0]);
exit(1);
}
}
if ((msg_qid = msgget(2021, IPC_CREAT | 0666)) < 0) {
perror("msgget");
exit(1);
}
if (mode == 1) {
send_msg(msg_qid, msgtype, mtext, sizeof(mtext)); // 发送消息;
}
else {
get_msg(msg_qid, msgtype); // 接收消息;
}
return 0;
}