System V IPC对象 共享内存、消息队列、信号灯集(6.7)

System V IPC对象 共享内存、消息队列、信号灯集


原理:
进程都是通过IPC对象唯一的名字,称为键key,找到IPC对象,但内核还是通过IPC对象的ID来找到它.
不同进程只要获得同一IPC对象的键key,就可以实现操作同一IPC对象,从而实际进程间通信


----------------------------------------------------------
key_t ftok(const char *pathname, int proj_id);
功能:获得key值 


参数:
    pathname 一个已经存在的文件路径,(避免传".")
    proj_id  只会使用它的低八位
----------------------------------------------------------
例:
key_t key = ftok("/home/will/",'i');


一 共享内存 


1.申请一块共享内存 


#include
#include


int shmget(key_t key, size_t size, int shmflg);
功能:申请一块指定大小共享内存
参数:
    key 


获得key方法:
a.IPC_PRIVATE : 在亲缘间进程通信使用 
b.ftok()函数获得key :可用于非亲缘关系的进程间




    size  申请的大小,(分配的实际大小是4K的倍数,但可用的大小为申请大小)


    shmflg : 


        IPC_CREAT | 0666  对应的共享内存段不存在,则创建, 存在直接返回ID 


        IPC_CREAT | IPC_EXCL | 0666 
        如果对应的共享内存段已经存在,此时shmget调用失败,错误码设为EEXIST 


返回值: 成功返回共享内存id;失败返回-1 


注意:
当key值对应的共享内存ID不存在,此时会申请一块新的共享内存区域
当key值对应的共享内存ID存在,此时直接返回已经存在的共享内存ID  


---------------------------------------------------------------
查看IPC对象


ipcs -m/-q/-s 


删除IPC对象
ipcrm -m/-q/-s  ID


-m:memory 
-q:queue 
-s:semaphore


练习:探测共享内存是否存在,如果存在,报错,如果不存在,则创建


2.共享内存映射
#include
#include


void *shmat(int shmid, const void *shmaddr, int shmflg);
功能:映射共享内存到进程的私有地址空间


参数:
    shmid   共享内存段的标识
    shmaddr NULL        让系统自动完成映射
    shmflg  0           映射可以读写;
            SHM_RDONLY  映射后只能读


返回值:
成功返回映射后的地址;失败返回 (void *)-1,并置errno


注意:进程结束,映射自动撤销


3.撤销共享内存映射
int shmdt(const void *shmaddr); 


返回值:成功返回0;失败返回-1,并置errno
注意给shmdt传的地址必须是shmat获得的!


4.删除共享内存
#include
#include


int shmctl(int shmid, int cmd, struct shmid_ds *buf);
功能:对共享内存进行控制


若cmd参数设置为IPC_RMID,可实现删除共享内存


注意:删除共享内存并不是只要有进程删除就立刻删除,
而是当映射次数为0时才会真正删除。


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




二 消息队列


1.创建消息队列


int msgget(key_t key, int msgflg);
功能:创建消息队列
参数:
    key  IPC_PRIVATE 或 ftok() 
    msgflg  IPC_CREAT | 0666  或  IPC_CREAT | IPC_EXCL | 0666
返回值:
成功返回ID,失败返回-1 




2.发送消息


//封装消息结构体
typedef struct 
{
//第一个字段必须是消息类型
long  type;


//正文部分
...
}msg_t;


//消息正文大小 
#define MSG_LEN  (sizeof(msg_t) - sizeof(long))


int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
功能:向消息队列中添加消息 


参数:
    msqid  消息队列的ID 
    msgp   消息存放的地址
    msgsz  消息正文的大小--- sizeof(msg_t) - sizeof(long)


    msgflg 
        0:阻塞的方式发送消息 
        IPC_NOWAIT:非阻塞发送消息(当消息队列中没有可用空间时,不阻塞,立即返回一个错误码EAGAIN) 


返回值:
成功返回0,失败返回-1


例如:发送消息类型是100,消息内容"hello world"

typedef struct 
{
	long type;
	char buf[1024];
#if 0/*{{{*/
    time_t t;
    pid_t pid;
#endif/*}}}*/
}msg_t;

msg_t msg;
msg_t *pmsg = (msg_t *)malloc(sizeof(msg_t));

msg.type = 100;
strcpy(msg.buf,"hello world");
sprintf(msg.buf,"hello world");

