内存映射,信号量,消息队列

文件与内存映射
将普通文件映射到内存中, 普通文件被映射到进程地址空间后,进程可以像访问普通内存一样对文件进行访问,不必再调用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 保存到物理存储器,实际的存储量直接反映到主存上;进程间访问速度较快,存储量不大;

同步进程
把异步环境下的一组并发进程因直接制约而互相发送消息、进行互相合作、互相等待,使得各进程按一定的速度执行的过程称为进程间的同步

信号量
获得共享资源,进程需执行以下操作:

  1. 测试控制该资源的信号量;

  2. 若此信号的值为正,进程可以使用该资源。进程将信号量值减一,表示它使用了一个资源单位;

  3. 若此信号量的为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值规定。可正可负也可为零。

  1. sem_op为正,对应进程释放占用的资源数,sem_op 加到信号量值上;
  2. 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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值