管道可以用来在两个进程之间传递数据,如: ps -ef | grep “bash”, 其中‘|’就是管道。
其作用就是将 ps 命令的结果写入管道文件,然后 grep 再从管道文件中读出该数据进行过滤。
1. 有名管道
有名管道可以在任意两个进程之间通信
有名管道的创建:
#include <sys/types.h> #include <sys/stat.h> //filename 是管道名 mode 是创建的文件访问权限 int mkfifo(const char *filename, mode_t mode);
2. 无名管道
无名管道主要应用于父子进程间的通信。
无名管道的创建:
#include <unistd.h> /* pipe()成功返回 0,失败返回-1 fds[0]是管道读端的描述符 fds[1]是管道写端的描述符 */ int pipe(int fds[2]); 无名管道代码演示: #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <assert.h> #include <string.h> int main() { int fd[2]; int res = pipe(fd); assert( res != -1 ); pid_t pid = fork(); assert( pid != -1 ); if( pid == 0 ) { char buff[128] = {0}; read(fd[0], buff, 127); printf("child read: %s\n", buff); } else { write(fd[1], "hello", 5); } close(fd[0]); close(fd[1]); exit(0); }
3. 管道的特点
管道的实现:
信号量描述:
信号量是一个特殊的变量,一般取正数值。它的值代表允许访问的资源数目,获取资源时,需要对信号量的值进行原子减一,该操作被称为 P 操作。当信号量值为 0 时,代表没有资源可用,P 操作会阻塞。释放资源时,需要对信号量的值进行原子加一,该操作被称为 V操作。信号量主要用来同步进程。信号量的值如果只取 0,1,将其称为二值信号量。如果信号量的值大于 1,则称之为计数信号量。
临界资源:同一时刻,只允许被一个进程或线程访问的资源
临界区:访问临界资源的代码段
信号量使用:
操作信号量的接口介绍:
#include <sys/sem.h> #include <sys/types.h> #include <sys/ipc.h> /* semget()创建或者获取已存在的信号量 semget()成功返回信号量的 ID, 失败返回-1 key:两个进程使用相同的 key 值,就可以使用同一个信号量 nsems:内核维护的是一个信号量集,在新建信号量时,其指定信号量集中信号 量的个数 semflg 可选: IPC_CREAT IPC_EXCL */ int semget(key_t key, int nsems, int semflg); /* semop()对信号量进行改变,做 P 操作或者 V 操作 semop()成功返回 0,失败返回-1 struct sembuf { unsigned short sem_num; //指定信号量集中的信号量下标 short sem_op; //其值为-1,代表 P 操作,其值为 1,代表 V 操作 short sem_flg; //SEM_UNDO }; */ int semop(int semid, struct sembuf *sops, unsigned nsops); /* semctl()控制信号量 semctl()成功返回 0,失败返回-1 cmd 选项: SETVAL IPC_RMID union semun { int val; struct semid_ds *buf; unsigned short *array; struct seminfo *_buf; }; */ int semctl(int semid, int semnum, int cmd, ...);
ipcs/ipcrm 介绍
ipcs 可以查看消息队列、共享内存、信号量的使用情况,使用 ipcrm 可以进行删除操作。
共享内存原理
共享内存为多个进程之间共享和传递数据提供了一种有效的方式。共享内存是先在物理内存上申请一块空间,多个进程可以将其映射到自己的虚拟地址空间中。所有进程都可以访问共享内存中的地址,就好像它们是由 malloc 分配的一样。如果某个进程向共享内存写入了数据,所做的改动将立刻被可以访问同一段共享内存的任何其他进程看到。由于它并未提供同步机制,所以我们通常需要用其他的机制来同步对共享内存的访问。
共享内存示例代码
#include <sys/ipc.h> #include <sys/shm.h> #include <sys/types.h> /* shmget()用于创建或者获取共享内存 shmget()成功返回共享内存的 ID, 失败返回-1 key: 不同的进程使用相同的 key 值可以获取到同一个共享内存 size: 创建共享内存时,指定要申请的共享内存空间大小 shmflg: IPC_CREAT IPC_EXCL */ int shmget(key_t key, size_t size, int shmflg); /* shmat()将申请的共享内存的物理内存映射到当前进程的虚拟地址空间上 shmat()成功返回返回共享内存的首地址,失败返回 NULL shmaddr:一般给 NULL,由系统自动选择映射的虚拟地址空间 shmflg: 一般给 0, 可以给 SHM_RDONLY 为只读模式,其他的为读写 */ void* shmat(int shmid, const void *shmaddr, int shmflg); /* shmdt()断开当前进程的 shmaddr 指向的共享内存映射 shmdt()成功返回 0, 失败返回-1 */ int shmdt(const void *shmaddr); /* shmctl()控制共享内存 shmctl()成功返回 0,失败返回-1 cmd: IPC_RMID */ int shmctl(int shmid, int cmd, struct shmid_ds *buf);
消息队列原理
消息队列示例代码
#include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> /* msgget()创建或者获取一个消息队列 msgget()成功返回消息队列 ID,失败返回-1 msqflg: IPC_CREAT */ int msgget(key_t key, int msqflg); /* msgsnd()发送一条消息,消息结构为: struct msgbuf { long mtype; // 消息类型, 必须大于 0 char mtext[1]; // 消息数据 }; msgsnd()成功返回 0, 失败返回-1 msqsz: 指定 mtext 中有效数据的长度 msqflg:一般设置为 0 可以设置 IPC_NOWAIT */ int msgsnd(int msqid, const void *msqp, size_t msqsz, int msqflg); /* msgrcv()接收一条消息 msgrcv()成功返回 mtext 中接收到的数据长度, 失败返回-1 msqtyp: 指定接收的消息类型,类型可以为 0 msqflg: 一般设置为 0 可以设置 IPC_NOWAIT */ ssize_t msgrcv(int msqid, void *msgp, size_t msqsz, long msqtyp, int msqflg); /* msgctl()控制消息队列 msgctl()成功返回 0,失败返回-1 cmd: IPC_RMID */ int msgctl(int msqid, int cmd, struct msqid_ds *buf);