进程间通讯
1 管道通信
1.1 匿名管道
定义
匿名管道用来实现具有亲缘关系的进程之间的通信,匿名管道是由内核创建的一块内存区域,返回这块区域对应的文件描述符后,进程间可以通过这个文件描述符的操作来进行通信。
特点
- 由内核提供
- 单工通信
- 自同步机制,读端没有数据的时候会阻塞直到有数据写入后接着读,写端写满后会阻塞直到有数据被读取后再接着写入
相关函数
- pipe() 会生成两个文件描述符回填到pipefd中去,其中pipefd[0]为读端,pipefd[1]为写端
int pipe(int pipefd[2]);
- 成功返回0,失败返回-1并设置errno
- 示例
#define BUFSIZE 1024
//利用管道实现父写自读的一个程序
int main(int argc, char **argv) {
pid_t pid;
int ps[2];
size_t len;
char buf[BUFSIZE];
if (pipe(ps) == -1) {
perror("pipe()");
exit(1);
}
pid = fork();
if (pid < 0) {
perror("fork()");
exit(1);
}
if (pid == 0) { //子进程
close(ps[1]);
len = read(ps[0], buf, BUFSIZE);
write(1, buf, len);
close(ps[0]);
exit(0);
} else {
close(ps[0]);
write(ps[1], "Hello world!", 12);
close(ps[1]);
wait(NULL);
exit(0);
}
}
1.2 命名管道
定义
命名管道能够实现任意进程之间的通信,命名管道是由内核在磁盘上创建的一个文件,进程间通过操作这个文件来实现通信
特点
- 由内核提供
- 单工通信
- 自同步机制,读端没有数据的时候会阻塞直到有数据写入后接着读,写端写满后会阻塞直到有数据被读取后再接着写入
相关函数
- mkfifo() 创建一个名称为path的管道,mode指定权限
/* Create a new FIFO named PATH, with permission bits MODE. */
extern int mkfifo (const char *__path, __mode_t __mode);
- 成功返回0,失败返回-1并设置errno
2 IPC
2.1 ftok()
-
定义
系统建立IPC通信时需要一个key,这个key就通过ftok()函数给出,pathname是一个已存在的文件名,proj_id就是子序号,虽然是int型,但是只是用8bits。在一般的unix实现中,key的值就是子序号加上pathname文件的额inode号
key_t ftok(const char *pathname, int proj_id);
2.2 消息队列
msgget() 获取消息队列
- 返回一个消息队列的id,成功返回消息队列id,失败返回-1并设置errno
int msgget(key_t key, int msgflg);
msgop() 操作消息队列
//发送消息
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
//接收消息,msgid为消息队列id,msgp为收取的消息存放位置,msgsz为收取消息的大小,msgtype表示收取的第几个包
//msgflg为特殊要求
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,
int msgflg);
msgctl() 其他控制
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
实例:传输结构体信息
- 协议头文件
#ifndef MSG_PROTO_H
#define MSG_PROTO_H
#define KEYPATH "/etc/services"
#define KEYPROJ 'g'
#define NAMESIZE 1024
//要传递的消息体
struct msg_st {
long mtype;
char name[NAMESIZE];
int chinese;
int math;
};
#endif //MSG_PROTO_H
- 接收方(被动端)
static int msgid;
static void handler(int s) {
//销毁消息队列
msgctl(msgid, IPC_RMID, NULL);
exit(0);
}
int main() {
key_t k;
struct msg_st rbuf;
struct sigaction sa;
sa.sa_handler = handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
sa.sa_restorer = NULL;
sigaction(SIGINT, &sa, NULL);
//获取key
k = ftok(KEYPATH, KEYPROJ);
if (k < 0) {
perror("ftok()");
exit(1);
}
//获取消息队列id,被动方需要有IPC_CREAT特殊要求来创建消息队列,0600为消息队列的使用权限
msgid = msgget(k, IPC_CREAT | 0600);
if (msgid < 0) {
perror("msgget()");
exit(1);
}
//收取信息
while (1) {
if (msgrcv(msgid, &rbuf, sizeof(rbuf) - sizeof(long), 0, 0) < 0) {
perror("msgrcv()");
exit(1);
}
printf("name = %s, cn = %d, math = %d\n", rbuf.name, rbuf.chinese, rbuf.math);
}
}int main() {
key_t k;
int msgid;
struct msg_st rbuf;
//获取key
k = ftok(KEYPATH, KEYPROJ);
if (k < 0) {
perror("ftok()");
exit(1);
}
//获取消息队列id,被动方需要有IPC_CREAT特殊要求来创建消息队列,0600为消息队列的使用权限
msgid = msgget(k, IPC_CREAT | 0600);
if (msgid < 0) {
perror("msgget()");
exit(1);
}
//收取信息
while (1) {
if (msgrcv(msgid, &rbuf, sizeof(rbuf) - sizeof(long), 0, 0) < 0) {
perror("msgrcv()");
exit(1);
}
printf("name = %s, cn = %d, math = %d\n", rbuf.name, rbuf.chinese, rbuf.math);
}
//销毁消息队列
msgctl(msgid, IPC_RMID, NULL);
exit(0);
}
- 发送端(主动端)
int main() {
key_t k;
struct msg_st sbuf;
int msgid;
//获取key
k = ftok(KEYPATH, KEYPROJ);
if (k < 0) {
perror("ftok()");
exit(1);
}
//获取消息队列,主动方不需要特殊的msgflg
msgid = msgget(k, 0);
if (msgid < 0) {
perror("msgget()");
exit(1);
}
//发送消息
sbuf.mtype = 1;
strcpy(sbuf.name, "Alan");
sbuf.math = rand() % 100;
sbuf.chinese = rand() % 100;
if (msgsnd(msgid, &sbuf, sizeof (sbuf) - sizeof (long ), 0) < 0) {
perror("msgsnd()");
exit(1);
}
exit(0);
}
2.3 信号量
semget() 创建信号量
- 获取一个为信号量数组,数组中的信号量个数为nsems,semflg为特殊要求
int semget(key_t key, int nsems, int semflg)
semop() 操作信号量
- 对semid信号量数组中信号量执行nsops个sops操作
int semop(int semid, struct sembuf *sops, size_t nsops);
int semtimedop(int semid, struct sembuf *sops, size_t nsops,
const struct timespec *timeout);
semctl() 信号量其他控制操作
- 对semid信号量数组中的下标为semnum的信号量进行cmd操作
int semctl(int semid, int semnum, int cmd, ...);
实例 多个进程给文件中的数+1
#define PROCNUM 20
#define FNAME "out"
#define LINESIZE 1024
static int semid;
static void P() {
struct sembuf op;
op.sem_num = 0;
op.sem_op = -1; //表示在下标为sem_num的信号量上加上sem_op
op.sem_flg = 0;
while (semop(semid, &op, 1) < 0) {
if (errno == EINTR || errno == EAGAIN) continue;
perror("semop()");
exit(1);
}
}
static void V() {
struct sembuf op;
op.sem_num = 0;
op.sem_op = 1; //表示在下标为sem_num的信号量上加上sem_op
op.sem_flg = 0;
while (semop(semid, &op, 1) < 0) {
if (errno == EINTR || errno == EAGAIN) continue;
perror("semop()");
exit(1);
}
}
static void func_add() {
FILE *fp;
char linebuf[LINESIZE];
fp = fopen(FNAME, "r+");
if (fp == NULL) {
perror("fopen()");
exit(1);
}
P();
fgets(linebuf, LINESIZE, fp);
fseek(fp, 0, SEEK_SET);
fprintf(fp, "%d\n", atoi(linebuf) + 1);
fflush(fp);
V();
fclose(fp);
}
int main(int argc, char **argv) {
pid_t pid;
int i;
//获取信号量
semid = semget(IPC_PRIVATE, 1, 0600);
if (semid < 0) {
perror("semget()");
exit(1);
}
//设置信号量的值
if (semctl(semid, 0, SETVAL, 1) < 0) {
perror("semctl()");
exit(1);
}
for (i = 0; i < PROCNUM; i++) {
pid = fork();
if (pid < 0) {
perror("fork()");
exit(1);
}
if (pid == 0) {
func_add();
exit(1);
}
}
for (i = 0; i < PROCNUM; i++) {
wait(NULL);
}
//销毁信号量数组
semctl(semid, 0, IPC_RMID);
exit(0);
}
2.4 共享内存
shmget() 获取共享内存
- size为共享内存的到小
int shmget(key_t key, size_t size, int shmflg);
shmop() 操作共享内存
//将自己的shmaddr内存和共享内存做映射后返回一个指针用于操作共享内存
void *shmat(int shmid, const void *shmaddr, int shmflg);
//删除共享内存
int shmdt(const void *shmaddr);
shmctl() 共享内存其他控制操作
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
===