Chinaunix首页 | 论坛 | 博客
  • 博客访问: 40081
  • 博文数量: 31
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 330
  • 用 户 组: 普通用户
  • 注册时间: 2015-07-28 17:39
文章分类
文章存档

2015年(31)

我的朋友

分类: C/C++

2015-11-02 18:56:49

IPC:
UNIX:管道,FIFO及信号
system V : 消息队列,信号量,共享内存
posix : 消息队列,信号量,共享内存
BSD: socket

1.信号:用户空间和内核空间的交互,内核进程利用他来通知用户空间发生的系统事件。
阻塞的信号:由内核保存,直到阻塞取消后才传递给进程

不可靠信号:如果信号已经注册,就忽略该信号。因此会产生信号丢失
可靠信号:信号产生就注册。不会丢失信号

2.管道:
2.1标准管道流
FILE *popen(const char *command, const char *open_mode);
int pclose(FILE *fp);


这和多进程中的用法是一直的


2.2无名管道
2.2.1只能在亲缘关系的进程中通信(fork产生的进程),
2.2.2固定的读端和写端
2.2.3特殊的文件,可以read,write,只能在内存中


int pipe(int fds[2]);
fds[0]是读端
fds[1]是写端


先关写端,则read返回0;
先关读端,写端进程继续写数据则会收到SIGPIPE信号;如果不对该信号处理,则写进程终止;如果写进程处理了该信号,则write函数返回一个负值。


2.3命名管道
2.3.1
int mkfifo(const char *pathname, mode_t mode);
pathname:创建的文件全路径名
mode:文件权限


2.3.2
int unlink(const char *pathname);//删除文件


2.3.3打开关闭
使用open和close函数
open中使用O_RDONLY时,没有其他进程以写的方式打开,则open函数阻塞;
open中使用O_WRONLY时,没有其他进程以读的方式打开,则open函数阻塞;
如果加上O_NOBLOCK则不被阻塞。


先关读端,写端进程继续写数据则会收到SIGPIPE信号;这与pipe是一致。


环形缓冲区为一个大页64k,数据大于一个小页4k就分批传送。如果缓冲区满,则写端阻塞。


查看页大小命令:getconf PAGESIZE


3.内存映射

将文件内容映射到内存中,返会内存的起始地址,对该内存的存取就是对文件的读写。
void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offsize);

start:NULL,代表让系统自动选定地址
length:文件的多大部分映射到内存
prot:
PROT_EXEC 映射区域可被读取
PROT_WRITE 映射区域可被写
PROT_READ 映射区域可被读
PROT_NONE 映射区域不能存取

flags:
MAP_SHARED 映射区域的数据会写入文件,允许其他映射该文件的进程共享
MAP_PRIVATE 对区域的写入会产生一个映射文件的复制,对区域的修改不会写回原来的文件

fd:open函数返回的描述符,代表映射到内存的文件,在不同的进程中,需要打开同一个文件。
offset:文件映射的偏移量,一般为0,必须是分页大小的整数倍

//写入文件
int msync(const void *start, size_t length, int flags);

//取消参数start起始地址length大小的内存映射
int munmap(void *start, size_t length);

文件读写加速,当要操作文件是,可以用这个方法直接对内存操作。


4.共享内存
不同进程的虚拟内存,映射到同一块物理内存上。这块内存以key来标识,

key_t ftok(const char *pathname, int proj_id);
int shmget(key_t key, int size, int shmflg);
void *shmat(int shmid, const void *shmaddr, int shmflg);
int shmdt(const void *shmaddr);
int shmctl(int shmid, int cmd, struct shmid_ds *buf);

key_t ftok(const char *pathname, int proj_id);
pathname:可访问的文件名和非0的字符proj_id组合成一个key返回;

int shmget(key_t key, int size, int shmflg);
key:ftok的返回值
shmflg:IPC_CREAT和IPC_EXCL组和

//将内存映射到进程空间的某一地址,返回映射后进程空间的首地址
void *shmat(int shmid, const void *shmaddr, int shmflg);
shmid:shmget函数的返回值
shmaddr:当前进程的位置,NULL表示由系统选择
shmflg:0

//共享内存和当前空间分离
int shmdt(const void *shmaddr);
shmaddr:shmat成功后的返回值

