进程间通讯_IPC

IPC进程间通信_system V

消息队列

共享内存

信号灯

查看相关的命令

ipcs 查看所有的信息(消息队列,共享内存,信号量)

ipcs -q 消息队列(queue)

ipcs -m 共享内存(memory)

ipcs -s 信号量(信号灯)(semphore)

删除相关的命令

ipcrm -q/-m/-s ID 删除

注:这里的ID并不是key

1消息队列

1.1消息队列的原理图

在这里插入图片描述

1.2消息队列API(ftok|msgget|msgsnd|msgrcv|msgctl)

1.获得key值 
第一种:
key_t ftok(const char *pathname, int proj_id);
参数:
	@pathname:已经存在的文件路径
	@proj_id :获取这个整数的低8bit
返回值:成功返回 key值,失败返回-1 
 注:(key = proj_id(8bit),st_dev(8bit),inode(16bit))
    
第二种:
将key值指定为IPC_PRIVATE ,当IPC对象在亲缘关系进程通信的时候

2.创建IPC对象 
int msgget(key_t key, int msgflg);
功能:创建消息队列
参数:
	@key    :IPC_PRIVATE 或 ftok
	@msgflg :创建共享内存的标志位
		IPC_CREAT | 0666      //如果消息队列不存在就创建,如果存在就返回消息队列的ID
        或 
        IPC_CREAT | IPC_EXCL | 0666 //如果消息队列不存在就创建,如果存在就报已存在错误
返回值:成功返回ID,失败返回-1 

注意:
如果对应key值的IPC对象不存在,则创建,如果存在,直接返回IPC对象的ID 

3.发送消息
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
功能:发送消息
参数:
	@msqid  :消息队列ID 
	@msgp   :需要发送的消息存放的地址
	@msgsz  :消息正文的大小
	@msgflg :发消息的标志位
			0:阻塞的方式发送  
            IPC_NOWAIT:非阻塞方式调用
返回值:成功返回0,失败返回-1  (队列的大小限制MSGMNB 16384)

消息结构体定义:
typedef struct{
 	long  msg_type;   //消息类型必须在第一个位置,
 	char  mtxt[1024];
	...
}msg_t;

正文大小:sizeof(msg_t) - sizeof(long)

4.接收消息 
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);
功能:接收消息
参数:
	@msqid:消息队列ID 
	@msgp :存放接收到的消息 
	@msgsz:正文大小 
	@msgtyp:消息类型  
			0: 总是从消息队列中提取第一个消息
            >0:指向消息的类型接收消息,如果类型不匹配,详细依然在消息队列中存放    
            <0:对消息的类型取绝对值
             -5--->|-5|-->5-->接收消息类型小于5的消息(1-2-3-4-1-3)   
          队列  -1-2-3-4-1-3-100-200--     
	@msgflg:
			0:阻塞的方式接收   
            IPC_NOWAIT:非阻塞方式调用
返回值:成功返回 接收消息正文大小,失败返回-1
    
5.消息队列的控制
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
功能:消息队列的控制
参数:
	@msgqid:消息队列ID 
	@cmd   : 控制方式
			IPC_RMID(删除消息队列)  
            IPC_SET(设置消息队列的属性信息) 
            IPC_STAT(获取消息队列属性信息)
	@buf   :存放消息队列属性 (如果cmd填写为IPC_RMID这个参数缺省)
返回值:成功返回0,失败返回-1

1.3消息队列的实例

1.3.1相同的消息类型收发实例

01snd.c

#include <head.h>

typedef struct{
 long  msg_type;   //消息类型必须在第一个位置,
 char  mtxt[1024]; //消息的正文
}msg_t;

#define MSGSIZE (sizeof(msg_t)-sizeof(long))

