进程间通信

进程间通信

管道

  1. 父子进程之间不共享数据段和堆栈段,通过管道进行通信
  2. 管道是两个进程之间进行单项通信的机制,是一种半双工管道
  • 管道只能用于具有亲缘关系的进程(父子进程和兄弟进程)
  • 管道没有名字
  • 管道的缓冲区大小受限制
  • 管道传输的是无格式的字节流
  1. 两端进程向管道读写数据是通过创建管道时系统设置的文件描述符进行的,管道本质上也是一种文件
  2. 写数据每次都添加到管道缓冲区的末尾,读数据是从缓冲区的头部读出数据的
  3. 管道由函数int pipe(int fd[2]);函数创建,依赖头文件#include <unistd.h>
  4. 有名管道已FIFO的文件形式存在于文件系统中,即使访问该路径的进程不存在亲缘关系也可以通过FIFO相互通信
  • 实现两个不相关进程之间的通信
  • 该管道可以通过路径名指出,在文件系统中是可见的
  • FIFO严格的遵守先进先出原则,从管道头部读出数据,将写操作的数据添加到末尾
    1
    2
    3
    4
    5
    6
    #include <sys/sypes.h>
    #include <sys/stat.h>

    int mkfifo(const char *pathname, mode_t mode);
    //pathname是路径名也就是创建后FIFO的名字,mode和open()函数中的mode参数
    //如果路径已经存在则返回EEXIST错误

消息队列

  1. 用于运行于同一条机器上的进程间通信,在一个系统内核中用来保存消息的队列,在系统内核中以消息链表的形式出现的
  2. 创建消息队列或取得已经存在的消息队列

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    int msgget(key_t key, int msgflg); 
    //key是端口号,也可以通过ftok函数生成
    //msgflg如果等于IPC_CREAT,如果没有该队列,则创建一个并返回一个标识符,如果已经存在则返回原标识符
    //msgflg等于IPC_EXCL,如果没有该队列返回-1,如果已经存在返回0

    ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
    //从队列中取用消息
    //msqid是消息队列的标识码
    //msgp是指向消息缓冲区的指针
    //msgsz是消息的大小
    //msgstp是从消息队列中读取的消息形态,如果值为0表示消息队列中的所有消息都会被读取
    //msgflg用来指明核心程序在队列没有数据的情况下应该采取的行动

    struct msgstru{
    long mtype;
    char mtext[512];
    };

    int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
    //将数据放到消息队列中

    int msgctl(int msgqid, int cmd, struct msqid_ds *buf);
    //设置消息队列的属性
    //对msgqid表示的消息队列执行cmd操作
    //IPC_STAT来获取消息队列的msqid_ds数据结构,并保存到buf指定的地址空间
    //IPC_SET设置消息队列的属性,要设置的属性存储在buf中
    //IPC_RMID从内核中删除msqid标识的消息队列
  3. 消息队列和有名管道有相似之处,也有优势:

  • 消息队列可以用于独立发送和接收进程而存在,消除同步有名管道打开和关闭可能产生的困难
  • 可以同时通过发送信息来避免命名管道的同步和阻塞问题,不需要由进程自己来提供同步方法
  • 接收程序可以通过消息类型有选择的接收数据

共享内存

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <sys/shm.h>

int shmget(key_t key, int size, int flag);
//不相关的内存可以通过该函数的返回值访问同一个共享内存
//key为共享内存段命名,函数运行成功,则返回一个与key相关的共享内存标识符,用于后续的共享内存函数,调用失败返回-1
//size指定共享内存的容量

void *shmat(int shmid, void *addr, int flag);
//内存创建之后,其余进程可以调用shmat函数将其连接到自身的地址空间中
//shmid为shmget函数返回的共享存储标识符
//addr和flag参数决定以什么方式决定连接的地址,函数返回值是该进程数据段所连接的实际地址
//不是原子操作,如果有多个程序同时向共享内存中读写数据,执行不安全

int shmdt(const void *shmaddr);
//函数用于从共享内存从当前进程中分离
//参数shmaddr是函数shmat函数返回的地址指针,调用成功返回0,失败返回-1

信号量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>

int semget(key_t key, int nsems, int semflg);
//用于创建和打开信号量
//执行成功返回0,失败返回-1
//参数key是调用ftok函数得到的键值
//nsems代表创建信号量的个数,如果只是访问而不创建可以指定这个参数为0,一旦创建信号量就不能改变信号量的个数
//只要不删除该信号量,就可以调用这个函数创建该键值的信号量,但是不能更新key的值
//semflg执行该信号量的读写权限

int semop(int semid, struct sembuf *sops, unsigned nsops);
//用于改变信号量的值
//sem_id是由semget返回的信号量标识符

struct sembuf{
short sem_num; //除非使用一组信号量,否则就是0
short sem_op; //信号量在一次操作中需要改变的数据,-1表示等待操作,+1表示发送信号操作
short sem_flg; //通常是SEM_UNDO,使操作系统跟踪信号,在进程没有释放该信号量而终止时,操作系统释放信号量
};

int semctl(int semid, int semnum, int cmd, ...);
//函数用来直接控制信号量信息
//cmd通常是SETVAL或IPC_RMID
//SETVAL用来把信号量初始为一个已知的值,p值通过union semun中的val,在信号量第一次使用前对它进行设置
//IPC_RMID用于删除一个已经无需继续使用的信号量标识符
//第四个参数通常是union semum结构
union semun{
int val;
struct semid_ds *buf;
usigned short *arry;
};

ipcs命令

  1. ipcs -a列出本用户相关的ipcs参数
  2. ipcs -q列出进程中的消息队列
  3. ipcs -s用于列出所有的信号量
  4. ipcs -m用于列出所有共享内存信息
  5. ipcs -l用于列出系统的险恶
  6. ipcs -t列出最后的访问时间
  7. ipcs -u列出当前的使用情况
WhitneyLu wechat
Contact me by scanning my public WeChat QR code
0%