一、pause函数的使用
该函数使调用进程进入无时限的睡眠状态
"pause"(2) //暂停/中止
#include <unistd.h>
int pause(void);
功能:等待信号
参数:void
返回值:
成功 - 不返回,阻塞信号,信号捕获后再返回
失败 - 返回-1,errno被设置
/* 使用alarm和pause实现sleep的功能,mysleep.c */
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
void handle(int signum) {
printf(" signum is %d\n", signum);
return ;
}
unsigned int mysleep(unsigned int seconds) {
alarm(seconds);
pause();
return 1;
}
int main(void) {
signal(SIGALRM, handle); //向进程注册信号
//alarm(1);
while(1) {
mysleep(2);//睡眠间隔时间2秒/次,每2秒打印两句
printf("hello,world!\n"); // '\n'必须加,否则不打印
#if 0
alarm(1);//设置闹钟时间为1秒
pause();//等待信号到来
#endif
}
return 0;
}
二、可重入函数
函数中所有变量的空间只能在栈里分配,这样的函数称为"可重入函数"
可以安全的调用自身(从信号处理中或从其他线程中)的函数,为了使函数可重入,函数"绝不能操作静态数据,只访问在栈里分配的数据和调用者提供的数据",同时也不得调用任何不可重入的函数。
参与"信号处理的函数必须是可重入函数"
"信号处理函数"和"正常的执行流程"是不同的两个执行路线。
/*举例验证可重入函数,代码 retest.c*/
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
void handle(int signum) { //该函数也不能malloc,内存也会共享
static int count = 0; //去掉staric,count在栈帧一直打印1
int value;
value = count;
count = value + 1; //count++
usleep(5000); //休眠微秒(秒-毫秒-微妙,1000倍)
printf("count = %d\n", count);
return ;
}
int main(void) {
signal(2, handle);
while(1) {
handle(3);
}
return 0;
}
补充:
子进程结束的时候会给父进程发送一个信号 SIGCHLD 。
父进程为子进程收尸的时候接收的就是此信号。
三、setitimer计时器
"setitimer"(2)
#include <sys/time.h>
//int getitimer(int which, struct itimerval *curr_value);
int setitimer(int which, const struct itimerval *new_value,
struct itimerval *old_value);
功能:设置一个间歇时间(计时器/定时器)
参数:
"which" 3选1
ITIMER_REAL -> SIGALRM 信号(倒计时,按时间减少真实时间)
ITIMER_VIRTUAL -> SIGVTALRM 信号(仅当遇到该信号时开始倒计时)
ITIMER_PROF -> SIGPROF 信号
"new_value" 结构体变量地址
"old_value" 结构体变量地址
struct itimerval {
struct timeval it_interval; /*next value重复间隔时间*/
struct timeval it_value; /*current value初始间隔时间(倒计时)*/
};
struct timeval {
long tv_sec; /*seconds秒*/ /** 秒-毫秒-微妙:各1000倍 **/
long tv_usec; /*microseconds微秒*/
};
返回值:
成功 - 返回 0
失败 - 返回 -1,errno被设置
/* 举例使用setitimer实现定时器,每隔1秒发送1次,timer.c */
#include <stdio.h>
#include <signal.h>
#include <sys/time.h>
void handle(int signum) {
printf(" pa...\n");
usleep(200000);
printf(" pa...\n");
usleep(200000);
printf(" pa...\n");
return ;
}
int main(void) {
struct itimerval new;
signal(SIGALRM, handle);
//初始化间隔时间的信息
new.it_value.tv_sec = 3; //初始间隔时间,倒计时3秒
new.it_value.tv_usec = 0;
new.it_interval.tv_sec = 2; //重复间隔时间,每2秒
new.it_interval.tv_usec = 0;
//设置闹钟
setitimer(ITIMER_REAL, &new, NULL);
while(1);
return 0;
}
信号小结:
1)什么是信号?可靠信号和不可靠信号
2)信号的产生
3)信号捕获
4)信号阻塞和信号未决
5)可重入函数
四、进程间通讯
可以"实现进程间通讯的方式":
管道(无名/有名)/环境变量/信号
"system v IPC" 进程通信包括以下三种: //IPC交互进程通信
1. 消息队列
2. 共享内存
3. 信号量集
为了实现进程之间的数据交换,系统内核会为参与通信的诸方维护一个内核对象(类似一个结构体变量),记录和通信有关的各种配置参数和运行时信息,叫"IPC对象"
系统中的每个IPC对象都有唯一的非负整数形式的标识符,所有与IPC相关的操作,都需要提供"IPC对象标识符",标识符是IPC对象的内部名,为了使多个合作进程能够在同一个IPC对象上会合,需要提供一个外部名方案,为此使用了"键"
"每一个IPC对象都与一个键相关联,1对1关系"
内核提供了一块/** 缓存 **/,用以维护进程间的通讯。
使用"ipcs"命令查看内核提供的这些缓存。
命令行输入"man ipcs" 或 "ipcs -h" 查看所有ipcs的子命令
在内存中为每一块内存都标记了一个id。
要想获取这个id,需要在/** 用户态 **/下有一个唯一的"键值"去对应。
键值:IPC对象唯一的标记
获取键值,使用ftok函数
"ftok"(3)
#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok(const char *pathname, int proj_id);
功能:将pathname和proj_id转换为system v IPC键值
参数:
"pathname" 有效的文件名
"proj_id" 取一个数字的低8位(0~255) //取21和取277(277-256)结果都是21
//多次调用ftok的时候,参数pathname和proj_id的组合一样,那么得到的键值也是一样的,必须保证键值唯一。
返回值:
成功 - 返回一个键值
失败 - 返回 -1,errno被设置
/* 举例验证,获取一个有效的键值,ftok.c */
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
int main(void) {
key_t key;
key = ftok(".", 21);//创建一个键值
if(-1 == key) {
perror("ftok");
return 1;
} else {
printf("ftok success...\n");
}
return 0;
}
"消息队列"
获取消息队列的ID,使用msgget(2)函数
"msgget"(2)
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgget(key_t key, int msgflg);
功能:获取一个消息队列的id
参数:
"key" 跟消息队列向关联的键值,ftok(3)的返回值
"msgflg"
IPC_CREAT 如果消息队列不存在,则创建并返回创建的消息队列id
如果存在,则返回该消息队列id
IPC_EXCL 如果消息队列不存在,则创建并返回创建的消息队列id
如果消息队列存在,masgget失败,errno被设置为EEXIST
可以指定消息队列的操作权限。做"或 |"操作,权限参见open(2)的mode。
返回值:
成功 - 返回消息队列的id
失败 - 返回 -1,errno被设置
/* 举例验证,根据键值获取消息队列的id,msgget.c */
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int main(void) {
key_t key;
key = ftok(".", 21);//获取键值
if(-1 == key) {
perror("ftok");
return 1;
}
//根据键值获取消息队列
int msgid = msgget(key, IPC_CREAT|0664);
if(-1 == msgid) {
perror("msgget");
return 2;
} else {
printf("msgid = %d\n", msgid);
}
return 0;
}
向消息队列中写数据,使用msgsnd函数
"msgsnd"(2) /** 写入消息 **/
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
功能:往消息队列里添加数据
参数:
"msqid" msgget(2)的返回值,就是队列的id
"msgp" 消息的结构体
"msgsz" 消息的长度,不包含消息的类型mtype,只有mtext的长度
"msgflg"
IPC_NOWAIT 非阻塞,消息队列没有空间的时候,立即返回
0 阻塞,消息队列没有空间的时候,阻塞等待
返回值:
成功 - 返回 0
失败 - 返回 -1,errno被设置
struct msgbuf {
long mtype; /*message type, must be > 0 */
char mtext[1]; /*message data该数组可变长,该字节往后都是数据*/
}; "使用此结构体之前要先声明该结构体(数组变长),并定义结构体变量"
/* 举例验证,将字符串hello,world放入消息队列当中 processA.c*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
int main(void) {
key_t key;
key = ftok(".", 21);//创建键值
if(-1 == key) {
perror("ftok");
return 1;
}
int msqid = msgget(key, IPC_CREAT|0664);//获取消息队列id
if(-1 == msqid) {
perror("msgget");
return 2;
}
struct msgbuf {
long mtype;
char str[20]; /** 定义包含可变长数组的结构体 **/
}msg;
msg.mtype = 3; //只要大于0即可,读取消息队列数据时此数字需一致
strcpy(msg.str, "hello,world!");
//向消息队列里写数据
msgsnd(msqid, &msg, strlen(msg.str)+1, 0);
return 0;
}
"msgrcv"(2) /** 移除消息 **/
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, \
int msgflg);
功能:从msqid消息队列中移除一条消息
参数:
"msqid" msgget(2)的返回值,消息队列的id
"msgp" 指定一块内存空间,将消息存放到这块空间里
"msgsz" metxt的最大尺寸
"msgtyp" 消息的类型(需要与添加消息的时候指定的数字一致)
"msgflg"
IPC_NOWAIT 消息队列中没有消息,立即返回
0 消息队列中没有消息,等待
返回值:
成功 - 返回从消息队列中获取的消息的字节数
失败 - 返回 -1,errno被设置
/* 举例验证,从消息队列中获取消息,将消息输出到屏幕 processB.c*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
int main(void) {
key_t key;
key = ftok(".", 21);//获取键值
if(-1 == key) {
perror("ftok");
return 1;
}
//获取消息队列的id
int msqid = msgget(key, IPC_CREAT|0664);
if(-1 == msqid) {
perror("msgget");
return 2;
}
struct msgbuf { //定义接收消息的结构体类型和变量msg
long mtype;
char str[50];
}msg;
//从消息队列里面获取消息
int bytes = msgrcv(msqid, &msg, sizeof(msg.str), 3, 0);
printf("%s\n", msg.str); /** 消息获取后,会从消息队列移除 **/
return 0;
}
"共享内存"
由内核维护的一块内存,将这块内存映射到不同进程的虚拟地址空间里,在不同的进程里操作这块内存,起到进程间通讯的作用。
"shmget"(2)
#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, size_t size, int shmflg);
功能:分配一块共享内存区域
参数:
"key" 根据key的值获取共享内存的id,ftok(3)的返回值
"size" 需要的共享内存的大小/尺寸
"shmflg"
IPC_CREAT 如果存在共享内存,则返回;不存在就创建
返回值:
成功 - 返回共享内存的id
失败 - 返回 -1
/* 举例创建自己的内核共享内存,获取共享的内存的id shmget.c*/
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
int main(void) {
key_t key;
key = ftok(".", 22);//获取键值
if(-1 == key) {
perror("ftok");
return 1;
}
//根据键值获取共享内存的id
int shmid = shmget(key, 1024, IPC_CREAT|0664);
if(-1 == shmid) {
printf("shmget error...\n");
return 2;
} else {
printf("shared memory success...\n");
printf("shmid = %d\n", shmid);
}
return 0;
}
将"内核"维护的共享内存,"映射"到进程("用户")的虚拟地址空间里:
"shmat"(2) /** 映射 **/
#include <sys/types.h>
#include <sys/shm.h>
void *shmat(int shmid, const void *shmaddr, int shmflg);
功能:将共享内存映射到虚拟地址空间里
参数:
"shmid" 共享内存的id,shmget(2)的返回值
"shmaddr" NULL 代表系统自动分配虚拟空间
"shmflg"
SHM_RDONLY 只读(共享内存权限为只读)
0 可读可写
返回值:
成功 - 返回跟共享内存相关的虚拟地址(用户空间)
失败 - 返回(void *)-1,errno被设置
"shmdt"(2) /** 解除映射 **/
#include <sys/types.h>
#include <sys/shm.h>
int shmdt(const void *shmaddr);
功能:解除shmat的映射
参数:"shmaddr" shmat(2)的返回值
返回值:
成功 - 返回 0
失败 - 返回 -1,errno被设置
/* 使用共享内存实现两个进程间的通信 shmA.c shmB.c */
/** A **/
#include <stdio.h>
#include <sys/types.h>
#include <sys/shm.h>
#include <sys/ipc.h>
#include <string.h>
int main(void) {
key_t key;
key = ftok(".", 22);//获取键值
if(-1 == key) {
perror("ftok");
return 1;
}
//根据键值获取跟键值相关的共享内存区域
int shmid = shmget(key, 1024, IPC_CREAT|0664);
if(-1 == shmid) {
perror("shmget");
return 2;
} else {
void *p = shmat(shmid, NULL, 0);//将共享内存映射到虚拟地址
if((void *)-1 == p) {
perror("shmat");
return 3;
} else {
printf("shmat success...\n");
printf("p address is %p\n", p);
strcpy(p, "hello,world!");
printf("A - p is %s\n", (char *)p);
//解除映射
shmdt(p);
}
}
return 0;
}
/** B **/
#include <stdio.h>
#include <sys/types.h>
#include <sys/shm.h>
#include <sys/ipc.h>
#include <string.h>
int main(void) {
key_t key = ftok(".", 22);
if(-1 == key) {
perror("ftok");
return 1;
}
int shmid = shmget(key, 1024, IPC_CREAT|0664);
if(-1 == shmid) {
perror("shmget");
return 2;
} else {
void *p = shmat(shmid, NULL, 0);
if((void *)-1 == p) {
perror("shmat");
return 3;
} else {
printf("B - p is %s\n", (char *)p);
shmdt(p);
}
}
return 0;
}
该函数使调用进程进入无时限的睡眠状态
"pause"(2) //暂停/中止
#include <unistd.h>
int pause(void);
功能:等待信号
参数:void
返回值:
成功 - 不返回,阻塞信号,信号捕获后再返回
失败 - 返回-1,errno被设置
/* 使用alarm和pause实现sleep的功能,mysleep.c */
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
void handle(int signum) {
printf(" signum is %d\n", signum);
return ;
}
unsigned int mysleep(unsigned int seconds) {
alarm(seconds);
pause();
return 1;
}
int main(void) {
signal(SIGALRM, handle); //向进程注册信号
//alarm(1);
while(1) {
mysleep(2);//睡眠间隔时间2秒/次,每2秒打印两句
printf("hello,world!\n"); // '\n'必须加,否则不打印
#if 0
alarm(1);//设置闹钟时间为1秒
pause();//等待信号到来
#endif
}
return 0;
}
二、可重入函数
函数中所有变量的空间只能在栈里分配,这样的函数称为"可重入函数"
可以安全的调用自身(从信号处理中或从其他线程中)的函数,为了使函数可重入,函数"绝不能操作静态数据,只访问在栈里分配的数据和调用者提供的数据",同时也不得调用任何不可重入的函数。
参与"信号处理的函数必须是可重入函数"
"信号处理函数"和"正常的执行流程"是不同的两个执行路线。
/*举例验证可重入函数,代码 retest.c*/
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
void handle(int signum) { //该函数也不能malloc,内存也会共享
static int count = 0; //去掉staric,count在栈帧一直打印1
int value;
value = count;
count = value + 1; //count++
usleep(5000); //休眠微秒(秒-毫秒-微妙,1000倍)
printf("count = %d\n", count);
return ;
}
int main(void) {
signal(2, handle);
while(1) {
handle(3);
}
return 0;
}
补充:
子进程结束的时候会给父进程发送一个信号 SIGCHLD 。
父进程为子进程收尸的时候接收的就是此信号。
三、setitimer计时器
"setitimer"(2)
#include <sys/time.h>
//int getitimer(int which, struct itimerval *curr_value);
int setitimer(int which, const struct itimerval *new_value,
struct itimerval *old_value);
功能:设置一个间歇时间(计时器/定时器)
参数:
"which" 3选1
ITIMER_REAL -> SIGALRM 信号(倒计时,按时间减少真实时间)
ITIMER_VIRTUAL -> SIGVTALRM 信号(仅当遇到该信号时开始倒计时)
ITIMER_PROF -> SIGPROF 信号
"new_value" 结构体变量地址
"old_value" 结构体变量地址
struct itimerval {
struct timeval it_interval; /*next value重复间隔时间*/
struct timeval it_value; /*current value初始间隔时间(倒计时)*/
};
struct timeval {
long tv_sec; /*seconds秒*/ /** 秒-毫秒-微妙:各1000倍 **/
long tv_usec; /*microseconds微秒*/
};
返回值:
成功 - 返回 0
失败 - 返回 -1,errno被设置
/* 举例使用setitimer实现定时器,每隔1秒发送1次,timer.c */
#include <stdio.h>
#include <signal.h>
#include <sys/time.h>
void handle(int signum) {
printf(" pa...\n");
usleep(200000);
printf(" pa...\n");
usleep(200000);
printf(" pa...\n");
return ;
}
int main(void) {
struct itimerval new;
signal(SIGALRM, handle);
//初始化间隔时间的信息
new.it_value.tv_sec = 3; //初始间隔时间,倒计时3秒
new.it_value.tv_usec = 0;
new.it_interval.tv_sec = 2; //重复间隔时间,每2秒
new.it_interval.tv_usec = 0;
//设置闹钟
setitimer(ITIMER_REAL, &new, NULL);
while(1);
return 0;
}
信号小结:
1)什么是信号?可靠信号和不可靠信号
2)信号的产生
3)信号捕获
4)信号阻塞和信号未决
5)可重入函数
四、进程间通讯
可以"实现进程间通讯的方式":
管道(无名/有名)/环境变量/信号
"system v IPC" 进程通信包括以下三种: //IPC交互进程通信
1. 消息队列
2. 共享内存
3. 信号量集
为了实现进程之间的数据交换,系统内核会为参与通信的诸方维护一个内核对象(类似一个结构体变量),记录和通信有关的各种配置参数和运行时信息,叫"IPC对象"
系统中的每个IPC对象都有唯一的非负整数形式的标识符,所有与IPC相关的操作,都需要提供"IPC对象标识符",标识符是IPC对象的内部名,为了使多个合作进程能够在同一个IPC对象上会合,需要提供一个外部名方案,为此使用了"键"
"每一个IPC对象都与一个键相关联,1对1关系"
内核提供了一块/** 缓存 **/,用以维护进程间的通讯。
使用"ipcs"命令查看内核提供的这些缓存。
命令行输入"man ipcs" 或 "ipcs -h" 查看所有ipcs的子命令
在内存中为每一块内存都标记了一个id。
要想获取这个id,需要在/** 用户态 **/下有一个唯一的"键值"去对应。
键值:IPC对象唯一的标记
获取键值,使用ftok函数
"ftok"(3)
#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok(const char *pathname, int proj_id);
功能:将pathname和proj_id转换为system v IPC键值
参数:
"pathname" 有效的文件名
"proj_id" 取一个数字的低8位(0~255) //取21和取277(277-256)结果都是21
//多次调用ftok的时候,参数pathname和proj_id的组合一样,那么得到的键值也是一样的,必须保证键值唯一。
返回值:
成功 - 返回一个键值
失败 - 返回 -1,errno被设置
/* 举例验证,获取一个有效的键值,ftok.c */
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
int main(void) {
key_t key;
key = ftok(".", 21);//创建一个键值
if(-1 == key) {
perror("ftok");
return 1;
} else {
printf("ftok success...\n");
}
return 0;
}
"消息队列"
获取消息队列的ID,使用msgget(2)函数
"msgget"(2)
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgget(key_t key, int msgflg);
功能:获取一个消息队列的id
参数:
"key" 跟消息队列向关联的键值,ftok(3)的返回值
"msgflg"
IPC_CREAT 如果消息队列不存在,则创建并返回创建的消息队列id
如果存在,则返回该消息队列id
IPC_EXCL 如果消息队列不存在,则创建并返回创建的消息队列id
如果消息队列存在,masgget失败,errno被设置为EEXIST
可以指定消息队列的操作权限。做"或 |"操作,权限参见open(2)的mode。
返回值:
成功 - 返回消息队列的id
失败 - 返回 -1,errno被设置
/* 举例验证,根据键值获取消息队列的id,msgget.c */
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int main(void) {
key_t key;
key = ftok(".", 21);//获取键值
if(-1 == key) {
perror("ftok");
return 1;
}
//根据键值获取消息队列
int msgid = msgget(key, IPC_CREAT|0664);
if(-1 == msgid) {
perror("msgget");
return 2;
} else {
printf("msgid = %d\n", msgid);
}
return 0;
}
向消息队列中写数据,使用msgsnd函数
"msgsnd"(2) /** 写入消息 **/
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
功能:往消息队列里添加数据
参数:
"msqid" msgget(2)的返回值,就是队列的id
"msgp" 消息的结构体
"msgsz" 消息的长度,不包含消息的类型mtype,只有mtext的长度
"msgflg"
IPC_NOWAIT 非阻塞,消息队列没有空间的时候,立即返回
0 阻塞,消息队列没有空间的时候,阻塞等待
返回值:
成功 - 返回 0
失败 - 返回 -1,errno被设置
struct msgbuf {
long mtype; /*message type, must be > 0 */
char mtext[1]; /*message data该数组可变长,该字节往后都是数据*/
}; "使用此结构体之前要先声明该结构体(数组变长),并定义结构体变量"
/* 举例验证,将字符串hello,world放入消息队列当中 processA.c*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
int main(void) {
key_t key;
key = ftok(".", 21);//创建键值
if(-1 == key) {
perror("ftok");
return 1;
}
int msqid = msgget(key, IPC_CREAT|0664);//获取消息队列id
if(-1 == msqid) {
perror("msgget");
return 2;
}
struct msgbuf {
long mtype;
char str[20]; /** 定义包含可变长数组的结构体 **/
}msg;
msg.mtype = 3; //只要大于0即可,读取消息队列数据时此数字需一致
strcpy(msg.str, "hello,world!");
//向消息队列里写数据
msgsnd(msqid, &msg, strlen(msg.str)+1, 0);
return 0;
}
"msgrcv"(2) /** 移除消息 **/
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, \
int msgflg);
功能:从msqid消息队列中移除一条消息
参数:
"msqid" msgget(2)的返回值,消息队列的id
"msgp" 指定一块内存空间,将消息存放到这块空间里
"msgsz" metxt的最大尺寸
"msgtyp" 消息的类型(需要与添加消息的时候指定的数字一致)
"msgflg"
IPC_NOWAIT 消息队列中没有消息,立即返回
0 消息队列中没有消息,等待
返回值:
成功 - 返回从消息队列中获取的消息的字节数
失败 - 返回 -1,errno被设置
/* 举例验证,从消息队列中获取消息,将消息输出到屏幕 processB.c*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
int main(void) {
key_t key;
key = ftok(".", 21);//获取键值
if(-1 == key) {
perror("ftok");
return 1;
}
//获取消息队列的id
int msqid = msgget(key, IPC_CREAT|0664);
if(-1 == msqid) {
perror("msgget");
return 2;
}
struct msgbuf { //定义接收消息的结构体类型和变量msg
long mtype;
char str[50];
}msg;
//从消息队列里面获取消息
int bytes = msgrcv(msqid, &msg, sizeof(msg.str), 3, 0);
printf("%s\n", msg.str); /** 消息获取后,会从消息队列移除 **/
return 0;
}
"共享内存"
由内核维护的一块内存,将这块内存映射到不同进程的虚拟地址空间里,在不同的进程里操作这块内存,起到进程间通讯的作用。
"shmget"(2)
#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, size_t size, int shmflg);
功能:分配一块共享内存区域
参数:
"key" 根据key的值获取共享内存的id,ftok(3)的返回值
"size" 需要的共享内存的大小/尺寸
"shmflg"
IPC_CREAT 如果存在共享内存,则返回;不存在就创建
返回值:
成功 - 返回共享内存的id
失败 - 返回 -1
/* 举例创建自己的内核共享内存,获取共享的内存的id shmget.c*/
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
int main(void) {
key_t key;
key = ftok(".", 22);//获取键值
if(-1 == key) {
perror("ftok");
return 1;
}
//根据键值获取共享内存的id
int shmid = shmget(key, 1024, IPC_CREAT|0664);
if(-1 == shmid) {
printf("shmget error...\n");
return 2;
} else {
printf("shared memory success...\n");
printf("shmid = %d\n", shmid);
}
return 0;
}
将"内核"维护的共享内存,"映射"到进程("用户")的虚拟地址空间里:
"shmat"(2) /** 映射 **/
#include <sys/types.h>
#include <sys/shm.h>
void *shmat(int shmid, const void *shmaddr, int shmflg);
功能:将共享内存映射到虚拟地址空间里
参数:
"shmid" 共享内存的id,shmget(2)的返回值
"shmaddr" NULL 代表系统自动分配虚拟空间
"shmflg"
SHM_RDONLY 只读(共享内存权限为只读)
0 可读可写
返回值:
成功 - 返回跟共享内存相关的虚拟地址(用户空间)
失败 - 返回(void *)-1,errno被设置
"shmdt"(2) /** 解除映射 **/
#include <sys/types.h>
#include <sys/shm.h>
int shmdt(const void *shmaddr);
功能:解除shmat的映射
参数:"shmaddr" shmat(2)的返回值
返回值:
成功 - 返回 0
失败 - 返回 -1,errno被设置
/* 使用共享内存实现两个进程间的通信 shmA.c shmB.c */
/** A **/
#include <stdio.h>
#include <sys/types.h>
#include <sys/shm.h>
#include <sys/ipc.h>
#include <string.h>
int main(void) {
key_t key;
key = ftok(".", 22);//获取键值
if(-1 == key) {
perror("ftok");
return 1;
}
//根据键值获取跟键值相关的共享内存区域
int shmid = shmget(key, 1024, IPC_CREAT|0664);
if(-1 == shmid) {
perror("shmget");
return 2;
} else {
void *p = shmat(shmid, NULL, 0);//将共享内存映射到虚拟地址
if((void *)-1 == p) {
perror("shmat");
return 3;
} else {
printf("shmat success...\n");
printf("p address is %p\n", p);
strcpy(p, "hello,world!");
printf("A - p is %s\n", (char *)p);
//解除映射
shmdt(p);
}
}
return 0;
}
/** B **/
#include <stdio.h>
#include <sys/types.h>
#include <sys/shm.h>
#include <sys/ipc.h>
#include <string.h>
int main(void) {
key_t key = ftok(".", 22);
if(-1 == key) {
perror("ftok");
return 1;
}
int shmid = shmget(key, 1024, IPC_CREAT|0664);
if(-1 == shmid) {
perror("shmget");
return 2;
} else {
void *p = shmat(shmid, NULL, 0);
if((void *)-1 == p) {
perror("shmat");
return 3;
} else {
printf("B - p is %s\n", (char *)p);
shmdt(p);
}
}
return 0;
}