美文网首页乱七八糟风暴
Linux-C-day-4-进程间通信-消息队列||信号量 ||

Linux-C-day-4-进程间通信-消息队列||信号量 ||

作者: 秋风弄影 | 来源:发表于2017-06-06 00:36 被阅读0次

消息队列

消息队列是在内核中实现的,并且是具有一定的优先级的一种进程间通信模型

POSIX PIC消息队列

在unpv22e,ch 5.1--5.5可以查看这些知识,POSIX消息队列使用的头函数是mqueue.h;使用的库文件为librt.so;消息队列通过结构体:struct mq_arrt 来定义消息队列属性,结构体中选项包含的有:mq_flags:标志,在mq_open时被初始化,mq_setattr可以进行设置;这个值通常为0或者O_NONBLOCK;mq_maxmsg:表示的是队列中消息的最大个数,只能在mq_open时被初始化;mq_msgsize:表示队列中每个消息的最大值;mq_curmsgs:表示当前队列消息长度,在mq_getattr 时进行获取;
关于消息队列的八种操作:
 创建消息队列:mqd_t mq_open(const char *name, int oflag,mode_t mode,struct mq_attr *attr);name:表示posix IPC名字;oflag:表示标志,O_CREAT:表示在没有时,创建该对象;O_EXCL:表示如果O_CREAT指定,但是name不存在,就返回错误信息;O_NONBLOCK:表示以非阻塞方式打开消息队列;O_RDONLY:表示只读;O_RDWR:表示读写;O_WRONLY:表示只写;mode:权限:S_IWUSR:表示用户属主写权限;S_IRUSR:表示用户属主读权限;S_IWGRP:表示组成员写权限;S_IRGRP:表示组成员读权限;S_IWOTH:其他用户写权限;S_IROTH:表示其他用户读权限;attr:表示队列属性:attr.mq_falg = 0;表示阻塞;attr.mq_flag = NONBLOCK,表示非阻塞;返回值:-1,表示出错;返回值为其他表示消息队列描述符;
mqcreate.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <mqueue.h>
#include <fcntl.h>
#include <sys/stat.h>
#define FILE_MODE (S_IWUSR|S_IRUSR|S_IRGRP|S_IROTH) //0644
int main(int argc,char* argv[]){
    int c,flag=0;
    long maxmsg = 10;
    long msglen = 8192;
    while((c=getopt(argc,argv,"q:l:"))!=-1){
        switch(c){
        case 'q':
            maxmsg = atoi(optarg);
            break;
        case 'l':
            msglen = atoi(optarg);
            break;
        }
    }

    if(optind != argc-1){
        printf("usage:%s [-q <maxmsg>] [-l <msglen>] <mqname>\n",argv[0]);
        return 1;
    }
    struct mq_attr attr;
    attr.mq_maxmsg = maxmsg;
    attr.mq_msgsize = msglen;
    mqd_t mqd = mq_open(argv[optind],O_CREAT,FILE_MODE,&attr);
    if(-1 == mqd){
        perror("mq_open error");
        return 1;
    }
}

删除消息队列:int mq_unlink(const char *name);name:表示posixIPC名字;返回值-1表示失败,0表示成功;
mq_unlink.c

#include <stdio.h>
#include <stdlib.h>
#include <mqueue.h>
#include <fcntl.h>
#include <sys/stat.h>
int main(){
    mq_unlink("/tmp.test");
}

打开消息队列:mqd_t mq_open(const char *name,int oflag);name:posix IPC表示名字;oflag:表示标志位,O_RDONLY,只读;O_RDWR:表示读写;O_WRONLY:表示只写;返回值为-1时表示出错,返回值为其他表示描述符;
队列的打开函数一般和发送或者接收一起出现,就写在一个函数里面;
 关闭消息队列:int mq_close(mqd_t mqdes);其中mqdes表示消息队列描述符;返回值-1表示出错,0表示成功;
 设置消息队列属性:int mq_setattr(mqd_t mqdes,struct mq_attr *newattr,struct mq_attr *oldattr);mqdes:表示消息队列描述符;newattr:用于设置新属性,但是只能用于设置mq_falgs,其中0表示阻塞,NONBLOCK表示非阻塞;
oldattr:表示用于设置旧属性;函数返回值-1表示出错,返回值0表示成功;
mqsetattr.c:

