目录
Linux -> unix
GNU GNU's Not Unix
SYS V
BSD
一.Linux进程间通信的方式:
管道、信号
消息队列、共享内存、信号灯
套接字
1.管道:
1.无名管道
只能用于具有亲缘关系的进程间通信(父子进程)
无名管道创建后,进程会获得操作管道的两个文件描述符,创建子进程时,子进程会拷贝得到父进程的操作无名管道的两个文件描述符,
两个进程任务操作同一管道实现通信。
int pipe(int pipefd[2]);
功能:
创建一个用来通信的管道
参数:
pipefd:存放管道两端(读写)的文件描述符数组
pipefd[0]:读
pipefd[1]:写
成功返回0
失败返回-1
1.如果管道中至少有一个写端:
如果管道中有数据,直接读出
如果管道中没有数据,会阻塞等待直到有数据写入后读出
2.如果管道中没有写端:
如果管道中有数据,直接读出
如果管道中没有数据,不会阻塞直接继续向下执行
3.如果管道中至少有一个读端:
如果管道没有写满,直接写入
如果管道中写满(64k),则阻塞等待,等有数据读出才能继续写入
4.如果管道中没有读端:
向管道中写入数据会产生管道破裂信号
管道是一段内核缓存区
write,read已经实现同步,read会一直阻塞等待写入。
#include "../head.h"
char tmpbuff[4096] = {0};
int main(void)
{
pid_t pid;
int pipefd[2];
ssize_t nsize = 0;
pipe(pipefd);
pid = fork();
if (-1 == pid)
{
perror("fail to fork");
return -1;
}
if (0 == pid)
{
close(pipefd[0]);
#if 0
strcpy(tmpbuff, "hello world");
write(pipefd[1], tmpbuff, strlen(tmpbuff));
read(pipefd[0], tmpbuff, sizeof(tmpbuff));
#endif
while (1)
{
}
}
else if (pid > 0)
{
int cnt = 0;
sleep(2);
close(pipefd[0]);
while (1)
{
printf("write前!\n");
write(pipefd[1], tmpbuff, sizeof(tmpbuff));
printf("write后!\n");
cnt++;
printf("cnt = %d\n", cnt);
}
#if 0
nsize = read(pipefd[0], tmpbuff, sizeof(tmpbuff));
printf("读到 %ld 个字节,内容为: %s\n", nsize, tmpbuff);
#endif
}
return 0;
}
2.有名管道
通过管道在文件系统中的路径找到管道名,两个进程以读写的方式打开同一管道完成通信
mkfifo:
int mkfifo(const char *pathname, mode_t mode);
功能:
创建一个管道
参数:
pathname:管道文件的路径
mode:权限
返回值:
成功返回0
失败返回-1
有名管道必须读写两端同时加入后,才能继续向下执行
写:
#include "../head.h"
extern int errno;
int main(void)
{
int ret = 0;
int fd = 0;
ret = mkfifo("/tmp/myfifo", 0664);
if (-1 == ret && errno != EEXIST)
{
perror("fail to mkfifo");
return -1;
}
fd = open("/tmp/myfifo", O_WRONLY);
if (-1 == fd)
{
perror("fail to open");
return -1;
}
printf("打开管道成功!\n");
write(fd, "hello world", 11);
close(fd);
return 0;
}
读:
#include "../head.h"
extern int errno;
int main(void)
{
int ret = 0;
int fd = 0;
ssize_t nsize = 0;
char tmpbuff[4096] = {0};
ret = mkfifo("/tmp/myfifo", 0664);
if (-1 == ret && errno != EEXIST)
{
perror("fail to mkfifo");
return -1;
}
fd = open("/tmp/myfifo", O_RDONLY);
if (-1 == fd)
{
perror("fail to open");
return -1;
}
printf("打开管道成功!\n");
nsize = read(fd, tmpbuff, sizeof(tmpbuff));
printf("读取到 %ld 个字节, 内容: %s\n", nsize, tmpbuff);
close(fd);
return 0;
}
2.安装mplayer软件:
1.让Ubuntu可以上网
ping www.baidu.com
2.配置apt-get工具集的源
3.安装mplayer
sudo apt-get install mplayer
4.将视频拷贝到虚拟机中
mplayer 视频文件名
5.播放视频
mplayer -slave -input file=/tmp/myfifo 视频路径
#include "../head.h"
extern int errno;
int main(void)
{
int ret = 0;
int fd = 0;
ret = mkfifo("/tmp/myfifo", 0664);
if (-1 == ret && errno != EEXIST)
{
perror("fail to mkfifo");
return -1;
}
fd = open("/tmp/myfifo", O_RDWR);
if (-1 == fd)
{
perror("fail to open");
return -1;
}
write(fd, "pause\n", 6);
close(fd);
return 0;
}
4.编写两个进程任务clienta.c和clientb.c, clienta.c从终端接收一行字符串,通过管道发送给clientb.c
clientb.c接收到内容后打印,并从终端接收一行字符串,发回给clienta.c
循环此过程实现两个进程聊天的功能
1.管道:
进程间通信最简单的形式
2.信号:
内核层和用户层通信的一种方式
1.信号类型:
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
可以从终端输入:
SIGINT ctrl+c
SIGQUIT ctrl+\
SIGTSTP ctrl+z
9号和19号信号
SIGKILL 进程结束
SIGSTOP 进程挂起
SIGSEGV:表示内核操作内存错误
SIGUSR1:用户可以使用该信号实现通信
SIGCHLD:表示该进程有一个子进程结束了
SIGPIPE:表示管道破裂
SIGIO:表示IO事件发生
2.信号的处理方式:
1.缺省:
按照默认的方式处理信号
2.忽略:
不处理信号
3.捕捉:
按照指定的方式处理信号
9号SIGKILL和19号信号SIGSTOP不能被忽略和捕捉
3.函数接口:
1.signal
typedef void (*sighandler_t)(int);//类型别名,把typedef void (*)(int)定义为sighandler_t
sighandler_t signal(int signum, sighandler_t handler);
这一行等价于void (*signal(int signum, void(* handler)(int))(int)
void(* )(int)是函数返回的类型,signal是函数的名字,signum是第一个形参,类型为int型,第二个参数handler的类型为void(*)(int)型。这里的handler是一个函数的名字,这里handler是一个函数指针,也就是一个函数的名字所以,只需要传入一个函数的名字即可。
定义一个void (*pret)(int)类型来接函数的返回值,
功能:
切换信号的处理方式
参数:
signum:信号的编号
handler:信号对应的处理方式
SIG_IGN 忽略信号
SIG_DFL 缺省信号处理方式
自定义的函数的函数名(指向函数的指针)
返回值:
成功返回之前注册的信号的处理方式
失败返回SIG_ERR
#include "../head.h"
void handler(int signo)//signo是传入的信号的id,这里是系统内核自动获得的,无需手动写入
{
printf("电话来了,休息一下!\n");
return;
}
void handler2(int signo)
{
printf("外卖来了, 快开门\n");
return;
}
int main(void)
{
void (*pret)(int) = NULL;
pret = signal(SIGINT, handler2);
if (SIG_ERR == pret)
{
perror("fail to signal");
return -1;
}
printf("SIGINT信号处理方式切换为:捕捉\n");
pret = signal(SIGQUIT, handler);
if (SIG_ERR == pret)
{
perror("fail to signal");
return -1;
}
printf("SIGQUIT信号处理方式切换为:捕捉\n");
while (1)
{
printf("正在工作!\n");
sleep(1);
}
return 0;
}
#include "../head.h"
void handler(int signo)
{
if (SIGINT == signo)
{
printf("外卖来了,快开门!\n");
}
else if (SIGQUIT == signo)
{
printf("电话来了,休息一下!\n");
}
return;
}
int main(void)
{
signal(SIGINT, handler);
signal(SIGQUIT, handler);
while (1)
{
printf("正在工作\n");
sleep(1);
}
return 0;
}
4.定时器:
alarm
unsigned int alarm(unsigned int seconds);
功能:
间隔seconds秒后给进程发送SIGALRM信号
参数:
seconds:间隔秒数
返回值:
成功返回上次定时剩余的秒数
失败返回-1
#include "../head.h"
void ontimer_handler(int signo)
{
printf("刷新一次界面!\n");
alarm(5);
return;
}
int main(void)
{
signal(SIGALRM, ontimer_handler);
alarm(5);
while (1)
{
printf("正在工作!\n");
sleep(1);
}
return 0;
}
5.kill
int kill(pid_t pid, int sig);
功能:
向进程发送信号
参数:
pid:进程id
sig:信号值
返回值:
成功返回0
失败返回-1
#include "../head.h"
pid_t pid;
void handler_child(int signo)
{
if (SIGUSR1 == signo)
{
printf("好的,快去做饭!\n");
}
else if (SIGQUIT == signo)
{
printf("爸,我回来了!\n");
kill(getppid(), SIGUSR2);
}
return;
}
void handler_parent(int signo)
{
if (SIGINT == signo)
{
printf("儿子,我回来了!\n");
kill(pid, SIGUSR1);
}
else if (SIGUSR2 == signo)
{
printf("今天肯定进步了吧!\n");
}
return;
}
int main(void)
{
pid = fork();
if (-1 == pid)
{
perror("fail to fork");
return -1;
}
if (0 == pid)
{
signal(SIGUSR1, handler_child);
signal(SIGINT, SIG_IGN);
signal(SIGQUIT, handler_child);
}
else if (pid > 0)
{
signal(SIGINT, handler_parent);
signal(SIGQUIT, SIG_IGN);
signal(SIGUSR2, handler_parent);
}
while (1)
{
}
return 0;
}
管道、信号 Unix
1.消息队列、共享内存、信号灯 SYS V
IPC对象:
内存文件
1.ipcs
查看所有ipc对象的信息
2.ipcrm
ipcrm -Q/M/S Key
ipcrm -q/m/s 消息队列ID/共享内存ID/信号灯ID
3.消息队列:
1.步骤:
1.创建IPC对象名称 ftok
//1.生成key值
key = ftok("/", 'a');
if (-1 == key)
{
return -1;
}
2.创建消息队列 msgget
//2.创建消息队列
msgid = msgget(key, IPC_CREAT | 0664);
if (-1 == msgid)
{
return -1;
}
printf("msgid = %d\n", msgid);
3.发送消息 msgsnd
//3.发送消息
sendmsg.mtype = 100;
strcpy(sendmsg.mtext, "hello world");
ret = msgsnd(msgid, &sendmsg, sizeof(sendmsg) - sizeof(long), 0);
if (-1 == ret)
{
return -1;
}
printf("发送成功!\n");
sendmsg.mtype = 200;
strcpy(sendmsg.mtext, "how are you");
ret = msgsnd(msgid, &sendmsg, sizeof(sendmsg) - sizeof(long), 0);
if (-1 == ret)
{
return -1;
}
printf("发送成功!\n");
4.接收消息 msgrcv
nsize = msgrcv(msgid, &recvmsg, sizeof(recvmsg.mtext), 100, 0);
if (-1 == nsize)
{
return -1;
}
printf("接收到 %ld 字节,内容为: %s\n", nsize, recvmsg.mtext);
5.消息队列销毁 msgctl
msgctl(msgid, IPC_RMID, NULL);
2.函数接口:
1.ftok
key_t ftok(const char *pathname, int proj_id);
功能:
创建一个IPC对象名称
参数:
pathname:文件的路径
proj_id:项目ID(8bits)
返回值:
成功返回IPC对象名称
失败返回-1
2.msgget
int msgget(key_t key, int msgflg);
功能:
创建一个消息队列
参数:
key:IPC对象名称
msgflg:消息队列属性
IPC_CREAT:创建一个消息队列
IPC_EXCL: 如果消息队列存在就报错
返回值:
成功返回消息队列ID
失败返回 -1
3.msgsnd
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
功能:
向消息队列中发送消息
参数:
msqid:消息队列的ID号
msgp:发送消息的内容
msgsz:发送消息的大小
msgflg:消息属性 默认为0
返回值:
成功返回0
失败返回-1
struct msgbuf {
long mtype; /* message type, must be > 0 */
char mtext[1]; /* message data */
};
4.msgrcv
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
功能:
从消息队列中接收消息
参数:
msqid:消息队列的ID号
msgp:存放消息的空间首地址
msgsz:最多接收消息的大小
msgtyp:接收消息的类型
msgflg:消息属性 默认为0
返回值:
成功返回接收到数据的字节数
失败返回-1
5.msgctl
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
功能:
向消息队列发送命令
参数:
msqid:消息队列的ID号
cmd:命令
IPC_STAT:获取消息队列的信息
返回值:
成功返回0
失败返回-1
#include "../head.h"
typedef struct msg
{
long mtype; //发送消息的类型
char mtext[256]; //发送的消息
}message_t;
int main(void)
{
key_t key;
int msgid = -1;
int ret = 0;
message_t sendmsg;
message_t recvmsg;
ssize_t nsize = 0;
//1.生成key值
key = ftok("/", 'a');
if (-1 == key)
{
return -1;
}
printf("%#x\n", key);
//2.创建消息队列
msgid = msgget(key, IPC_CREAT | 0664);
if (-1 == msgid)
{
return -1;
}
printf("msgid = %d\n", msgid);
//3.发送消息
sendmsg.mtype = 100;
strcpy(sendmsg.mtext, "hello world");
ret = msgsnd(msgid, &sendmsg, sizeof(sendmsg) - sizeof(long), 0);
if (-1 == ret)
{
return -1;
}
printf("发送成功!\n");
sendmsg.mtype = 200;
strcpy(sendmsg.mtext, "how are you");
ret = msgsnd(msgid, &sendmsg, sizeof(sendmsg) - sizeof(long), 0);
if (-1 == ret)
{
return -1;
}
printf("发送成功!\n");
nsize = msgrcv(msgid, &recvmsg, sizeof(recvmsg.mtext), 100, 0);
if (-1 == nsize)
{
return -1;
}
printf("接收到 %ld 字节,内容为: %s\n", nsize, recvmsg.mtext);
msgctl(msgid, IPC_RMID, NULL);
return 0;
}
练习:
1.编写两个进程,实现利用消息队列的通信
send.c 创建消息队列 -> 发送消息
recv.c 创建消息队列 -> 接收消息 -> 销毁消息队列
#include "../head.h"
int main(void)
{
key_t key;
int msgid;
int ret = 0;
message_t sendmsg;
//1.创建IPC对象 Key 值
key = ftok("/", 'b');
if (-1 == key)
{
perror("fail to ftok");
return -1;
}
//2.创建消息队列
msgid = msgget(key, IPC_CREAT | 0664);
if (-1 == msgid)
{
perror("fail to shmget");
return -1;
}
sendmsg.mtype = 100;
fgets(sendmsg.mtext, sizeof(sendmsg.mtext), stdin);
sendmsg.mtext[strlen(sendmsg.mtext)-1] = '\0';
//3.发送消息
ret = msgsnd(msgid, &sendmsg, sizeof(sendmsg.mtext), 0);
if (-1 == ret)
{
perror("fail to msgsnd");
return -1;
}
return 0;
}
#include "../head.h"
int main(void)
{
key_t key;
int msgid;
ssize_t nsize = 0;
message_t recvmsg;
//1.创建IPC对象 Key 值
key = ftok("/", 'b');
if (-1 == key)
{
perror("fail to ftok");
return -1;
}
//2.创建消息队列
msgid = msgget(key, IPC_CREAT | 0664);
if (-1 == msgid)
{
perror("fail to shmget");
return -1;
}
//3.接收消息
nsize = msgrcv(msgid, &recvmsg, sizeof(recvmsg.mtext), 100, 0);
if (-1 == nsize)
{
perror("fail to msgrcv");
return -1;
}
//4.打印消息
printf("RECV:%s\n", recvmsg.mtext);
//5.删除消息队列
msgctl(msgid, IPC_RMID, NULL);
return 0;
}
4.共享内存:
进程间通信最高效的方法
1.操作步骤:
1.创建 key 值
//1.创建 key 值
key_t key;
int shmid = 0;
int ret = 0;
char *pshmaddr = NULL;
key = ftok("/", 'c');
if (-1 == key)
{
perror("fail to ftok");
return -1;
}
2.创建共享内存
//2.创建共享内存
shmid = shmget(key, 4096, IPC_CREAT | 0664);
if (-1 == shmid)
{
perror("fail to shmget");
return -1;
}
3.映射共享内存
//3.映射到共享内存中
pshmaddr = shmat(shmid, NULL, 0);
if (NULL == pshmaddr)
{
perror("fail to shmat");
return -1;
}
fgets(pshmaddr, 4096, stdin);
pshmaddr[strlen(pshmaddr)-1] = '\0';
printf("共享空间: %s\n", pshmaddr);
4.解除映射
//4.解除映射
ret = shmdt(pshmaddr);
if (-1 == ret)
{
perror("fail to shmdt");
return -1;
}
5.销毁共享内存
//5.销毁共享空间
shmctl(shmid, IPC_RMID, NULL);
2.函数接口:
1.ftok
2.shmget
int shmget(key_t key, size_t size, int shmflg);
功能:
创建一个共享内存
参数:
key:IPC对象名称
size:共享内存的大小
shmflg:
IPC_CREAT 创建
IPC_EXCL 如果存在就报错
返回值:
成功返回共享内存ID号
失败返回-1
3.shmat
void *shmat(int shmid, const void *shmaddr, int shmflg);
功能:
将地址映射到共享内存中
参数:
shmid:共享内存ID号
shmaddr:
NULL: 让系统选择一个合适的地址映射到共享内存中
shmflg:
属性,默认为0
返回值:
成功返回映射到共享空间的地址
失败返回NULL
4.shmdt
int shmdt(const void *shmaddr);
功能:
解除映射空间
参数:
shmaddr:映射到共享内存中的地址
返回值:
成功返回0
失败返回-1
5.shmctl
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
功能:
向共享内存发送命令
参数:
shmid:共享内存ID号
cmd:命令
IPC_RMID 删除
返回值:
成功返回0
失败返回-1
练习:
1.编写两个进程,实现利用共享内存的通信
write.c 创建共享内存 -> 映射 -> 从终端接收数据写入共享内存中
read.c 创建共享内存 -> 映射 -> 从共享内存中读取数据 -> 显示到终端
#include "../head.h"
int main(void)
{
int ret = 0;
int shmid = 0;
char *pshmaddr = NULL;
//1.创建 key 值
key_t key;
key = ftok("/", 'a');
if (-1 == key)
{
perror("fail to ftok");
return -1;
}
//2.创建共享内存
shmid = shmget(key, 4096, IPC_CREAT | 0664);
if (-1 == shmid)
{
perror("fail to shmget");
return -1;
}
//3.映射共享内存空间
pshmaddr = shmat(shmid, NULL, 0);
if (NULL == pshmaddr)
{
perror("fail to shmat");
return -1;
}
//4.从终端接收写入到共享内存中
while (1)
{
gets(pshmaddr);
if (!strcmp(pshmaddr, ".quit"))
{
break;
}
}
//5.解除映射
ret = shmdt(pshmaddr);
if (-1 == ret)
{
perror("fail to shmdt");
return -1;
}
return 0;
}
#include "../head.h"
int main(void)
{
int ret = 0;
int shmid = 0;
char *pshmaddr = NULL;
//1.创建 key 值
key_t key;
key = ftok("/", 'a');
if (-1 == key)
{
perror("fail to ftok");
return -1;
}
//2.创建共享内存
shmid = shmget(key, 4096, IPC_CREAT | 0664);
if (-1 == shmid)
{
perror("fail to shmget");
return -1;
}
//3.映射共享内存空间
pshmaddr = shmat(shmid, NULL, 0);
if (NULL == pshmaddr)
{
perror("fail to shmat");
return -1;
}
while (1)
{
printf("内容:%s\n", pshmaddr);
if (!strcmp(pshmaddr, ".quit"))
{
break;
}
}
//4.解除映射
ret = shmdt(pshmaddr);
if (-1 == ret)
{
perror("fail to shmdt");
return -1;
}
//5.删除共享内存
shmctl(shmid, IPC_RMID, NULL);
return 0;
}
5.信号灯:
有名信号量数组
1.创建信号灯
//2.创建信号量数组
semid = semget(key, 2, IPC_CREAT | 0664);
if (-1 == semid)
{
perror("fail to semget");
return -1;
}
int semget(key_t key, int nsems, int semflg);
功能:
创建信号量数组
参数:
key:IPC对象名称
nsems:信号量个数
semflg:信号量属性
IPC_CREAT:创建一个信号量数组
返回值:
成功返回0
失败返回-1
2.向信号灯发送命令
//将下标为0的读信号量设置为0
myun.val = 0;
ret = semctl(semid, 0, SETVAL, myun);
if (-1 == ret)
{
perror("fail to semctl");
return -1;
}
//将下标为1的写信号量设置为1
myun.val = 1;
ret = semctl(semid, 1, SETVAL, myun);
if (-1 == ret)
{
perror("fail to semctl");
return -1;
}
int semctl(int semid, int semnum, int cmd, ...);
功能:
向信号灯发送命令
参数:
IPC_RMID 删除信号灯
SETVAL 设置第semnum-th信号量的值为arg.val
返回值:
成功返回0
失败返回-1
3.对信号量完成申请和释放操作
//申请写信号量
mybuf.sem_num = 1;
mybuf.sem_op = -1;
mybuf.sem_flg = SEM_UNDO;
ret = semop(semid, &mybuf, 1);
if (-1 == ret)
{
perror("fail to semop");
return -1;
}
printf("拿到写信号量了!\n");
//释放读信号量
mybuf.sem_num = 0;
mybuf.sem_op = +1;
mybuf.sem_flg = SEM_UNDO;
ret = semop(semid, &mybuf, 1);
if (-1 == ret)
{
perror("fail to semop");
return -1;
}
printf("释放读信号量了!\n");
//申请读信号量
mybuf.sem_num = 0;
mybuf.sem_op = -1;
mybuf.sem_flg = SEM_UNDO;
ret = semop(semid, &mybuf, 1);
if (-1 == ret)
{
perror("fail to semop");
return -1;
}
printf("申请读信号量了!\n");
//申请读信号量
mybuf.sem_num = 0;
mybuf.sem_op = -1;
mybuf.sem_flg = SEM_UNDO;
ret = semop(semid, &mybuf, 1);
if (-1 == ret)
{
perror("fail to semop");
return -1;
}
printf("申请读信号量了!\n");
int semop(int semid, struct sembuf *sops, size_t nsops);
功能:
对信号量完成申请和释放操作
参数:
semid:信号灯ID号
sops:信号灯操作数组
unsigned short sem_num; //操作信号量的下标
short sem_op; //对信号量的操作 +1(释放信号量) -1(申请信号量)
short sem_flg; //SEM_UNDO 操作结束后,信号量的值会恢复到原来的值
nsops:数组元素个数
返回值:
成功返回0
失败返回-1
6.信号灯和共享内存的使用
步骤:
1.创建key
2.创建共享内存
3.创建信号量组
4.信号量组初始化
5.映射共享内存
6.申请写
7.写操作
8.释放读
9.申请读
10.释放写
11.解除映射
12.删除共享内存
信号量灯:
#include "../head.h"
/* 对信号量初始化 */
int init_sem(int semid, int *parray, int len)
{
int i = 0;
union semun myun;
int ret = 0;
for (i = 0; i < len; i++)
{
myun.val = parray[i];
ret = semctl(semid, i, SETVAL, myun);
if (-1 == ret)
{
return -1;
}
}
return 0;
}
/* 信号量申请操作 */
int sem_p(int semid, int semnum)
{
int ret = 0;
struct sembuf mybuf;
mybuf.sem_num = semnum;
mybuf.sem_op = -1;
mybuf.sem_flg = SEM_UNDO;
ret = semop(semid, &mybuf, 1);
if (-1 == ret)
{
return -1;
}
return 0;
}
/* 信号量释放操作 */
int sem_v(int semid, int semnum)
{
int ret = 0;
struct sembuf mybuf;
mybuf.sem_num = semnum;
mybuf.sem_op = +1;
mybuf.sem_flg = SEM_UNDO;
ret = semop(semid, &mybuf, 1);
if (-1 == ret)
{
return -1;
}
return 0;
}
读:
#include "../head.h"
int main(void)
{
//1.创建 key 值
key_t key;
int semid = 0;
int shmid = 0;
int val[2] = {0, 1};
char *pshmaddr = NULL;
key = ftok("/", 'a');
if (-1 == key)
{
perror("fail to ftok");
return -1;
}
//2.创建共享内存
shmid = shmget(key, 4096, IPC_CREAT | 0664);
if (-1 == shmid)
{
perror("fail to shmget");
return -1;
}
//3.创建信号量数组
semid = semget(key, 2, IPC_CREAT | 0664);
if (-1 == semid)
{
perror("fail to semget");
return -1;
}
//4.对信号量数组初始化
init_sem(semid, val, 2);
//5.映射共享内存
pshmaddr = shmat(shmid, NULL, 0);
if (NULL == pshmaddr)
{
perror("fail to shmat");
return -1;
}
while (1)
{
//6.申请读资源
sem_p(semid, 0);
//7.读操作
printf("内容:%s\n", pshmaddr);
if (!strcmp(pshmaddr, ".quit"))
{
break;
}
//8.释放写资源
sem_v(semid, 1);
}
shmdt(pshmaddr);
shmctl(shmid, IPC_RMID, NULL);
semctl(semid, 0, IPC_RMID);
return 0;
}
写:
#include "../head.h"
int main(void)
{
//1.创建 key 值
key_t key;
int semid = 0;
int shmid = 0;
int val[2] = {0, 1};
char *pshmaddr = NULL;
key = ftok("/", 'a');
if (-1 == key)
{
perror("fail to ftok");
return -1;
}
//2.创建共享内存
shmid = shmget(key, 4096, IPC_CREAT | 0664);
if (-1 == shmid)
{
perror("fail to shmget");
return -1;
}
//3.创建信号量数组
semid = semget(key, 2, IPC_CREAT | 0664);
if (-1 == semid)
{
perror("fail to semget");
return -1;
}
//4.对信号量数组初始化
init_sem(semid, val, 2);
//5.映射共享内存
pshmaddr = shmat(shmid, NULL, 0);
if (NULL == pshmaddr)
{
perror("fail to shmat");
return -1;
}
while (1)
{
//6.申请写资源
sem_p(semid, 1);
//7.写操作
gets(pshmaddr);
//8.释放读资源
sem_v(semid, 0);
if (!strcmp(pshmaddr, ".quit"))
{
break;
}
}
shmdt(pshmaddr);
return 0;
}
#ifndef __HEAD_H__
#define __HEAD_H__
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/wait.h>
#include <time.h>
#include <pthread.h>
#include <semaphore.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/shm.h>
#include <sys/sem.h>
typedef struct msg
{
long mtype;
char mtext[256];
}message_t;
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) */
};
extern int sem_v(int semid, int semnum);
extern int sem_p(int semid, int semnum);
extern int init_sem(int semid, int *parray, int len);
#endif