进程通讯

进程间通讯的方式:

管道
管道是一种特殊的文件,对于进程来说,它和普通的文件没什么区别。

1)管道是半双工的,数据只能一个方向流动;需要双方通信时,需要建立两个管道。
2)管道只能用于父子进程或兄弟进程之间。

int pipe(int fileds[2]);

创建一对指向管道inode节点为文件描述符,将其保存在fields数组中。fileds[0]用于读取,fileds[1]用于写入。

进行父子进程之间的通信,如果是父进程写数据,子进程读数据的话。

在子进程中,关闭写入端fileds[1] fclose(fileds[1])
从读取端读取数据
read(fileds[0],msg,BUFSIZE);将数据读入msg的字符数组中。

在父进程中,关闭读取端 fclose(fileds[0])
往写入端写入数据 write(fileds[1],avg[1], strlen(avg[1]));将avg[1]的字符串写入

命名管道

先进先出的通讯方式
以文件形式存在但是没有直接写入文件系统。

int mkfifo(const char *pathname, mode_t mode)

mode指出权限。打开fifo进行读取时,没有其他进程向fifo中写入数据,进程将处于阻塞。同样,在写入数据时,如果没有进程从fifo中读,进程同样阻塞。

我们可以在main函数中根据输入的不同将当前的这个进程设置为读或者写。
这样同时在可以先将读进程在后台运行,它一直处于阻塞状态,在后台运行。
再运行写进程,并输入写的内容。

管道问题:只能传递无格式的字节流

其他IPC

IPC资源,消息队列、信号量、共享内存都属于ipc资源,在使用ipc资源前,都需要创建该资源。

key_t ftok(const char *pathname, int proj_id);

给定pathname和proj_id,产生ipc关键字。
通过ipcs查看系统中的各种ipc资源。

消息队列
消息:含有消息类型和数据的信息。消息类型可以是私有的(用于创建消息队列的进程和其子进程访问),也可以是公有的(可以被系统中所有的进程访问)。
要想让系统中的其他进程访问,必须通过一个唯一的标识(消息key),不同进程通过向消息队列写入消息或读取消息来实现进程间的数据交换。

消息队列实际上是消息链表,通过

int msgget(key_t key, int msgflg);

返回消息队列标识符msqid
可以创建或访问消息队列,根绝key和msgflg的不同。

获取或设置消息队列的属性

int msgctl(int msqid, int cmd, struct msqid_ds *buf);

根据cmd的不同,可以将msqid的属性存到buf中去;或是将内种msqid_ds的属性设置为buf的值。

向消息队列发送或从消息队列接受消息

int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);

msqid为要发送的消息队列标示符,msgp为指向msgbuf结构体的指针

struct msgbuf{
    long mtype;
    char mtext[1];
    }

mtype标识消息的类型,为大于0的任意整数。
mtext的大小由msgsz来确定。
这个msgbuf一般自己定义,然后我们有字符串比如为msg,通过
strcpy(msgbuf.mtext, msg);
将源字符串拷贝到buf中去,再调用上面的函数添加到消息队列中。

如果消息队列中有足够的空间,那么将立即返回,将msgp指向的消息加入消息队列中。否则根据msgflg的参数决定是阻塞直到有足够的空间,还是调用失败。

从消息队列中接受消息类似

size_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);

从消息队列中读取一个消息,并把消息存到msgp指向的缓存结构,它的定义类似上面的msgbuf。

需要注意的是,消息队列是用创建文件的方式建立的,如果一个进程向某个消息队列中写入了数据之后,另一个进程并没有取出数据,即使向消息队列中写数据的进程已经结束,保存在消息队列中的数据并没有消失,也就是说下次再从这个消息队列读数据的时候,就是上次的数据!!!

信号量

信号量,不能传递复杂消息,只能用于同步。用于控制多个进程对共享资源或数据的访问,使得在某个时刻,只有一个进程使用或访问资源。

临界区是一段独占某些共享资源的代码,通过信号量实现去临界区的互斥访问。

int semget(key_t key, int nsems, int semflg);

返回信号量标示符,nsems设置信号量元素的个数。

类似的semctl函数可以设置信号量集的属性(最大信号量数、做多可容纳的信号量数、最大信号量值等等)

信号量集的操作

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

用于在单个信号量集上的操作,semid表示要操作的信号量集的标示符,nsops表示要操作的信号量是哪个,sops指向用于操作该信号量的结构体

unsigned short sem_num;  //信号量的数值
short sem_op;//进行的操作
short sem_flg; //操作指定的标志位

信号量增加表示(sem_op为整数)释放资源,减少表示试图获取资源。

共享内存

最快的ipc通信方式,因为不需要在内核空间与用户空间的数据之间进行复制操作,直接进行内存的读写。例如,AB进程通信,A把需要交换的数据复制到共享内存中区,B只要到共享内存空间中去读取即可。

由于多个进程同时需要对共享内存操作,必须对共享内存的访问采用某种同步。常见的是信号量。

创建共享内存

int shmget(key_t key, size_t size, int shmflg);

同样shmctl用于设置或读取共享内存的属性。

实现对共享内存的操作

void *shmat(int shmid, const void* shmaddr, int shmflg);

将共享内存连接到指定的进程空间中,shmaddr为连接的内存地址,可以为空(NULL),这时自动选择合适的内存地址。、
eg
char *addr;
addr=(char *)shmat(shmid,NULL,0);

int shmdt(const void*shmaddr);

实现共享内存与进程空间的脱离。

相关文章
相关标签/搜索