#include <stdio.h>
#include <stdlib.h>
#include <mqueue.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
int main(){
    mqd_t mqd = mq_open("/tmp.test",O_RDWR);
    if(-1 == mqd){
        perror("mq_open error");
        return;
    }
    struct mq_attr new_attr;
    bzero(&new_attr,sizeof(new_attr));

    new_attr.mq_flags = O_NONBLOCK;
    struct mq_attr attr;
    if(-1 == mq_setattr(mqd,&new_attr,&attr)){
        perror("mq_setattr error");
        return 1;
    }
    printf("flag:%ld,Max msg:%ld,Max msgsize:%ld,Cur msgnun:%ld\n",attr.mq_flags,attr.mq_maxmsg,attr.mq_msgsize,attr.mq_curmsgs);
}

获取消息队列属性:int mq_getattr(mqd_t mqdes, struct mqdes,struct mq_attr *attr);mqdes:表示消息队列描述符;attr:表示属性信息;返回值:-1表示出错信息;0表示成功;
mqgetattr.c

#include <stdio.h>
#include <stdlib.h>
#include <mqueue.h>
#include <fcntl.h>
#include <sys/stat.h>
int main(int argc,char* argv[]){
    mqd_t mqd = mq_open(argv[1],O_RDONLY);
    if(-1 == mqd){
        perror("mq_open error");
        return;
    }
    struct mq_attr attr;
    mq_getattr(mqd,&attr);
    printf("flag:%ld,Max msg:%ld,Max msgsize:%ld,Cur msgnun:%ld\n",attr.mq_flags,attr.mq_maxmsg,attr.mq_msgsize,attr.mq_curmsgs);
}

发送消息:int mq_send(mqd_t mqdes,const char *msg_ptr,size_t msg_len,unsigned msg_prio);msg_ptr:表示消息队列的指针;msg_len:表示消息长度,但是不能大于属性值mq_msgsize的值;msg_prio:表示优先级,消息在队列中将按照优先级大小顺序来排列消息;如果消息队列已满,mq_send()函数将阻塞,知道油客埇的空间再次允许放置消息;如果O_NONBLOCK被指定,mq_send()那么将不会被阻塞,而是返回EAGAIN的值;
mqsnd.c:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <mqueue.h>
#include <fcntl.h>
#include <string.h>
#include <sys/stat.h>
#define FILE_MODE (S_IWUSR|S_IRUSR|S_IRGRP|S_IROTH) //0644
int main(int argc,char* argv[]){
    int c,flags=O_WRONLY;
    while((c=getopt(argc,argv,"n"))!=-1){
        switch(c){
        case 'n':
            flags|=O_NONBLOCK;
            break;
        }
    }
    if(optind != argc-1){
        printf("usage:%s [-n] <mqname> <message> <prio>\n");
        return 1;
    }
    mqd_t mqd = mq_open(argv[optind],flags);
    if(-1 == mqd){
        perror("mq_open error");
        return 1;
    }
    if(-1 == mq_send(mqd,argv[optind+1],strlen(argv[optind+1])+1,atoi(argv[optind+2]))){
        perror("mq_send error");
        return 1;
    }
}

接收消息:ssize_t mq_recceive(mqd_t mqdes,char *msg_ptr,size_t msg_len,unsigned *msg_len,unsigned *msg_prio);msg_prt:表示消息队列的指针;msg_len:表示消息长度,不能够大于属性值mq_msgsize的值;msg_prio:表示优先级,消息在队列中将按照优先级大小顺序来排列消息;返回值:-1表示出错,正数表示接收到消息的长度;如果队列为空,mq_receive()函数将阻塞,知道消息队列中有新的消息。如果O_NONBLOCK被指定,mq_receive()将不会阻塞,而是返回EAGAIN错误;
mqrecv.c:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <mqueue.h>
#include <fcntl.h>
#include <sys/stat.h>
#define FILE_MODE (S_IWUSR|S_IRUSR|S_IRGRP|S_IROTH) //0644
int main(int argc,char* argv[]){
    int c,flags=O_RDONLY;
    while((c=getopt(argc,argv,"n"))!=-1){
        switch(c){
        case 'n':
            flags|=O_NONBLOCK;
            break;
        }
    }
    if(optind != argc-1){
        printf("usage:%s [-n] <mqname>\n");
        return 1;
    }
    mqd_t mqd = mq_open(argv[optind],flags);
    if(-1 == mqd){
        perror("mq_open error");
        return 1;
    }
    char buf[BUFSIZ];
    int prio;
    if(-1 == mq_receive(mqd,buf,BUFSIZ,&prio)){
        perror("mq_send error");
        return 1;
    }
    printf("msg:%s\nprio:%d\n",buf,prio);
}