int main(int argc,const char * argv[])
{
    key_t key;
    int msgid; //消息队列的id;
    msg_t msg = {
        .msg_type = 100,
    };
    //1.获取一个key值
    if((key = ftok("/home/linux",'t'))==-1)
        PRINT_ERR("ftok get key error");

    //2.创建消息队列
    if((msgid = msgget(key, IPC_CREAT|0664))==-1)
        PRINT_ERR("msgget error");

    //3.向消息队列中发消息    
    while(1){
        //从终端向消息结构体的正文中输入信息
        printf("input > ");
        fgets(msg.mtxt,sizeof(msg.mtxt),stdin);
        msg.mtxt[strlen(msg.mtxt)-1]='\0';
        //将消息发到消息队列中
        //msgid:消息队列号
        //msg:消息首地址
        //MSGSIZE:消息正文的大小
        //0:阻塞方式发送,如果消息队列满了,就阻塞等待
        if(msgsnd(msgid, &msg,MSGSIZE, 0)==-1)
            PRINT_ERR("msgsnd error");
    }

    return 0;
}

02rcv.c

#include <head.h>

typedef struct{
 long  msg_type;   //消息类型必须在第一个位置,
 char  mtxt[1024]; //消息的正文
}msg_t;

#define MSGSIZE (sizeof(msg_t)-sizeof(long))

int main(int argc,const char * argv[])
{
    key_t key;
    int msgid; //消息队列的id;
    msg_t msg;
    //1.获取一个key值
    if((key = ftok("/home/linux",'t'))==-1)
        PRINT_ERR("ftok get key error");

    //2.创建消息队列(不会再次创建)
    if((msgid = msgget(key, IPC_CREAT|0664))==-1)
        PRINT_ERR("msgget error");

    //3.向消息队列中取消息 
    while(1){
        //0:每次从消息队列中取第一个消息
        //0:阻塞方式接收消息
        if(msgrcv(msgid,&msg,MSGSIZE,0,0)==-1)
            PRINT_ERR("msgsnd error");
        printf("消息类型=%ld,消息正文=%s\n",msg.msg_type,msg.mtxt);
    }

    return 0;
}

1.3.2不同的消息类型收发实例

01snd.c

#include <head.h>

typedef struct
{
    long id;       //类型,学号
    char name[20]; //学生名字
    int score;     //学生的成绩
} msg_t;

#define MSGSIZE (sizeof(msg_t) - sizeof(long))

int main(int argc, const char *argv[])
{
    key_t key;
    int msgid; //消息队列的id;
    struct msqid_ds buf;

    // 1.获取一个key值
    if ((key = ftok("/home/linux", 't')) == -1)
        PRINT_ERR("ftok get key error");

    // 2.创建消息队列
    if ((msgid = msgget(key, IPC_CREAT | 0664)) == -1)
        PRINT_ERR("msgget error");

    msg_t msg = {100,"zhangsan",88};
    if (msgsnd(msgid, &msg, MSGSIZE, 0) == -1)
        PRINT_ERR("msgsnd error");
    msg_t msg1 = {101,"lisi",20};
    if (msgsnd(msgid, &msg1, MSGSIZE, 0) == -1)
        PRINT_ERR("msgsnd error");
    msg_t msg2 = {102,"wangwu",100};
    if (msgsnd(msgid, &msg2, MSGSIZE, 0) == -1)
        PRINT_ERR("msgsnd error");
    return 0;
}

02rcv.c

#include <head.h>

typedef struct
{
    long id;       //类型,学号
    char name[20]; //学生名字
    int score;     //学生的成绩
} msg_t;

#define MSGSIZE (sizeof(msg_t)-sizeof(long))

int main(int argc,const char * argv[])
{
    key_t key;
    int msgid; //消息队列的id;
    msg_t msg;
    //1.获取一个key值
    if((key = ftok("/home/linux",'t'))==-1)
        PRINT_ERR("ftok get key error");

    //2.创建消息队列(不会再次创建)
    if((msgid = msgget(key, IPC_CREAT|0664))==-1)
        PRINT_ERR("msgget error");

    int id;
    //3.向消息队列中取消息 
    while(1){
        printf("请输入学号> ");
        scanf("%d",&id);
        if(msgrcv(msgid,&msg,MSGSIZE,id,0)==-1)
            PRINT_ERR("msgsnd error");
        printf("id=%ld,name=%s,score=%d\n",
            msg.id,msg.name,msg.score);
    }

    return 0;
}

