目录
进程间通信的方式有六种:
1、无名 管道(单向通信)
2、有名管道(单向通信)
3、消息队列(双向通信)
4.、共享内存(双向通信)
5、信号(单向通信)
6、信号量(可配合共享内存来使用、合理分配共享资源)
一、无名管道:
创建无名管道:int pipe(int pipefd[2]);
pipefd[2]为一个数组用以控制无名管道的读端和写端;pipefd[0]为读端;pipefd[1] 为写端
返回值:成功返回0,失败返回-1
demo1.c
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
int main()
{
int status;
int fd[2];
pid_t pid;
char *buf = "hello from farther!";
char *readBuf = (char *)malloc(strlen(buf));
if(pipe(fd) == -1){
printf("pipe fail!\n");
}
pid = fork();
if(pid < 0){
printf("fork fail!\n");
}else if(pid > 0){
printf("this is farther\n");
close(fd[0]);
if(write(fd[1],buf,strlen(buf)) == -1){
perror("write fail:");
}
close(fd[1]);
wait(&status);
if(WEXITSTATUS(status) == -1){
printf("child pro qiut becauseof error!\n");
}
}else{
printf("this is child\n");
close(fd[1]);
if(read(fd[0],readBuf,strlen(buf)) == -1){
perror("read fail:");
exit(-1);
}
printf("%s\n",readBuf);
close(fd[0]);
exit(1);
}
return 0;
}
~
-- INSERT -- 11,14-21 All
二、有名管道:
创建有名管道:int mkfifo(const char *pathname, mode_t mode);使用mkfifo来创建一个特殊的文件用以进程间通信
pathname为所创建的文件名,mode为文件的权限 0600为可读可写
创建后,使用open函数打开文件,进程一write写入信息内容,进程二read读取信息,最后
将文件close;
返回值:成功返回0,失败返回-1
demo2.c
进程一写入信息
include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
int main()
{
int n_write;
char *buf = "caiqingfeng";
if(mkfifo("./fifo",0600) == -1 && errno != EEXIST){
printf("mkfifo fail!\n");
perror("why");
}
int fd = open("./fifo",O_WRONLY);
if(fd == -1){
printf("open fail!\n");
}
while(1){
n_write = write(fd,buf,strlen(buf));
if(n_write == -1){
printf("write fail!\n");
}
sleep(1);
}
return 0;
}
~
~
demo3.c
进程二读取信息
#include<stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
int main()
{
char *readBuf = (char *)malloc(50);
int n_read;
int fd = open("./fifo",O_RDONLY);
if(fd == -1){
printf("open fail!\n");
}
while(1){
n_read = read(fd,readBuf,50);
if(n_read == -1){
printf("read fail!\n");
}
printf("%s\n",readBuf);
}
return 0;
}
~
~
~
~
~
~
三、消息队列
1、创建消息队列:int msgget(key_t key, int msgflg);
key为获取和创建消息队列的编码,读端和写端必须key一致才能保证双方是同一个消息队列
key可用ftok()来生成键值
msgflg通常为IPC_CREAT|0777
返回值:成功返回消息队列的ID,错误返回-1
2.、消息队列的写入:int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
msqid为消息队列的ID,msgp为你所写的内容 常常是一个结构体因为除了写入的内容外 我们还需要传递信息的类型,msgsz为写入的大小,msgflg正常情况下为0,以默认模式写入信息
3、消息队列的读取: ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);
msqid消息队列ID;msgp常常也是为结构体;msgtyp信息的类型;msgflg正常情况下为0
4、消息队列的管理:int msgctl(int msqid, int cmd, struct msqid_ds *buf);
msqid消息队列的ID;cmd若要删除消息队列则为宏(IPC_RMID);buf在删除消息队列的情况下为NULL 以默认情况下删除消息队列
写端demo
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
//int msgget(key_t key, int msgflg);
struct msgbuf {
long mtype; /* message type, must be > 0 */
char mtext[128]; /* message data */
};
int main()
{
key_t key;
int msgId;
struct msgbuf sendBuf = {808,"caiqingfeng"};
key = ftok(".",2);
if(key == -1){
printf("ftok fail!\n");
}
msgId = msgget(key,IPC_CREAT|0777);
if(msgId == -1){
printf("msgget fail!\n");
}
if(msgsnd(msgId,&sendBuf,strlen(sendBuf.mtext),0) == -1){
printf("msgsend error!\n");
}
printf("send massge over!\n");
return 0;
}
读端demo
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
//int msgget(key_t key, int msgflg);
struct mymsg
{
long mtype; /* message type, must be > 0 */
char mtext[128]; /* message data */
};
int main()
{
int msgId;
struct mymsg rcvBuf;
key_t key;
key = ftok(".",2);
if(key == -1){
printf("ftok fail!\n");
}
msgId = msgget(key,IPC_CREAT|0777);
if(msgId == -1){
printf("msgget fail!\n");
}
if(msgrcv(msgId,&rcvBuf,sizeof(rcvBuf),808,0) == -1){
printf("msgsend error!\n");
}
printf("%s\n",rcvBuf.mtext);
if(msgctl(msgId,IPC_RMID,NULL) == -1){
printf("msgctl fail!\n");
}
return 0;
}
~
四、共享内存
说明:
1、查看共享内存,使用命令:ipcs -m
2、删除共享内存,使用命令:ipcrm -m [shmid]
1、创建共享内存:
int shmget(key_t key, size_t size, int shmflg);
key与消息队列的key相同;size为所创建的共享内存的大小(只能以兆为单位); shmflg为标识符,IPC_CREAT|0666 即以可读可写的方式创建共享内存
返回值:成功返回shmID,错误返回-1
2、挂载共享内存:
void *shmat(int shmid, const void *shmaddr, int shmflg);
shmid为shmID;shmaddr和shmflg正常情况下设为0;
返回值:成功返回一个共享内存的地址,进程可直接对该地址进行读写、打印等操作;错误返回-1
注:写入时可直接用strcpy();将字符串写入共享内存
3、解除挂载:
int shmdt(const void *shmaddr);
shmaddr为共享内存的地址 即函数shmat的返回值
4、删除共享内存:
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
shmid为shmID,cmd为IPC_RMID,buf为0
写端demo:
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <string.h>
int main()
{
int shmId;
char *shmaddr;
key_t key;
key = ftok(".",1);
if(key == -1){
printf("ftok fail!\n");
}
shmId = shmget(key,1024*5,IPC_CREAT|0666);
if(shmId == -1){
printf("shmget fail!\n");
}
shmaddr = shmat(shmId,0,0);
if(*shmaddr == -1){
printf("shmat fail!\n");
}
strcpy(shmaddr,"caiqingfeng");
if(shmdt(shmaddr) == -1){
printf("shmdt fail!\n");
}
return 0;
}
~
~
~
读端demo:
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <string.h>
int main()
{
int shmId;
char *shmaddr;
key_t key;
key = ftok(".",1);
if(key == -1){
printf("ftok fail!\n");
}
shmId = shmget(key,1024*5,0);
if(shmId == -1){
printf("shmget fail!\n");
}
shmaddr = shmat(shmId,0,0);
if(*shmaddr == -1){
printf("shmat fail!\n");
}
printf("shm massage:%s\n",shmaddr);
if(shmdt(shmaddr) == -1){
printf("shmdt fail!\n");
}
if(shmctl(shmId,IPC_RMID,0) == -1){
printf("shmctl fail!\n");
}
return 0;
}
五、信号:
入门版(无法携带消息):
1、绑定信号: sighandler_t signal(int signum, sighandler_t handler);
signum为你所要接受的信号编号或宏(指令kill-l查看);handler为你接受到函数后所做出的操作函数
2、发送信号:int kill(pid_t pid, int sig);
pid为所发送的进程的进程号,sig为发送的信号编号或宏
发送端demo:
#include <stdio.h>
#include <signal.h>
#include <sys/types.h>
#include <stdlib.h>
int main(int argc,char **argv)
{
int pid;
int signum;
if(argc != 2){
printf("argv error!\n");
}
signum = atoi(argv[1]);
pid = atoi(argv[2]);
if(kill(pid,signum) == -1){
printf("kill fail!\n");
}
return 0;
接受端demo:
#include <stdio.h>
#include <signal.h>
void handler(int signum)
{
printf("signum = %d\n",signum);
printf("I never be killed!\n");
}
int main()
{
signal(2,handler);
while(1);
return 0;
}
~
~
~
~
高级版(可携带消息):
1、绑定信号:int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);
signum为所接受的信号编号和宏,oldact正常情况为NULL,先定义一个struct sigaction的结构体该结构体内容如下
struct sigaction {
void (*sa_handler)(int);
void (*sa_sigaction)(int, siginfo_t *, void *);
sigset_t sa_mask;
int sa_flags;
void (*sa_restorer)(void);
};
若要发送消息,则需要把sa_flags初始化为SA_SIGINFO;且第二个操作函数初始化为你的操作函数;
第二个操作函数的形参int为信号编号,void *无消息传递时为NULL,反之为非空,siginfo_t *为结构体内容如下
siginfo_t {
int si_signo; /* Signal number */
int si_errno; /* An errno value */
int si_code; /* Signal code */
int si_trapno; /* Trap number that caused
hardware-generated signal
(unused on most architectures) */
pid_t si_pid; /* Sending process ID */
uid_t si_uid; /* Real user ID of sending process */
int si_status; /* Exit value or signal */
clock_t si_utime; /* User time consumed */
clock_t si_stime; /* System time consumed */
sigval_t si_value; /* Signal value */
int si_int; /* POSIX.1b signal */
void *si_ptr; /* POSIX.1b signal */
int si_overrun; /* Timer overrun count; POSIX.1b timers */
int si_timerid; /* Timer ID; POSIX.1b timers */
void *si_addr; /* Memory location which caused fault */
long si_band; /* Band event (was int in
glibc 2.3.2 and earlier) */
int si_fd; /* File descriptor */
short si_addr_lsb; /* Least significant bit of address
(since kernel 2.6.32) */
}
void *si_ptr;和 int si_int伴随发送信号指令所携带的消息就储存在这两个变量中,打印出来即可
2、发送信号:
int sigqueue(pid_t pid, int sig, const union sigval value);
pid发送进程的pid;sig为发送的信号;const union sigval value为联合体内容如下
union sigval {
int sival_int;
void *sival_ptr;
};
可以使信号携带整形和字符串类型的消息
发送端demo:
#include <stdio.h>
#include <signal.h>
int main(int argc,char **argv)
{
int pid;
int signum;
pid = atoi(argv[2]);
signum = atoi(argv[1]);
if(argc != 2){
printf("argc fail!\n");
}
// int sigqueue(pid_t pid, int sig, const union sigval value);
union sigval value;
value.sival_int = 404;
value.sival_ptr = "access denied!";
if(sigqueue(pid,signum,value) == -1){
printf("sigqueue fail!\n");
}
printf("my pid = %d\n",getpid());
return 0;
}
~
接收端demo:
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
//void (*sa_sigaction)(int, siginfo_t *, void *);
void handler(int signum,siginfo_t *info,void *context)
{
printf("signum = %d\n",signum);
if(context != NULL){
printf("si_int = %d\n",info->si_int);
printf("si_ptr = %p\n",info->si_ptr);
printf("si_pid = %d\n",info->si_pid);
}else{
printf("no massage\n");
}
}
int main()
{
printf("my pid = %d\n",getpid());
//int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);
struct sigaction *act;
act = malloc(1024);
act->sa_sigaction = handler;
act->sa_flags = SA_SIGINFO;
if(sigaction(3,act,NULL) == -1){
printf("sigaction fail!\n");
}
while(1);
return 0;
}
~
六、信号量
我们可以把信号量看成一个锁,把临界资源看成一个房间,信号量可以管理临界资源,只允许同时只有一个进程来使用临界资源,以免造成临界资源的损坏
1、 创建信号量:int semget(key_t key, int nsems, int semflg);
key与消息队列、共享内存的key相同,nsems为你创建信号量的数量;semflg为IPC_CREAT|0666
返回值:成功返回信号量ID;失败返回-1
2、初始化信号量:int semctl(int semid, int semnum, int cmd, ...);
semid为信号量ID,semnum为你要初始化哪个信号量 0为第一个;cmd填宏SETVAL,cmd填SETVAL会有第四个参数,是一个联合体内容如下:
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) */
};
要先定义一个union semun联合体变量;初始化val ;1为有锁状态,0为无锁状态,并将联合体传入第四个参数即可
3、p、v操作(上锁、解锁):int semop(int semid, struct sembuf *sops, unsigned nsops);
semid为信号量的ID;nsops为上锁的数量;sops为结构体内容如下
unsigned short sem_num; /* semaphore number */
short sem_op; /* semaphore operation */
short sem_flg; /* operation flags */
sem_num为你操作的第几个锁 0为第一个;sem_op为1加锁;为-1则为解锁;sem_flg常规为0
4、删除信号量:int semctl(int semid, int semnum, int cmd, ...);
cmd为IPC_RMID前两个参数和第2点相同
demo:
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <unistd.h>
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) */
};
void p(int semid)
{
struct sembuf pop;
pop.sem_num = 0;
pop.sem_op = -1;
pop.sem_flg = 0;
if(semop(semid,&pop,1) == -1){
printf("p:semop fail!\n");
}
}
void v(int semid)
{
struct sembuf vop;
vop.sem_num = 0;
vop.sem_op = 1;
vop.sem_flg = 0;
if(semop(semid,&vop,1) == -1){
printf("v:semop fail!\n");
}
}
int main()
{
key_t key;
if(ftok(".",1) == -1){
printf("ftok fail!\n");
}
int semid;
union semun setval;
pid_t pid;
setval.val = 0;
semid = semget(key,1,IPC_CREAT|0666);
if(semid == -1){
printf("semget fail!\n");
}
if(semctl(semid,0,SETVAL,setval) == -1){
printf("init sem fail!\n");
}
pid = fork();
if(pid < 0){
printf("fork fail!\n");
}else if(pid == 0){
printf("this is child pro\n");
v(semid);
}else{
p(semid);
printf("this is farther pro\n");
v(semid);
}
if(semctl(semid,0,IPC_RMID) == -1){
printf("delete sem fail!\n");
}
return 0;
}