对于共享内存的查看:man mq_overview,ls /dev/mqueue;cat /dev/mqueue/PIC名字;

POSIX共享内存

需要包含的头文件sys/mman.h 需要包含的库文件是librt.so
创建共享内存:int shm_open(const char *name,int oflag,mode_t mode);name:表示posix IPC的名字;ofloag:表示标志,O_CREAT:表示没有的话,创建该对象;
O_EXCL:如果O_CREAT指定,但是name不存在,就返回错误;O_RDONLY:表示只读;O_RDWR:表示读写;O_TRUNC:表示如果存在就截断;mode:表示权限:S_IWUSR:表示用户/属主写权限;S_IRUSR:表示用户/属主读权限;S_IR、WGRP:表示组成员写权限;S_IRGRP:表示组成员读权限;S_IWOTH:表示其他用户写权限;S_IROTH:表示其他用户读权限;函数的返回值-1表示出错;返回值为其他表示共享内存描述符;
shmcreate.c

#include <stdio.h>
#include <unistd.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <string.h>

int main(int argc,char* argv[]){
    int fd = shm_open(argv[1],O_CREAT|O_RDWR,0644);
    ftruncate(fd,atoi(argv[2]));
    void* buf = NULL;
    if(( buf =  mmap(NULL,BUFSIZ,PROT_WRITE,MAP_SHARED,fd,0)) == MAP_FAILED){
        perror("mmap error\n");
        return 1;
    }
}

删除int shm_unlink(const char *name);name:表示posix IPC名字;返回值:-1表示出错,0表示成功;
shmunlink.c

#include <stdio.h>
#include <unistd.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <string.h>

int main(int argc,char* argv[]){
    shm_unlink(argv[1]);
}

修改文件的大小:int fruncate(int fd, off_t lenght),fd:表示文件描述符;length:表示文件大小,如果原来的文件带下比参数length大,超过的部分就会被删除;返回值:0表示成功;-1:失败;这个函数用户设置共享内存的大小,fruncate(fd,len);
fruncate.c:

#include <stdio.h>
#include <unistd.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <string.h>

int main(int argc,char* argv[]){
    int fd = shm_open(argv[1],O_CREAT|O_RDWR,0644);
    ftruncate(fd,atoi(argv[2]));
    void* buf = NULL;
    if(( buf =  mmap(NULL,BUFSIZ,PROT_WRITE,MAP_SHARED,fd,0)) == MAP_FAILED){
        perror("mmap error\n");
        return 1;
    }
}

获取文件信息:int fstat(int fd,struct stat *buf),fd:表示文件描述符;buf:是一个结构体,里面的参数分别表示:st_mode:表示文件对应的模式,文件,目录等;st_size:表示普通文件,对应的文件字节数;st_atime:文件最后被访问的时间;st_mtime:文件最后被修改访问的时间;st_ctime:文件状态改变的时间;st_uid:文件所有者;st_gid:文件所有者对应的组;st_blksize:文件内容对应的块大小;st_blksize:文件内容对应的块大小;st_blocks:内容对应的块数量;st_ino:inode节点号;st_dev:设备号码;st_rdev:特殊设备号码;st_nlink:文件的连接数;
 st_mode:S_IFREG:表示一般文件;S_IFDIR:表示目录;S_IFSOCK:表示socket文件;S_IFIFO:表示先进先出;S_IFLINK:表示符号链接;S_IFCHR:表示字符设置;可以使用这些宏定义来判断文件的类型;S_ISLINK(st_mode),S_ISREG(st_mode):判断是否为一般文件;S_ISDIR(st_mode):判断是否为目录;S_ISCHR(st_mode):判断是否为字符文件;S_ISBLK(st_mode):判断是否为先进先出文件;S_ISSOCK(st_mode):判断是否为socket;S_IFMT:文件类型的位遮罩;S_IFBLK:区块装置;S_IUID:文件的(set user-id on execution)位;S_ISGID:文件的(set group-id on execution);S_ISVTX:文件的sticky位;
 st_size:表示文件的大小;st_uid:表示属主ID;st_uid:属主ID;st_guid:表示组ID;
 返回值:0表示成功;-1表示失败;