1.3.3使用IPC_PRIVATE实现亲缘进程消息队列通信

#include <head.h>

typedef struct
{
    long id;       //类型,学号
    char name[20]; //学生名字
    int score;     //学生的成绩
} msg_t;

#define MSGSIZE (sizeof(msg_t) - sizeof(long))

int main(int argc, const char *argv[])
{
    int msgid; //消息队列的id;
    pid_t pid;
    // 1.创建消息队列
    if ((msgid = msgget(IPC_PRIVATE, IPC_CREAT | 0664)) == -1)
        PRINT_ERR("msgget error");

    pid = fork();
    if (pid < 0)
    {
        PRINT_ERR("fork error");
    }
    else if (pid == 0)
    {
        msg_t msg = {100, "zhangsan", 88};
        if (msgsnd(msgid, &msg, MSGSIZE, 0) == -1)
            PRINT_ERR("msgsnd error");
        exit(EXIT_SUCCESS);
    }
    else
    {
        msg_t msg;
        memset(&msg, 0, sizeof(msg));
        if (msgrcv(msgid, &msg, MSGSIZE, 100, 0) == -1)
            PRINT_ERR("msgsnd error");
        printf("id=%ld,name=%s,score=%d\n",
               msg.id, msg.name, msg.score);
        wait(NULL);
    }

    return 0;
}

1.4msgctl函数的使用

1.4.1消息队列控制函数的使用(IPC_STAT)

struct msqid_ds
{
  struct ipc_perm msg_perm;	//消息队列权限的结构体
  __syscall_ulong_t __msg_cbytes; //当前消息队列中字节个数
  msgqnum_t msg_qnum;		//当前消息队列中消息的个数
  msglen_t msg_qbytes;		//消息队列能容纳的消息的个数(16384)
  __pid_t msg_lspid;		//最后一次发消息进程号
  __pid_t msg_lrpid;		//最后一次收消息进程号
};
struct ipc_perm
  {
    __key_t __key;			//ftok获取的key
    __uid_t uid;			//所属用户的ID
    __gid_t gid;			//所属组的ID
    __uid_t cuid;			//创建消息队列用户的ID
    __gid_t cgid;			//创建消息队列的组的ID
    unsigned short int mode;	//消息队列的权限
    unsigned short int __seq;	//消息队列号msgid

  };
#include <head.h>

typedef struct{
 long  msg_type;   //消息类型必须在第一个位置,
 char  mtxt[1024]; //消息的正文
}msg_t;

#define MSGSIZE (sizeof(msg_t)-sizeof(long))

int main(int argc,const char * argv[])
{
    key_t key;
    int msgid; //消息队列的id;
    struct msqid_ds buf;
    msg_t msg = {
        .msg_type = 100,
    };
    //1.获取一个key值
    if((key = ftok("/home/linux",'t'))==-1)
        PRINT_ERR("ftok get key error");

    //2.创建消息队列
    if((msgid = msgget(key, IPC_CREAT|0664))==-1)
        PRINT_ERR("msgget error");

    //3.向消息队列中发消息    
    while(1){
        //从终端向消息结构体的正文中输入信息
        printf("input > ");
        fgets(msg.mtxt,sizeof(msg.mtxt),stdin);
        msg.mtxt[strlen(msg.mtxt)-1]='\0';
        //将消息发到消息队列中
        //msgid:消息队列号
        //msg:消息首地址
        //MSGSIZE:消息正文的大小
        //0:阻塞方式发送,如果消息队列满了,就阻塞等待
        if(msgsnd(msgid, &msg,MSGSIZE, 0)==-1)
            PRINT_ERR("msgsnd error");

        msgctl(msgid,IPC_STAT,&buf); 
        printf("cbytes=%ld\n",buf.__msg_cbytes);//当前消息队列中字节个数
        printf("qnum  =%ld\n",buf.msg_qnum);//消息的个数
        printf("qbytes=%ld\n",buf.msg_qbytes); //消息队列能容纳的消息的个数(16384)
        printf("pid = %d\n",buf.msg_lspid); //最后一次发消息的进程号
        printf("mode = %#o\n",buf.msg_perm.mode);//消息队列的权限
        printf("key = %#x\n",buf.msg_perm.__key);//ftok函数得到的key
        printf("msgid = %u\n",buf.msg_perm.__seq);//暂未使用
        printf("msgid = %u\n",msgid);//消息队列序号
    }

    return 0;
}