if(msgsnd(msgid,&msg,MSG_LEN,0) < 0)
{
	....
}
3.接收消息
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);
功能:接收指定类型的消息 
参数:
    msgid  消息队列ID 
    msgp   接收的消息存放的地址
    msgsz  消息正文的大小
    msgtyp 接收的消息的类型 
    msgflg 0(阻塞方式调用) 或 IPC_NOWAIT (没有指定类型消息,不阻塞,立即返回)
返回值:
    成功返回接收正文的大小,失败返回-1


例如:接收消息类型为100的消息

msg_t msg;

if(msgrcv(msgid,&msg,MSG_LEN,100,0) < 0)
{
	.....
}

printf("------------------------------\n");
printf("TYPE : %ld.\n",msg.type);
printf("MTXT : %s.\n",msg.buf);
printf("------------------------------\n");
4.删除消息队列


int msgctl(int msqid, int cmd, struct msqid_ds *buf);
功能:对消息队列进行控制


若cmd参数设置为IPC_RMID,可实现立即删除消息队列


例:msgctl(msqid,IPC_RMID,NULL);




练习:通过消息队列让A终端上父子进程和B终端上子父进程聊天
要求一边输入quit,所有进程结束.






三 信号灯集


(1)创建一个信号灯集


int semget(key_t key, int nsems, int semflg);
功能:创建或获得信号灯集 


参数:
    key  IPC_PRIVATE 或 ftok() 
    nsems 信号灯集中信号灯的个数(注意:信号灯的编号从0开始)
    semflg  IPC_CREAT | 0666 或 IPC_CREAT | IPC_EXCL | 0666


返回值:
成功返回id,失败返回-1 


(2)初始化信号灯集中的信号灯的值 


int semctl(int semid, int semnum, int cmd, .../*union semun arg*/); 
功能:对信号灯集中信号灯进行控制
参数:
        semid   :信号灯集ID
        semnum  :操作的信号灯编号,注意信号灯集中的信号灯编号从0开始
        cmd     :控制命令  SETVAL:设置信号灯值 GETVAL:获得信号灯值  都需第四个共用体类型参数
                            IPC_RMID:立即删除信号灯集
        arg     :cmd参数中若指定SETVAL需要保存要设置的信号灯值,或GETVAL




例如:初始化信号灯集中0号信号灯的值为1 

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) */
};

union semun sem_val;

sem_val.val = 1;

if(semctl(semid,0,SETVAL,sem_val) < 0)
{
	...
}
(3)封装p/v操作

int semop(int semid, struct sembuf *sops, unsigned nsops);

//参数:
    semid  信号灯集id 
    sops

struct sembuf  //其中部分成员
{
    //操作信号灯集中信号灯序号
    unsigned short sem_num;  /* semaphore number */

    //sem_op  -1:p操作  1:v操作 0:等待
    short	  sem_op;   /* semaphore operation */

    //0:阻塞 IPC_NOWAIT:非阻塞  SEM_UNDO:进程结束的时候若未释放共享资源,由内核释放申请的资源
    short	  sem_flg;  /* operation flags */

}

    nsops 操作的信号灯个数 


//->p操作:
//      信号灯集的id     操作具体的信号灯编号(从0开始)
void P(int semid,       int s_num)
{
	struct sembuf sem;
	
	sem.sem_num = s_num;
	sem.sem_op  = -1;
	/*sem.sem_flg = 0;*/
    sem.sem_flg = SEM_UNDO;

	if(semop(semid,&sem,1) < 0)
	{
		perror("fail to semop");
		exit(EXIT_FAILURE);
	}

	return;
}


//->v操作:
//               操作信号灯集中信号灯序号
void V(int semid,int s_num)
{
	struct sembuf sem;
	
	sem.sem_num = s_num;
	sem.sem_op  = 1;
	sem.sem_flg = SEM_UNDO;

	if(semop(semid,&sem,1) < 0)
	{
		perror("fail to semop");
		exit(EXIT_FAILURE);
	}

	return;
}
(4)删除信号灯集

int semctl(int semid, int semnum, int cmd, ...);

//如果cmd是IPC_RMID,不需要第四个参数
if(semctl(semid,0,IPC_RMID) < 0)
{
	perror("fail to semctl");
	exit(exit_failure);
}

你可能感兴趣的:(Linux进程开发)