暂时没有代码文件;
 建立内存映射:void * mmap(void * start,size_t length, int prot,int flags, int fd, off_t offset):start:映射区的开始地址,参数如果设置成NULL,表示有系统设置映射区的起始地址,如果考虑系统的可移植性,必须设置成为NULL;lenght:表示映射区的长度,单位是字节,但不足一页按照一页内存进行处理;prot:内存保护装置 ;PROT_EXEC:表示页内容可以被执行;PROT_READ:表示页内容可以被读取;PROT_WAITE:表示页面内容可以被写入;PROT_NONE:表示页面内容不可以被访问;但是不能够与文件的打开模式冲突;flags:映射对象的类型,MAP_SHARED:表示变动共享;MAP_PRIVATE:表示变动私有;MAP_ANON:匿名内存映射;fd:文件描述符,不能使套接字和中断的fd,-1表示匿名内存映射;
off_toset:表示被映射对象内容的起点;返回值:MAP_FAILED:表示失败;非MAP_FAILED:表示共享内存地址;
mmapcreate.c

#include <stdio.h>
#include <unistd.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <string.h>

int main(int argc,char* argv[]){
    int fd = open("./mmap.txt",O_CREAT|O_RDWR,0644);
    char str[] = "hello mmap\n";    
    lseek(fd,sizeof(str),SEEK_SET);
    char* end = "\0";
    write(fd,end,1);
    void* buf = NULL;
    if(( buf =  mmap(NULL,sizeof(str),PROT_WRITE,MAP_SHARED,fd,0)) == MAP_FAILED){
        perror("mmap error\n");
        return 1;
    }
    strcpy(buf,str);
    munmap(buf,sizeof(str));
    close(fd);
}

mmapcreate02.c

#include <stdio.h>
#include <unistd.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <string.h>

int main(int argc,char* argv[]){
    int fd = open("./mmap.txt",O_RDWR);
    void* buf = NULL;
    if(( buf =  mmap(NULL,BUFSIZ,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0)) == MAP_FAILED){
        perror("mmap error\n");
        return 1;
    }
    printf("%s\n",buf);
    strcpy(buf,"this sdfdsfdsfdsfdsfdsfdsfdsfdsfdsf\n");
    munmap(buf,BUFSIZ);
    close(fd);
}

mmapcreate03.c

#include <stdio.h>
#include <unistd.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <string.h>

int main(int argc,char* argv[]){
    int fd = open("./mmap.txt",O_CREAT|O_RDWR,0644);
    char str[] = "hello mmap\n";    
    lseek(fd,sizeof(str),SEEK_SET);
    char* end = "\0";
    write(fd,end,1);
    void* buf = NULL;
    if(( buf =  mmap(NULL,sizeof(str),PROT_WRITE,MAP_SHARED,fd,0)) == MAP_FAILED){
        perror("mmap error\n");
        return 1;
    }
    strcpy(buf,str);
    pause();
//  munmap(str,sizeof(str));
//  close(fd);
    //_exit(0);
}

mmapcreate04.c

#include <stdio.h>
#include <unistd.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <string.h>

int main(int argc,char* argv[]){
    int fd = open("./mmap.txt",O_CREAT|O_RDWR,0644);
    void* buf = NULL;
    if(( buf =  mmap(NULL,BUFSIZ,PROT_WRITE|PROT_READ,MAP_SHARED,fd,0)) == MAP_FAILED){
        perror("mmap error\n");
        return 1;
    }
    if(fork()){
        strcpy(buf,argv[1]);    
    }else{
        printf("%s\n",buf);
    }
    munmap(buf,BUFSIZ);
    close(fd);
}

删除内存映射int munmap(void *start,size_t length);start:映射内存起始地址;length:表示内存大小;返回值:0表示成功,-1表示失败;需要注意的是关闭mmap中的文件描述符不能删除内存映射;

#include <stdio.h>
#include <unistd.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <string.h>

int main(int argc,char* argv[]){
    int fd = open("./mmap.txt",O_CREAT|O_RDWR,0644);
    char str[] = "hello mmap\n";    
    lseek(fd,sizeof(str),SEEK_SET);
    char* end = "\0";
    write(fd,end,1);
    void* buf = NULL;
    if(( buf =  mmap(NULL,sizeof(str),PROT_WRITE,MAP_SHARED,fd,0)) == MAP_FAILED){
        perror("mmap error\n");
        return 1;
    }
    strcpy(buf,str);
    munmap(buf,sizeof(str));//表示的含义就是删除内存映射
    close(fd);
}