1.4.2消息队列控制函数的使用(IPC_RMID)

msgctl(msgid,IPC_RMID,NULL); //删除消息队列 
或者
ipcrm -q msgid    //删除消息队列 
#include <head.h>

typedef struct{
 long  msg_type;   //消息类型必须在第一个位置,
 char  mtxt[1024]; //消息的正文
}msg_t;

#define MSGSIZE (sizeof(msg_t)-sizeof(long))

int main(int argc,const char * argv[])
{
    key_t key;
    int msgid; //消息队列的id;
    struct msqid_ds buf;
    msg_t msg = {
        .msg_type = 100,
    };
    //1.获取一个key值
    if((key = ftok("/home/linux",'t'))==-1)
        PRINT_ERR("ftok get key error");

    //2.创建消息队列
    if((msgid = msgget(key, IPC_CREAT|0664))==-1)
        PRINT_ERR("msgget error");

    //3.向消息队列中发消息    
    while(1){
        //从终端向消息结构体的正文中输入信息
        printf("input > ");
        fgets(msg.mtxt,sizeof(msg.mtxt),stdin);
        msg.mtxt[strlen(msg.mtxt)-1]='\0';
        //将消息发到消息队列中
        //msgid:消息队列号
        //msg:消息首地址
        //MSGSIZE:消息正文的大小
        //0:阻塞方式发送,如果消息队列满了,就阻塞等待
        if(msgsnd(msgid, &msg,MSGSIZE, 0)==-1)
            PRINT_ERR("msgsnd error");

        msgctl(msgid,IPC_STAT,&buf); 
        printf("cbytes=%ld\n",buf.__msg_cbytes);
        printf("qnum  =%ld\n",buf.msg_qnum);
        printf("qbytes=%ld\n",buf.msg_qbytes);
        printf("pid = %d\n",buf.msg_lspid);
        printf("mode = %#o\n",buf.msg_perm.mode);
        printf("key = %#x\n",buf.msg_perm.__key);
        printf("msgid = %u\n",buf.msg_perm.__seq);
        printf("msgid = %u\n",msgid);

        if(strcmp(msg.mtxt,"quit")==0)break;
    }

    msgctl(msgid,IPC_RMID,NULL); //删除消息队列 
    return 0;
}

2共享内存

2.1共享内存的原理

在这里插入图片描述

2.2共享内存的API(ftok|shmget|shmat|shmdt|shmctl)

(1)创建共享内存
int shmget(key_t key, size_t size, int shmflg);
功能:获取共享内存段的ID 
参数:
	@key :IPC_PRIVATE  或 ftok() 
	@size:申请的共享内存段大小 [4k的倍数]
	@shmflg:创建消息队列的标志位
		IPC_CREAT | 0666      //如果共享内存不存在就创建,如果存在就返回共享内存的ID
        或 
        IPC_CREAT | IPC_EXCL | 0666 //如果共享内存不存在就创建,如果存在就报已存在错误
返回值:成功返回ID,失败返回-1 

(2)映射内存
void *shmat(int shmid, const void *shmaddr, int shmflg);
功能:映射共享内存到用户空间 
参数:
	@shmid  :共享内存段ID 
	@shmaddr:映射到的地址
        	NULL:系统自动完成映射 
	@shmflg :共享内存操作的标志位
			SHM_RDONLY:只读   
            0:读写