int shmctl(int shmid, int cmd, struct shmid_ds *buf);
shmid:shmat成功后的返回值
cmd:IPC_RMID 删除共享内存段
buf:共享内存段的信息结构体,常为NULL

A:shmid标识的共享内存段与当前进程空间的shmaddr建立起联系shmat函数
B:shmid的产生:shmget函数
C:key的产生ftok函数
D:空间分离shmdt
E:空间删除shmctl

两个进程的虚拟空间,映射到同一块物理内存上。

5.消息队列
可以先往消息队列中写消息。
如果先打开读则会阻塞。

int msgget(key_t key, int msgflg);
int msgsnd(int msgid, struct msgbuf *msgp, size_t msgsize, int msgflg);
ssize_t msgrcv(int msgid, struct msgbuf Imsgp, size_t msgsize, long msgtype, int msgflg);
int msgctl(int msgid, int cmd, struct msgid_ds *buf);

//创建一个消息队列
int msgget(key_t key, int msgflg);
key:可以有ftok产生
msgflg:IPC_CREAT 和IPC_EXCL

int msgsnd(int msgid, struct msgbuf *msgp, size_t msgsize, int msgflg);
msgid:msgget成功后的返回值

重写这个结构体:
struct msgbuf
{
long mtype;
char mtext[1];
};

msgsize:消息体的大小
msgflg:通常为0,(msgsnd队满时阻塞,msgrcv队空时阻塞,如果是IPC_NOWAIT这两种情况将返回错误)

ssize_t msgrcv(int msgid, struct msgbuf *msgp, size_t msgsize, long msgtype, int msgflg);
msgtype:==0 接收第一个消息
>0 接收队列中第一个类型等于msgtype的消息
  <0  接收队列中第一个类型小于等于-msgtype的最低类型消息

int msgctl(int msgid, int cmd, struct msgid_ds *buf);
cmd:IPC_RMID 删除队列

6.system V信号量
为获得共享资源进行pv操作:
测试控制资源的信号量
信号量为正,则减一,表示使用了一个资源
信号量为0,则进程等待,直到信号量大于0。

int semget(key_t key, int nsems, int flags);
int semop(int semid, struct sembuf *sops,  size_t nops);
int semctl(int semid, int semnun, int cmd, ...);

int semget(key_t key, int nsems, int flags);
key:可以由ftok产生
nsems:创建新的集合中有几个信号量
flags:IPC_CREAT IPC_EXCL

int semop(int semid, struct sembuf *sops,  size_t nops);
semid:semget的返回值
struct sembuf
{
short sem_num; //信号集中的编号
short sem_op; //-1 --->p操作, +1--->v操作


short sem_flg; //SEM_UNDO ,程序结束,信号量为semop调用前的值
};
nops:sops结构数组元素个数(一次改变几个信号量)


int semctl(int semid, int semnum, int cmd, ...);
semnum:集合中信号量的编号,当用到成组的信号量集合是从0开始。
cmd:
IPC_RMID:删除信号集
GETVAL:根据semnum返回信号的值
SETVAL:根据semnum设定信号的值
GETALL:获取所有信号的值,第二个参数为0,存入到semun.array中
SETALL::设置所有信号的值,第二个参数为0,将semun.array的值设定到信号集中


这个结构体需要自己定义:
union semun
{
int val; //SETVAL
struct semid_ds *buf; //IPC_STAT ,IPC_SET
unsigned short *array; //GETALL ,SETALL
};

7.posix有名信号量
sem_t *sem_open(const char *pathname, int flag, mode_t mode, int init);
int sem_close(sem_t *psem);
sem_wait(sem_t *psem); //p操作,-1
sem_post(sem_t *psem); //v操作,+1

sem_t *sem_open(const char *semname, int flag, mode_t
mode, int init);
semname:文件名,不要包含路径,默认在/dev/shm/

//关闭
int sem_close(const char *semname);

//删除信号量
int sem_unlink(sem_t *sem);

int sem_wait(sem_t *psem);
int sem_post(sem_t *psem);

//信号量的值保存到sval中
int sem_getvalue(sem_t *sem, int *sval);

posix有名信号灯保存在文件中,用完后要关闭删除。
编译过程中要加-lpthread选项。
阅读(685) | 评论(0) | 转发(0) |
0

上一篇:进程

下一篇:线程

给主人留下些什么吧!~~