同步操作:int msync(void * addr,size_t len,int flags);addr:表示映射内存起始地址;len:表示内存大小;flags:表示同步参数,MX_ASYNC,调用会立即返回,不等到跟新的完成,MS_SYNC:表示同步,调用会等到更新完成之后返回;MS_INVALIDATE:通知使用该共享区域的进程,数据已经改变;在共享内容更改之后,是的文件的其他映射失效;返回值:0:表示成功;-1表示失败;和文件的fflush相似;
 查看:man shm_overview;ls /dev/shm;注意的是msg_open默认生成共享内存的大小是0,需要ftuncate设置大小;

POSIX信号量

资料可以查看unv22e,头文件是semaphore.h需要链接的库文件是pthread;信号量类似于停车场的电子牌,信号量可以分为二值信号量,0和1;计数信号量0和n;信号量适用于控制多进程访问共享资源的;
 信号:P信号:0表示挂起进程;>0时,减1;V信号:0表示恢复进程;>0:表示加1;信号的本质是在任一时刻只能有一个进程访问临界区;

命名信号量/基于文件

创建:sem_t *sem_open(const char *name, int oflag,mode_t mode, unsigned int value);name:信号量IPC 名字;oflag:O_CREAT:如果没有这些对象就创建这个对象;O_EXCL:如果O_CREAT指定,但是name不存在,就返回错误;mode:表示权限位;value:信号量初始值;返回值:非SEM_FAILED:信号量的指针;SEM_FAILED:出错;
semopen.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <semaphore.h>

int main(int argc,char * argv[]){

    sem_t* sem = sem_open(argv[1],O_CREAT|O_RDWR,0644,0);
    fork();
    int i=0;
    for(;i<5;i++){
        sleep(1);
        sem_wait(sem);
        printf("PID:%d,enter\n",getpid());
        printf("PID:%d,do something\n",getpid());
        printf("PID:%d,leave\n",getpid());
        sem_post(sem);
    }
}

打开:sem_t *sem_open(const char *name,int oflag);sem_t *sem_open(const char *name,int oflag):name:信号量IPC名字;oflag:0;返回值:非SEM_FAILED:信号量的指针;SEM_FAILED:出错;

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <semaphore.h>

int main(int argc,char * argv[]){

    sem_t* sem = sem_open(argv[1],O_CREAT|O_RDWR,0644,0);
    fork();
    int i=0;
    for(;i<5;i++){
        sleep(1);
        sem_wait(sem);
        printf("PID:%d,enter\n",getpid());
        printf("PID:%d,do something\n",getpid());
        printf("PID:%d,leave\n",getpid());
        sem_post(sem);
    }
}

关闭:int sem_close(sem_t *sem);sem:信号量的指针;返回值:-1表示出错,0表示成功;


删除int sem_unlink(const char *name);name:表示信号量IPC的名字;返回值-1出错,0表示成功;

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
无名信号量/基于内存

初始化:int sem_init(sem_t *sem,int pshared,unsigned int value);sem:表示信号量的指针;pshared:表示共享方式,0表示线程间共享,1表示进程间共享,但是需要共享内存;value:信号量初始值;返回值:-1,出错,-表示成功;

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

销毁:int sem_destory(sem_t *sem),sem:表示信号量的指针;返回值位1表示出错,0表示成功;

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

挂出:int sem_post(sem_t *sem);sem:信号量的指针;返回值:-1表示出错,0表示成功;

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

等待:int sem_wait(sem_t *sem);sem:表示信号量的指针;返回值:-1表示出错,0表示成功;

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

尝试等待:int sem_trywait(sem_t *sem);sem:信号量的指针;返回值:-1表示出错,0表示成功;

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

获取信号量的值:int sem_getvalue(sem_t *sem,int *sval);sem:信号量的指针;sval:信号量的值;返回值:0表示成功,-1表示出错;

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

查看man sem_overview
 标识/名字,路径名 /tmp.123
几种方式的对比:
创建:文件:mqd_t mq_open(const char *name,int oflag,mode_t mode);消息队列:mqd_t mqopen(const char *name,int oflag,mode_t mode,struct mq_arrt, *attr)共享内存:int shm_open(const char *name,int oflag,mode_t);信号量:sem_t *sem_open(const char *name,int oflag,mode_t mode,unsigned int value);
删除:文件:int unlink(const char *name);消息队列:mqd_t mq_unlink(const char *name);共享内存:int shm_unlink(const char *name) 信号量:sem_t *sem_unlink(const char *name);