返回值:成功返回映射后的地址,失败返回(void *)-1 
  
(3)撤销映射
int shmdt(const void *shmaddr);
功能:撤销映射
参数:
	@shmaddr  共享内存映射的地址 
返回值:成功返回0,失败返回-1置位错误码
注意:当一个进程结束的时候,它映射共享内存,会自动撤销映射
  
(4)共享内存的控制函数
 int shmctl(int shmid, int cmd, struct shmid_ds *buf);
//shmctl(shmid,IPC_RMID,NULL);
功能:根据命令控制共享内存 
参数:
	@shmid :共享内存的ID 
	@cmd   :控制方式
			IPC_STAT[获取属性],
			IPC_SET[设置属性],
			IPC_RMID[删除IPC对象]
	@buf   :存放消息队列属性 (如果cmd填写为IPC_RMID这个参数缺省)
返回值:成功返回0,失败返回 -1 

注意:当我们调用shmctl删除共享内存的时候,并不会立即删除。
    只有当共享内存映射次数为0,才会删除共享内存对象

2.3共享内存使用实例

01write.c

#include <head.h>
#define SHMSIZE 4096
int main(int argc,const char * argv[])
{
    key_t key;
    int shmid;
    char *addr=NULL;
    //1.获取key值
    if((key=ftok("/home/linux",'a'))==-1)
        PRINT_ERR("ftok error");

    //2.创建共享内存
    if((shmid=shmget(key,SHMSIZE,IPC_CREAT|0664))==-1)
        PRINT_ERR("create share memory error");

    //3.将共享内存映射到这个进程空间
    addr=shmat(shmid,NULL,0);
    if(addr == (void *)(-1))
        PRINT_ERR("shmat error");

    printf("shmat addr = %p\n",addr);

    //4.向共享内存中写数据
    printf("shm input > ");
    fgets(addr,SHMSIZE,stdin);
    addr[strlen(addr)-1]='\0';

    //敲回车结束程序
    getchar();

    //删除共享内存
    shmctl(shmid,IPC_RMID,NULL);

    return 0;
}

02read.c

#include <head.h>
#define SHMSIZE 4096
int main(int argc,const char * argv[])
{
    key_t key;
    int shmid;
    char *addr=NULL;
    //1.获取key值
    if((key=ftok("/home/linux",'a'))==-1)
        PRINT_ERR("ftok error");

    //2.创建共享内存
    if((shmid=shmget(key,SHMSIZE,IPC_CREAT|0664))==-1)
        PRINT_ERR("create share memory error");

    //3.将共享内存映射到这个进程空间
    addr=shmat(shmid,NULL,0);
    if(addr == (void *)(-1))
        PRINT_ERR("shmat error");

    printf("shmat addr = %p\n",addr);

    //4.从共享内存中读取数据
    printf("read shm data=%s\n",addr);

    //敲回车结束程序
    getchar();

    //删除共享内存
    shmctl(shmid,IPC_RMID,NULL);

    return 0;
}

问:上述的程序能够保证读写的顺序?如果没有执行写,先向执行读的现象?

答:上述的共享内存在没有执行写的时候,执行读是不会阻塞的,所以如果向

看到现象就必须先执行写,在执行读,上述的代码遵从生产者消费者模型,必

须同步后才能正确看到效果。使用IPC通信中的信号灯机制实现同步。

问:共享内存的数据读取后还存在吗?

答:存在,直到下一次写覆盖掉,或者删除共享内存这些数据才消失

2.4shmctl函数的使用

2.4.1共享内存控制函数的使用(IPC_STAT)

struct shmid_ds
{
    struct ipc_perm shm_perm;	//共享内存权限
    size_t shm_segsz;			//共享内存大小,单位是字节
    __time_t shm_atime;			//最后一次,shmat的时间
    __pid_t shm_cpid;			//创建者的pid
    __pid_t shm_lpid;			//最后一个操作共享内存的pid
    shmatt_t shm_nattch;		//共享内存的链接数
};

