- 要求实现AB进程对话
1. A进程先发送一句话给B进程,B进程接收后打印
2. B进程再回复一句话给A进程,A进程接收后打印
3. 重复1.2步骤,当收到quit后,要结束AB进程
4. 提示:两根管道
消息队列实现AB进程对话;
共享内存和信号量集完成多进程字符串翻转与输出
分类:
在进程的3G~4G的内核空间中,创建一个管道(特殊的文件),管道中的数据直接保存在内存中。
为什么无名管道只能用于具有亲缘关系的进程间通信
功能:
创建一个无名管道文件,同时打开管道的读写端。
原型:
#include
int pipe(int pipefd[2]);
参数:
int pipefd[2]:需要传入一个int类型的数组,且数组的容量为2;
用于存储读写端文件描述符;
pipefd[0]:读端文件描述符;
pipefd[1]:写端文件描述符;
返回值:
成功,返回0;
失败,返回-1,更新errno;
有名管道的特点
创建有名管道
mkfifo 有名管道的路径及名字
mkfifo ./myfifo
功能:创建一个有名管道文件;
原型:
#include
#include
int mkfifo(const char *pathname, mode_t mode);
参数:
char *pathname:指定要创建的有名管道路劲及名字;
mode_t mode:文件创建时候的权限,真实权限为 (mode & ~umask);
返回值:
成功,返回0;
失败,返回-1,更新errno;
文件已经存在导致的错误是合法错误 ,errno == 17
#define EEXIST 17
对有名管道的操作
有名管道的open规则
#include
#include
#include
int open(const char *pathname, int flags);
int flags:
O_RDONLY 只读
O_WRONLY 只写
O_RDWR 读写
----以上三种必须包含一种,且只能包含一种----
O_NONBLOCK 非阻塞选项
操作同一根有名管道,只有读写端均存在此时open函数才能
kill -l 查看所有信号 62个信号,其中32 33没有
1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP
6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1
11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM
16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ
26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR
31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3
38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8
43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7
58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2
63) SIGRTMAX-1 64) SIGRTMAX
功能:
捕获信号,为信号注册新的处理函数;
原型:
#include
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler); //sighandler_t handler ==> void (*handler)(int);
参数:
int signum:指定要捕获的信号,可以填对应的编号2 或者宏SIGINT;
sighandler_t handler:
1. SIG_IGN, 忽略信号。9) 19)无法忽略
2. SIG_DFL, 执行默认操作,缺省操作
3. 给信号注册新的处理函数。函数指针变量,回调函数,该指针可以指向返回值是void类型,参数列表是int类型的函数。
该函数代表新注册的处理函数。
返回值:
成功,返回注册后,上一个处理函数的首地址。
失败,返回SIG_ERR,更新errno;
注意:
当在某个信号A的处理函数内部时,若再次收到信号A,则此时A信号的信号处理函数不会被重新载入,
此时第二个信号A被屏蔽。
功能:
给指定进程或者进程组发送一个信号;
原型:
#include
#include
int kill(pid_t pid, int sig);
查看消息队列: ipcs
ipcs -q
删除消息队列:ipcrm -q msqid
功能:
需要使用到pathname提供的id号 和proj_id提供的8bits非0 参数,用于去计算键值,给msgget shmget semget函数使用。
简单来说,用户就是通过键值找IPC对象,只要键值不变,则找到的IPC对象就是同一个。
原型:
#include
#include
key_t ftok(const char *pathname, int proj_id);
参数:
char *pathname:路径及名字; (文件必须存在,且可访问即可)
int proj_id:非0参数即可;
返回值:
成功,返回key值;
失败,返回-1,更新errno;
功能:
通过key值找对应的消息队列,若不存在则需要创建,若存在则返回消息队列id号;
原型:
#include
#include
#include
int msgget(key_t key, int msgflg);
参数:
key_t key:指定要通过那个key值找消息队列;
int msgflg:
IPC_CREAT:若消息队列不存在则创建消息队列,若存在则忽略该选项;
IPC_CREAT | 八进制权限: IPC_CREAT| 0664
IPC_CREAT | IPC_EXCL :若消息队列不存在则创建消息队列,若存在则报错;
返回值:
成功,返回非负整数,即消息队列id号,我们一般成为msqid;
失败,返回-1,更新errno;
功能:
向指定的消息队列中发送数据;
原型:
#include
#include
#include
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
参数:
int msqid:消息队列的id号;
void *msgp:指定要发送的消息包;消息包的通用格式如下;
struct msgbuf {
long mtype; /* message type, must be > 0 */ 消息类型,必须大于0
char mtext[1]; /* message data */ 消息内容,可以根据需求改变成其他结构,但是大小由第三个参数msgsz指定
};
size_t msgsz:消息内容的大小,sizeof(真实的消息内容);
int msgflg:
0:阻塞方式,当消息队列满了,该函数阻塞;
IPC_NOWAIT:非阻塞方式,当消息队列满了,该函数不阻塞,立即返回失败情况,且errno == EAGAIN;
返回值:
成功,返回0;
失败,返回-1,更新errno;
功能:
从指定的消息队列中读取数据;
原型:
#include
#include
#include
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,
int msgflg);
参数:
int msqid:指定要从哪个消息队列中读取数据,填对应的消息队列id号;
void *msgp:存储从消息队列中读取到的消息;消息包的通用格式如下;
struct msgbuf {
long mtype; /* message type, must be > 0 */ 消息类型,必须大于0
char mtext[1]; /* message data */ 消息内容,可以根据需求改变成其他结构,但是大小由第三个参数msgsz指定
};
size_t msgsz:指定要读取的消息内容的大小,sizeof(真实的消息内容);
long msgtyp:指定要读取的消息类型;
msgtyp == 0, 读取消息队列中第一条消息; 即按照先进先出的原则读取
msgtyp >0, 读取消息队列中第一条消息类型 == msgtyp参数的消息;
若在msgflg中指定了MSG_EXCEPT,则会读取消息队列中第一条消息类型 != msgtyp参数的消息;
vi -t MSG_EXCPET ==> #define MSG_EXCEPT 020000
msgtyp <0, 读取消息队列中第一条最小的消息,且该消息类型<= msgtyp参数的绝对值
int msgflg:
0:阻塞方式读取,当消息队列中没有要读取的数据的时候,该函数阻塞;
IPC_NOWAIT:非阻塞,当消息队列中没有要读取的数据的时候,该函数不阻塞,函数运行失败,errno == ENOMSG;
MSG_EXCEPT:
返回值:
成功,返回成功读取到的字节数;
失败,返回-1,更新errno;
代码示例:
若消息队列中有消息:
mtype 100 101 99 100 101
mtext aaa bbb ccc ddd eee
while(1)
{
//阻塞方式读取消息队列中第一条消息,先进先出的原则
//res = msgrcv(msqid, &rcv, sizeof(rcv.mtext), 0, 0);
//非阻塞方式读取消息队列中第一条消息,先进先出的原则
res = msgrcv(msqid, &rcv, sizeof(rcv.mtext), 0, IPC_NOWAIT);
if(res < 0)
{
perror("msgrcv");
return -1;
}
printf("res=%ld : %ld %s\n", res, rcv.mtype, rcv.mtext);
}
输出顺序:
100 aaa 101 bbb 99 ccc 100 ddd 101 eee
while(1)
{
//1.阻塞方式读取消息队列中第一条消息类型 == 101 的消息
//res = msgrcv(msqid, &rcv, sizeof(rcv.mtext), 101, 0);
//2.非阻塞方式读取消息队列中第一条消息 == 101 的消息
//res = msgrcv(msqid, &rcv, sizeof(rcv.mtext), 101, IPC_NOWAIT);
//3.非阻塞方式读取消息队列中第一条消息类型 != 101的消息
res = msgrcv(msqid, &rcv, sizeof(rcv.mtext), 101, IPC_NOWAIT|020000);
if(res < 0)
{
perror("msgrcv");
return -1;
}
printf("res=%ld : %ld %s\n", res, rcv.mtype, rcv.mtext);
}
注释1,2的现象:
101 bbb 101 eee
第3个的现象:
100 aaa 99 ccc 100 ddd
while(1)
{
//阻塞方式读取消息队列中<=msgtyp参数指定的消息类型中最小的那条消息;
res = msgrcv(msqid, &rcv, sizeof(rcv.mtext), -100, 0);
//非阻塞方式读取消息队列中<=msgtyp参数指定的消息类型中最小的那条消息;
//res = msgrcv(msqid, &rcv, sizeof(rcv.mtext), -100, IPC_NOWAIT);
if(res < 0)
{
perror("msgrcv");
return -1;
}
printf("res=%ld : %ld %s\n", res, rcv.mtype, rcv.mtext);
}
现象:
res=128 : 99 ccc
res=128 : 100 aaa
res=128 : 100 ddd
功能:
控制消息队列,常用于删除消息队列;
原型:
#include
#include
#include
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
参数:
int msqid:消息队列的id号;
int cmd:
IPC_RMID:删除消息队列; 第三个参数填NULL;
IPC_STAT:获取消息队列属性; 存储到第三个参数指向的内存空间中
struct msqid_ds *buf:
返回值:
成功,返回0;
失败,返回-1,更新errno;
查看共享内存: ipcs
ipcs -m
删除共享内存:ipcrm -m shmid
功能:
需要使用到pathname提供的id号 和proj_id提供的8bits非0 参数,用于去计算键值,给msgget shmget semget函数使用。
简单来说,用户就是通过键值找IPC对象,只要键值不变,则找到的IPC对象就是同一个。
原型:
#include
#include
key_t ftok(const char *pathname, int proj_id);
参数:
char *pathname:路径及名字; (文件必须存在,且可访问即可)
int proj_id:非0参数即可;
返回值:
成功,返回key值;
失败,返回-1,更新errno;
功能:
通过key值找对应的共享内存,若不存在则需要创建,若存在则返回共享内存id号;
原型:
#include
#include
int shmget(key_t key, size_t size, int shmflg);
参数:
key_t key:指定要通过那个key值找共享内存;
size_t size:指定创建多大的共享内存;
int shmflg:
IPC_CREAT:若共享内存不存在则创建共享内存,若存在则忽略该选项;
IPC_CREAT | 八进制权限: IPC_CREAT| 0664
IPC_CREAT | IPC_EXCL :若共享内存不存在则创建共享内存,若存在则报错;
返回值:
成功,返回非负整数,即共享内存id号,我们一般成为shmid;
失败,返回-1,更新errno;
功能:
将共享内存映射到用户空间中;
原型:
#include
#include
void *shmat(int shmid, const void *shmaddr, int shmflg);
参数:
int shmid:指定要映射的共享内存的id号;
void *shmaddr:指定要将共享内存映射到用户空间的什么位置,例如想要映射到0x80000000该地址上,就需要填(void*)0x80000000
填NULL,代表让编译器自动映射;
int shmflg:
0 ,代表映射的进程对共享内存可读可写;
SHM_RDONLY:只读;
返回值:
成功,返回共享内存映射到用户空间的地址;
失败,返回-1,更新errno;
注意:获取到的映射空间的首地址的指向不允许修改,若修改后会导致首地址找不到,导致内存泄漏,与堆空间首地址不能改变的概念一致
功能:
断开共享内存的映射
原型:
#include
#include
int shmdt(const void *shmaddr);
参数:
void *shmaddr:将哪一块用户空间与共享内存断开映射;
返回值:
成功,返回0;
失败,返回-1,更新errno
功能:控制共享内存,常用于删除共享内存;
原型:
#include
#include
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
参数:
int msqid:共享内存的id号;
int cmd:
IPC_RMID:删除共享内存; 第三个参数填NULL;
IPC_STAT:获取共享内存属性; 存储到第三个参数指向的内存空间中
struct shmid_ds *buf:
返回值:
成功,返回0;
失败,返回-1,更新errno;
ipcs
ipcs -s
ipcrm -s semid
功能:
通过pathname参数给定文件的id号 和 proj_id给定的非0参数,计算key值(键值),提供给msgget shmget semget函数使用;
只要key值不变,则通过key值找到的IPC对象就是同一个。
原型:
#include
#include
key_t ftok(const char *pathname, int proj_id);
参数:
char *pathname:文件路径以及名字; 文件必须存在且可以访问即可;
int proj_id:非0参数,用户自定义;
返回值:
成功,返回key值;
失败,返回-1,更新errno;
功能:
通过key值找对应的信号灯集,若不存在则需要创建,若存在则返回信号灯集id号; 创建成功后,信号灯的值默认为0;
原型:
#include
#include
#include
int semget(key_t key, int nsems, int semflg);
参数:
key_t key:ftok计算出来的键值;
int nsems:信号灯集中有几个信号灯;
int semflg:
IPC_CREAT:如果信号灯集不存在,则创建,如果存在则忽略该选项;
IPC_CREAT | 0664
IPC_CREAT|IPC_EXCL:如果信号灯集存在,则该函数运行失败,errno == EEXIST;
返回值:
成功,返回非负数,即信号灯集id , semid;
失败,返回-1,更新errno;
功能:
P操作(阻塞,不够减阻塞) V操作 wait for zero操作(不为0阻塞)
原型:
#include
#include
#include
int semop(int semid, struct sembuf *sops, size_t nsops);
参数:
int semid:指定要操作的信号灯集:
struct sembuf *sops:
struct membuf{
unsigned short sem_num; /* semaphore number */ 指定要控制登记中信号灯的编号,从0开始
short sem_op; /* semaphore operation */ 填正整数,V操作,例如+2,代表在信号灯原来的值上+2;
填负整数,P操作,例如-2,代表在信号灯原来的值上-2,若不够减阻塞。
0,wait-for-zero操作,信号灯的值为0不阻塞,不为0阻塞。
short sem_flg; /* operation flags */ 0,代表阻塞方式。该阻塞的时候阻塞。P操作, wait for zero操作该阻塞时阻塞。
IPC_NOWAIT:非阻塞方式,该阻塞的时候不阻塞。此时函数运行失败.
}
size_t nsops:指定要控制的信号灯集中灯的个数;
返回值:
成功,返回非负数,即信号灯集id , semid;
失败,返回-1,更新errno;
功能:
控制信号灯集;
原型:
#include
#include
#include
int semctl(int semid, int semnum, int cmd, ...);
参数:
int semid:指定要控制哪个信号灯集;
int semnum:指定要控制灯集中的哪个灯,填对应的编号;
int cmd:
IPC_STAT:获取信号灯集的属性,存储到最后一个参数中,最后一个参数的类型:struct semid_ds *buf
IPC_RMID:删除信号灯集,第二个参数无意义,最后一个参数不用填;
GETALL:获取信号灯集中,所有灯的值。第二个参数无意义,最后一个参数的类型:unsigned short *array
GETVAL:获取信号灯集中,指定灯的值,最后一个参数不用填; 获取到的数据从返回值返回;
SETALL:设置信号灯集中,所有灯的值。第二个参数无意义,最后一个参数的类型:unsigned short *array
SETVAL:获取信号灯集中,指定灯的值,最后参数类型:int val
... :
union semun {
int val; /* Value for SETVAL */
struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */
unsigned short *array; /* Array for GETALL, SETALL */
struct seminfo *__buf; /* Buffer for IPC_INFO
(Linux-specific) */
};
if(semctl(semid, 0, IPC_RMID) < 0)
{
perror("semctl");
return -1;
}