System V PIC

system V消息队列

需要包含的头文件是sys/msg.h,需要自定义的结构体msgbuf,其中包含两个选项:消息类型:必须是long,必须是结构体的第一个变量,mtext:标识消息数据,可以随意定义;

相关函数

消息获取:int msgget(key_t key, int msgflg);key:IPC键,key_t ftok(char *path,int id),path,id;IPC_PRIVATE:通常用于亲缘进程;msgflg:IPC_CREAT:标识创建;IPC_CREAT | IPC_EXCL;权限:建议使用八进制数字来进行描述;返回值非负整数标识消息队列标识,-1:标识失败;
msghget.c:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/msg.h>
struct msgbuf {
       long mtype;       /* message type, must be > 0 */
       char mtext[1];    /* message data */
   };

int main(){
    int id = msgget(IPC_PRIVATE,O_CREAT|O_RDWR|0644);
    if(-1 == id){
        perror("msgget error");
        return 1;
    }
    char str[] = "this is msg";
    struct msgbuf* buf = malloc(sizeof(str)+sizeof(long));
    buf->mtype= 10;
    strcpy(buf->mtext,str);
    if(fork()){
        msgsnd(id,buf,sizeof(str),0);
    }else{
        sleep(1);
        bzero(buf,sizeof(str)+sizeof(long));
        msgrcv(id,buf,sizeof(str),-11,0);
        printf("recv :%s",buf->mtext);
    }
    //msgctl(id,IPC_RMID,NULL);
}

消息发送:int msgsnd(int msgid,const void *msgptr,size_t msgsz,int msgflg);msgid:表示消息队列标识;msgptr:表示消息结构体,是以一个长整型成员变量开始的结构体,

struct test_messsge{
          long mtype; //must > 0
          char mtext;// data;
};

msgsz:表示消息队列长度,但是不包括长整型变量;msgflg:用于控制函数的行为,0表示忽略;IPC_NOWAIT:表示如果消息队列为空,就返回一个ENOMSG,并将控制权教会个调用函数的进程。MSG_NOERROR:如果函数取得的消息长度大于msgsz,将只返回msgsz的长度,剩下的部分就被丢弃了。MSG_EXCEPT:当msgtyp > 0时,接受的类型不扥估msgtype的第一条消息;返回值:表示成功,-1表示失败;
msgsnd.c:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/msg.h>
#include <fcntl.h>
struct msgbuf{
    long mtype;
    char mtext[1];
};
int main(int argc,char* argv[]){
    int c,flag = 0;
    while((c = getopt(argc,argv,"n")) !=-1){
        switch(c){
        case 'n':
            flag=IPC_NOWAIT;
            break;
        }
    }
    if(optind != argc - 3){
        printf("usage:%s [-n] <pathname> <message> <type>\n",argv[0]);
        return 1;
    }

    int id = msgget(ftok(argv[optind],1),O_WRONLY);
    if(-1 == id){
        perror("msgget error");
        return 1;
    }
    size_t msglen = strlen(argv[optind+1])+1;
    struct msgbuf* buf = malloc(sizeof(long)+msglen);
    buf->mtype = atoi(argv[optind+2]);
    strcpy(buf->mtext,argv[optind+1]);
    
    if(-1 == msgsnd(id,buf,msglen,flag)){
        perror("fcntl error");
        return 1;
    }
}

接收消息:int msgrcv(int msgid,void *msgptr,size_t msgsz,long msgtype,int msgflg);msid:表示消息队列标识;msgptr:消息结构体,以一个长整型成员变量开始的结构体;

struct   test_message{
            long   int   message_type;  // the data you which to transfer;
};