struct ipc_perm
  {
    __key_t __key;			//ftok获取的key
    __uid_t uid;			//所属用户的ID
    __gid_t gid;			//所属组的ID
    __uid_t cuid;			//创建消息队列用户的ID
    __gid_t cgid;			//创建消息队列的组的ID
    unsigned short int mode;	//消息队列的权限
  };
#include <head.h>
#define SHMSIZE 4096
int main(int argc, const char *argv[])
{
    key_t key;
    int shmid;
    char *addr = NULL;
    // 1.获取key值
    if ((key = ftok("/home/linux", 'a')) == -1)
        PRINT_ERR("ftok error");

    // 2.创建共享内存
    if ((shmid = shmget(key, SHMSIZE, IPC_CREAT | 0664)) == -1)
        PRINT_ERR("create share memory error");

    // 3.将共享内存映射到这个进程空间
    addr = shmat(shmid, NULL, 0);
    if (addr == (void *)(-1))
        PRINT_ERR("shmat error");

    printf("shmat addr = %p\n", addr);

    // 4.向共享内存中写数据
    printf("shm input > ");
    fgets(addr, SHMSIZE, stdin);
    addr[strlen(addr) - 1] = '\0';
    
    struct shmid_ds buf;
    shmctl(shmid, IPC_STAT, &buf);
    printf("size = %ld\n", buf.shm_segsz);
    printf("nattch= %ld\n", buf.shm_nattch);
    printf("create pid = %d\n", buf.shm_cpid);
    printf("last op pid = %d\n", buf.shm_lpid);
    printf("mode = %#o\n", buf.shm_perm.mode);
    printf("key = %#x\n", buf.shm_perm.__key);
    printf("shmid = %u\n", shmid);
    //敲回车结束程序
    getchar();

    //删除共享内存
    shmctl(shmid, IPC_RMID, NULL);

    return 0;
}

2.4.2共享内存控制函数的使用(IPC_RMID)

shmctl(shmid,IPC_RMID,NULL); //删除共享内存
或者
ipcrm -m shmid    //删除共享内存
#include <head.h>
#define SHMSIZE 4096
int main(int argc, const char *argv[])
{
    key_t key;
    int shmid;
    char *addr = NULL;
    // 1.获取key值
    if ((key = ftok("/home/linux", 'a')) == -1)
        PRINT_ERR("ftok error");

    // 2.创建共享内存
    if ((shmid = shmget(key, SHMSIZE, IPC_CREAT | 0664)) == -1)
        PRINT_ERR("create share memory error");

    // 3.将共享内存映射到这个进程空间
    addr = shmat(shmid, NULL, 0);
    if (addr == (void *)(-1))
        PRINT_ERR("shmat error");

    printf("shmat addr = %p\n", addr);

    // 4.向共享内存中写数据
    printf("shm input > ");
    fgets(addr, SHMSIZE, stdin);
    addr[strlen(addr) - 1] = '\0';
    
    struct shmid_ds buf;
    shmctl(shmid, IPC_STAT, &buf);
    printf("size = %ld\n", buf.shm_segsz);
    printf("nattch= %ld\n", buf.shm_nattch);
    printf("create pid = %d\n", buf.shm_cpid);
    printf("last op pid = %d\n", buf.shm_lpid);
    printf("mode = %#o\n", buf.shm_perm.mode);
    printf("key = %#x\n", buf.shm_perm.__key);
    printf("shmid = %u\n", shmid);
    //敲回车结束程序
    getchar();

    //删除共享内存
    shmctl(shmid, IPC_RMID, NULL);

    return 0;
}

3.信号量

3.1信号量的概念

信号量:信号量是实现进程间同步的方式,这个信号量又叫信号灯集。在一个信号量中可以包含很多的信号灯,信号灯之间相互不干扰。
在这里插入图片描述

3.2信号量的API(ftok|semget|semop|semctl)

POSIX 线程中的同步用的是无名信号量 
进程间的同步使用的是IPC 对象[信号灯集]