msgtype:标识消息的接收类型,0标识消息队列中的第一个消息,大于0标识获取具有相同消息类型的第一个消息;小于0标识获取类型等于或者小于msgtype的绝对值的最小一个消息;msgflg:用于控制函数的行为,0标识忽略,IPC_NOWAIT:如果消息队列为空则返回一个ENOMSG,并将控制权返回给调用函数的进程;MSG_NOERROR,如果函数取得的消息队列长度大于msgsz,将只返回msgsz长度的信息,剩下的部分机会被丢弃;MSG_EXCEPT:当msgtype > 0 时,接受的类型不等于msgtype的第一个条消息;返回值:0标识接收到的消息长度,-1失败;
msgrecv.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/msg.h>
#include <fcntl.h>
struct msgbuf{
    long mtype;
    char mtext[1];
};
int main(int argc,char* argv[]){
    int c,flag = 0;
    while((c = getopt(argc,argv,"n")) !=-1){
        switch(c){
        case 'n':
            flag=IPC_NOWAIT;
            break;
        }
    }
    if(optind != argc - 2){
        printf("usage:%s [-n] <pathname> <type>\n",argv[0]);
        return 1;
    }

    int id = msgget(ftok(argv[optind],1),O_RDONLY);

    if(-1 == id){
        perror("msgget error");
        return 1;
    }
    size_t msglen = BUFSIZ;
    struct msgbuf* buf = malloc(sizeof(long)+msglen);
    
    if(-1 == msgrcv(id,buf,msglen,atoi(argv[optind+1]),flag)){
        perror("fcntl error");
        return 1;
    }
    printf("read:%s",buf->mtext);
}

消息控制:int msgctl(int msgqid,int cmd,struct msgqid_ds *buf);msgqid:消息队列标识符;IPC_STAT:获取当前消息队列控制信息;IPC_SET:设置当前消息队列控制信息;IPC_RMID:删除消息队列;buf:消息队列的模式结构,msg_perm.mode:表示消息队列读写模式;msg_qbytes:队列最大大小;msg_cbytes:当前队列的大小;msg_qnum:表示当前队列的消息数;返回值:0表示成功,-1表示失败;
msgctl.c:标识用于删除消息

#include <stdio.h>
#include <sys/msg.h>

int main(int argc,char* argv[]){
    int i;
    for(i=1;i<argc;i++){
        msgctl(atoi(argv[i]),IPC_RMID,NULL);
    }
}

对于消息队列的查看可以使用ipcs来进行查看;

System V 共享内存

资料查看unpv22e,ch14,头文件sys/shm.h

相关的函数

共享内存的获取:int shmget(key_t key, size_t size, int shmflg);key:命名共享内存IPC_PRIVATE,key_t ftok(const char *fname,int fd);fname:标识已经存在的文件路径,id:表示子序号1--255;size:表示内存容量,非0表示新建的共享内存大小,0表示获取共享内存指定为0;shmflg:IPC_CREAT:用于创建共享内存;IPC_CREAT | IPC_EXCL;权限:建议使用八进制数字来表示;返回值:非负整数,表示共享内存表示符,-1表示失败;
shmget.c:

#include <stdio.h>
#include <stdlib.h>
#include <sys/shm.h>

int main(int argc,char* argv[]){
    key_t key = ftok(argv[1],1);
    if(-1 == key){
        perror("ftok err");
        return 1;
    }
    int shmid = shmget(key,atoi(argv[2]),IPC_CREAT|0644);
    if(-1 == shmid){
        perror("shmget err");
        return 1;
    }
    printf("shmid:%d\n",shmid);
    return 0;
}

共享内存链接:void *shmat(int shmid,const void *shmaddr,int shmflg);shmid:共享内存标识符;shmaddr:指定共享内存连接到当前及才能拿中的地址位置;通常设置为NULL,表示让系统来选择共享内存的地址,这点是出于兼容性考虑;shmflg:表示标志位,SHM_RDONLY,只读,0表示读写;返回值:非负整数表示共享内存指针,-1表示失败;
shmread.c

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/shm.h>

int main(int argc,char* argv[]){
    key_t key = ftok(argv[1],1);
    if(-1 == key){
        perror("ftok err");
        return 1;
    }
    int shmid = shmget(key,0,O_RDONLY);
    if(-1 == shmid){
        perror("shmget err");
        return 1;
    }
    void* buf = shmat(shmid,NULL,SHM_RDONLY);
    printf("%s\n",buf);
    shmdt(NULL);
    printf("shmid:%d\n",shmid);
    return 0;
}

共享内存分离:int shmdt(const void *shmaddr);shmaddr:表示共享内存指针;返回值0表示成功,-1表示失败;

:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

共享内存控制:int shmctl(int shmid,int cmd,struct shmid_ds *buf);shmid:共享内存标识符;cmd:IPC_STAT,获取当前共享内存控制信息;IPC_SET:设置当前共享内存控制信息;IPC_RMID:删除共享内存;buf:共享内存信息结构:int shm_segsz,表示共享内存大小,shm_perm.mode:表示读写权限;返回值:0表示成功,-1表示失败;获取共享内存大小,shmctl(shmid,IPC_STAT,&buf);buf.shm_segsz;删除共享内存:shmctl(shmid,IPC_RMID,NULL);
shmctl.c

#include <stdio.h>
#include <stdlib.h>
#include <sys/shm.h>

int main(int argc,char* argv[]){
    
    if(-1 == shmctl(atoi(argv[1]),IPC_RMID,NULL)){
        perror("shmctl err");
        return 1;
    }
}

共享内存也可以使用ipcs进行查看;

System V信号量

&38195;unpv22e,ch11;头文件是:、sys/sem.h

相关函数

信号量的获取:int semget(key,nsems,semflg);key:表示命名信号量IPC_PRIVATE,key_t ftok(const char *fname,int fd);fname:已存在的文件路径;id:表示子序号,1--255;nsems:信号量数目,0表示获取信号量指定为0,非0新建的信号量数量;semflag:IPC_CREAT,信号量已经存在不出错,IPC_CREAT | IPC_EXCL,信号量已存在出错,权限建议使用八进制数字;返回值:非负整数,信号量标识,-1表示失败;
semcreate.c

#include <stdio.h>
#include <unistd.h>
#include <sys/sem.h>

int main(int argc,char* argv[]){
    key_t key = ftok(argv[1],1);
    if(-1 == semget(key,1,IPC_CREAT|0644)){
        perror("semget err");
        return 1;
    }
}

信号量操作:int semop(int semid,struct sembuf *sops,size_t nsops);semid:表示信号量标识;sops:信号量结构体,sem_num:信号量下标,从0开始,sem_flg:SEM_UNDO:表示在进程结束时,相应的操作将会被取消,进程没有释放该信号量而终止时操作系统释放信号量;0:表示的是默认的选项;IPC_NOWAIT:表示非阻塞状态;sem_op:-1表示P操作,1:表示V发送操作;nsops:semops大小;
race.c

#include <stdio.h>
#include <stdlib.h>
#include <sys/sem.h>
#include <unistd.h>

void sem_p(int semid){
    struct sembuf buf;
    buf.sem_num = 0;
    buf.sem_op = -1;
    buf.sem_flg = 0;
    semop(semid,&buf,1);
}
void sem_v(int semid){
    struct sembuf buf;
    buf.sem_num = 0;
    buf.sem_op = 1;
    buf.sem_flg = 0;
    semop(semid,&buf,1);
}

union semun{
    int val;
};

int main(int argc,char * argv[]){
    int semid  = semget(IPC_PRIVATE,1,IPC_CREAT|0644);
    union semun un = {1};
    if(-1 == semctl(semid,0,SETVAL,un)){
        perror("semctl err");
        return 1;
    }
    if(-1 == semid){
        perror("semget err");
        return 1;
    }

    fork();
    int i=0;
    for(;i<5;i++){
        sem_p(semid);
        printf("PID:%d,enter\n",getpid());
        sleep(1);
        printf("PID:%d,do something\n",getpid());
        printf("PID:%d,leave\n",getpid());
        sem_v(semid);
    }
}

信号量控制:int semctl(semid, nsem,cmd,...);semid:信号量标识;nsem:信号量下标;cmd:SETVAL,用于设置信号量的值;GETVAL,获取信号量的值;IPC_RMID,表示删除信号量的值;...:union semnum;
设置信号量的初始值semctl(smeid,信号量下标,SETVAL,semnum);

union semun {
          int val;
          struct semid_ds *buf;
          unsigned   short  *arry;
};

删除信号量:semctl(semid,信号量下标,IPC_RMID);
semrmid.c

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/sem.h>

int main(int argc,char* argv[]){
    key_t key = ftok(argv[1],1);
    int semid;
    if((semid = semget(key,0,O_RDWR))==-1){
        perror("semget err");
        return 1;
    }
    if(-1 == semctl(semid,0,IPC_RMID)){
        perror("semctl err");
        return 1;
    }
}

查看使用ipcs命令;
需要注意的几点是:
1、ftok()首个参数必须是已存在的路径名;
2、使用XXget()创建时,必须是使用IPC_CREAT,而不是O_CREAT
3、读写非阻塞的操作必须是设置在open打开的操作中;
Posix:优先级高的先出;
System V:当mtype小于0时,小于mtype绝对值的消息中,最小的先出;0最先进入的先出;>0

相关文章

网友评论

    本文标题:Linux-C-day-4-进程间通信-消息队列||信号量 ||

    本文链接:https://www.haomeiwen.com/subject/yxoifxtx.html