信号灯集:信号灯集合,每一个信号灯都可以用来表示一类资源,其值表示资源的个数

(1)创建信号灯集
int semget(key_t key, int nsems, int semflg);
功能:创建信号灯集
参数:
	@key    IPC_PRIVATE , ftok() 
	@nsems  信号灯集中信号灯的个数 
	@semflg IPC_CREAT | 0666,IPC_CREAT | IPC_EXCL
返回值:成功返回ID,失败返回-1 

(2)初始化信号灯集中信号灯的值
int semctl(int semid, int semnum, int cmd, ...);
参数:
	@semid  :信号灯集的ID 
	@semnum :信号灯的编号[编号从0开始]
	@cmd    :SETVAL[设置信号灯的值]  ,GETVAL(获取信号灯的值),IPC_RMID[删除信号灯集]
	@...    :联合体 union semun sem;
         union semun {
               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) */
           };
返回值:成功返回0,失败返回-1 

(3)信号灯的操作(PV)
int semop(int semid, struct sembuf *sops, unsigned nsops);
功能:完成PV操作  
参数:
	@semid  :信号灯集的ID 
	@sops   :操作方式结构体首地址 
        struct sembuf{
           unsigned short sem_num;  //信号灯的编号
           short          sem_op;   //操作方式
            				-1  :P操作,申请资源
                             1  :V操作,释放资源   
           short          sem_flg;  //操作的标志位
            				IPC_NOWAIT:非阻塞方式
                            0          :阻塞方式操作
        };
	@nsops  :操作信号灯的个数 
返回值:成功返回0,失败返回-1

3.3将上述的API进程函数封装

sem.c

#include <head.h>
union semun
{
    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 init_semnum_value(int semid, int semnum, int value)
{
    union semun sem;
    sem.val = value;

    if (semctl(semid, semnum, SETVAL, sem) < 0)
        PRINT_ERR("semctl init value error");
    return 0;
}

int sem_init(void)
{
    key_t key;
    int semid;

    // 1.获取key值
    if ((key = ftok("/home/linux", 'w')) == -1)
        PRINT_ERR("ftok error");

    // 2.创建信号量
    //如果信号量不存在就创建信号量,创建号信号量后执行else语句对
    //信号灯的初值进行初始化
    //如果信号量已存在,IPC_EXCL这个就会让semget报EEXIST,如果
    //信号灯已存在,直接获取信号灯semid,否则打印错误。如果信号
    //量已存在就不再对信号量中的信号灯的初值做初始化了
    if ((semid = semget(key, 2, IPC_CREAT | IPC_EXCL | 0664)) == -1)
    {
        if(errno == EEXIST){
            semid = semget(key, 2, IPC_CREAT | 0664);
        }else{
            PRINT_ERR("create sem error");
        }
    }
    else
    {
        // 3.初始化信号量
        //初始化信号灯集中的两个信号灯的初值
        // 0号灯的初值为0
        // 1号灯的初值为1
        init_semnum_value(semid, 0, 0);
        init_semnum_value(semid, 1, 1);
    }

    return semid;
}

// P操作代表申请资源
int P(int semid, int semnum)
{
    struct sembuf buf;
    buf.sem_num = semnum;
    buf.sem_op = -1; //申请资源
    buf.sem_flg = 0; //阻塞方式申请

    if (semop(semid, &buf, 1) < 0)
        PRINT_ERR("P error");

    return 0; //成功返回0
}

// V操作代表释放资源
int V(int semid, int semnum)
{
    struct sembuf buf;
    buf.sem_num = semnum;
    buf.sem_op = 1;  //释放资源
    buf.sem_flg = 0; //阻塞方式申请

    if (semop(semid, &buf, 1) < 0)
        PRINT_ERR("V error");

    return 0; //成功返回0
}

int sem_del(int semid)
{
    // 1.删除信号灯集合
    if (semctl(semid, 0, IPC_RMID, NULL) == -1)
        PRINT_ERR("delete sem error");

    return 0;
}

sem.h

#ifndef __SEM_H__
#define __SEM_H__

int sem_init(void);
int P(int semid, int semnum);
int V(int semid, int semnum);
int sem_del(int semid);

#endif

3.4实例(使用信号灯集完成AB进程共享内存的同步过程)

01write.c

#include <head.h>
#include "sem.h"
#define SHMSIZE 4096
int main(int argc, const char *argv[])
{
    key_t key;
    int shmid, semid;
    char *addr = NULL;
    // 1.创建信号量
    if ((semid = sem_init()) == -1)
        PRINT_ERR("sem create and init error");

    // 1.获取key值
    if ((key = ftok("/home/linux", 'a')) == -1)
        PRINT_ERR("ftok error");

    // 2.创建共享内存
    if ((shmid = shmget(key, SHMSIZE, IPC_CREAT | 0664)) == -1)
        PRINT_ERR("create share memory error");

    // 3.将共享内存映射到这个进程空间
    addr = shmat(shmid, NULL, 0);
    if (addr == (void *)(-1))
        PRINT_ERR("shmat error");

    printf("shmat addr = %p\n", addr);

    // 4.向共享内存中写数据
    while (1)
    {
        P(semid,1);
        printf("shm input > ");
        fgets(addr, SHMSIZE, stdin);
        addr[strlen(addr) - 1] = '\0';
        V(semid,0);
        if(strcmp(addr,"quit")==0)break;
    }


    //删除共享内存
    shmctl(shmid, IPC_RMID, NULL);
    sem_del(semid);

    return 0;
}

02read.c

#include <head.h>
#include "sem.h"
#define SHMSIZE 4096
int main(int argc,const char * argv[])
{
    key_t key;
    int shmid, semid;
    char *addr = NULL;
    // 1.创建信号量
    if ((semid = sem_init()) == -1)
        PRINT_ERR("sem create and init error");

    //1.获取key值
    if((key=ftok("/home/linux",'a'))==-1)
        PRINT_ERR("ftok error");

    //2.创建共享内存
    if((shmid=shmget(key,SHMSIZE,IPC_CREAT|0664))==-1)
        PRINT_ERR("create share memory error");

    //3.将共享内存映射到这个进程空间
    addr=shmat(shmid,NULL,0);
    if(addr == (void *)(-1))
        PRINT_ERR("shmat error");

    printf("shmat addr = %p\n",addr);

    //4.从共享内存中读取数据
    while(1){
        P(semid,0);
        printf("read shm data=%s\n",addr);
        if(strcmp(addr,"quit")==0)break;
        V(semid,1);
    }
  
    return 0;
}

编译过程:

gcc 01write.c sem.c -o write

gcc 02read.c  sem.c -o read

执行过程

shmctl(shmid, IPC_RMID, NULL);
sem_del(semid);

return 0;

}


### 02read.c

```c
#include <head.h>
#include "sem.h"
#define SHMSIZE 4096
int main(int argc,const char * argv[])
{
    key_t key;
    int shmid, semid;
    char *addr = NULL;
    // 1.创建信号量
    if ((semid = sem_init()) == -1)
        PRINT_ERR("sem create and init error");

    //1.获取key值
    if((key=ftok("/home/linux",'a'))==-1)
        PRINT_ERR("ftok error");

    //2.创建共享内存
    if((shmid=shmget(key,SHMSIZE,IPC_CREAT|0664))==-1)
        PRINT_ERR("create share memory error");

    //3.将共享内存映射到这个进程空间
    addr=shmat(shmid,NULL,0);
    if(addr == (void *)(-1))
        PRINT_ERR("shmat error");

    printf("shmat addr = %p\n",addr);

    //4.从共享内存中读取数据
    while(1){
        P(semid,0);
        printf("read shm data=%s\n",addr);
        if(strcmp(addr,"quit")==0)break;
        V(semid,1);
    }
  
    return 0;
}

编译过程:

gcc 01write.c sem.c -o write

gcc 02read.c  sem.c -o read

执行过程
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值