Linux进程间通信
由于read()函数在读不到数据时会阻塞,因此我们在读数据的进程创建命名管道,并阻塞等待写数据的进程向命名管道写入数据 ,在创建命名管道时,给管道的权限建议是0600,即可读可写可执行。创建一块共享内存,writeShm进程每隔1秒向共享内存写入一个字母,readShm进程每隔一秒从共享内存读取一个字母,当从共享内存连续10s读不到数据后,会删除该共享内存。信号量保证子进程先运行,父进程后运行,同
目录
高级信号发送函数sigqueue(),比kill()在发射信号时能携带更多信息
高级信号接收函数sigaction(),比signal()在接收信号时能获取更多信息
进程间通信的目的
- 数据传输:一个进程需要将它的数据发送给另一个进程
- 资源共享:多个进程之间共享同样的资源
- 通知事件:一个进程需要向另一个或一组进程发送消息,通知它发生了某种事件。
- 进程控制:有些进程希望完全控制另一个进程的执行(如 Debug进程),此时控制进程希望能够拦截另 一个进程的所有陷入和异常,并能够及时知道它的状态改变
进程间通信的几种方式
无名管道
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
int main()
{
pid_t pid;
int fd[2];
char readBuf[128];
if(pipe(fd) < 0){
perror("pipe");
exit(-1);
}
pid = vfork();
if(pid == 0){
printf("this is child process\n");
close(fd[0]);
write(fd[1],"my name is child process,haozige handsome",128);
printf("child process is done\n");
exit(0);
}else if(pid > 0){
printf("this is father process\n");
wait(NULL);
close(fd[1]);
read(fd[0],readBuf,128);
printf("from child process content is %s\n",readBuf);
printf("father process is done\n");
}else{
printf("fork is fail\n");
perror("fork");
exit(-1);
}
return 0;
}
命名管道
与无名管道不同的是,命名管道可以在没有关系的两个进程间进行通信
由于read()函数在读不到数据时会阻塞,因此我们在读数据的进程创建命名管道,并阻塞等待写数据的进程向命名管道写入数据 ,在创建命名管道时,给管道的权限建议是0600,即可读可写可执行
读数据进程
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
int main(int argc,char *argv[])
{
if(argc < 2){
printf("missing paramiter -> FIFO");
exit(-1);
}
char readBuf[128];
int readSize = 0;
if(mkfifo(argv[1],0600) < 0){
perror("mkfifo");
exit(-1);
}
int fd = open(argv[1],O_RDONLY);
if(fd < 0){
perror("open");
exit(-1);
}
while(1){
memset(readBuf,'\0',128);
readSize = read(fd,readBuf,128);
printf("read data from FIFO %s, content is %s\n",argv[1],readBuf);
}
close(fd);
return 0;
}
命名管道是类似创建文件的形式创建的
写数据进程
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
int main(int argc,char *argv[])
{
if(argc < 2){
printf("missing paramiter -> FIFO");
exit(-1);
}
char Msg[128] = "from write FIFO send jiangxiaoya is pigHead";
int fd = open(argv[1],O_WRONLY);
while(1){
write(fd,Msg,strlen(Msg));
sleep(1);
}
close(fd);
return 0;
}
当写数据进程启动后,读数据进程不再阻塞,开始读取命名管道的数据
命名管道的应用:
- shell命令使用FIFO将数据从一条管道传送到另一条时,无需创建中间临时文件。
- 客户进程-服务器进程应用程中,FIFO用作汇聚点,在客户进程和服务器进程二者之间传递数据。
创建键值函数ftok()
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
int main(int argc,char *argv[])
{
if(argc < 2){
printf("missing parameter -> keyfile\n");
exit(-1);
}
key_t key;
key = ftok(argv[1],'z');
if(key == -1){
perror("ftok");
exit(-1);
}else{
printf("key value is %d\n",key);
}
key = ftok(argv[1],159);
if(key == -1){
perror("ftok");
exit(-1);
}else{
printf("key value is %d\n",key);
}
return 0;
}
消息队列
消息队列是一个存储信息的链表,全双工通信,进程终止后,数据不会丢式
创建或引用一个消息队列msgget()
向消息队列中添加消息msgsnd()
从消息队列中读取消息msgrcv()
控制消息队列msgctl()
向消息队列添加各种消息的sendMsg进程
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
struct Msgbuf
{
long mtype;
char mtext[128];
};
int main(int argc,char *argv[])
{
key_t key;
key = ftok("./keyfile.txt",'z');
if(key == -1){
perror("ftok");
exit(-1);
}
int msgID = msgget(key,IPC_CREAT|0777);
if(msgID == -1){
perror("msgget");
exit(-1);
}
struct Msgbuf sendMsg = {
130,
"this is sendMsg process"
};
struct Msgbuf sendMsg1 = {
181,
"haozige handsome"
};
struct Msgbuf sendMsg2 = {
166,
"jiangxiaoya pigHand"
};
msgsnd(msgID,&sendMsg,strlen(sendMsg.mtext),0);
msgsnd(msgID,&sendMsg1,strlen(sendMsg1.mtext),0);
msgsnd(msgID,&sendMsg2,strlen(sendMsg2.mtext),0);
struct Msgbuf receive;
memset(&receive,0,sizeof(receive));
msgrcv(msgID,&receive,sizeof(receive.mtext),2349,0);
printf("this is sendMsg process,receive msg from messageQueue,content is %s\n",receive.mtext);
//msgctl(msgID,IPC_RMID,NULL);
return 0;
}
根据消息字段读取消息队列中的消息的receive进程,要使用同一个键值才能访问到同一个消息队列
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
#include <unistd.h>
struct Msgbuf
{
long mtype;
char mtext[128];
};
int main(int argc,char *argv[])
{
key_t key;
key = ftok("./keyfile.txt",'z');
if(key == -1){
perror("ftok");
exit(-1);
}
int msgID = msgget(key,IPC_CREAT|0777);
if(msgID == -1){
perror("msgget");
exit(-1);
}
struct Msgbuf sendMsg = {
2349,
"this is receiveMsg process"
};
msgsnd(msgID,&sendMsg,strlen(sendMsg.mtext),0);
struct Msgbuf receive;
struct Msgbuf receive1;
struct Msgbuf receive2;
memset(&receive,0,sizeof(receive));
memset(&receive1,0,sizeof(receive1));
memset(&receive2,0,sizeof(receive2));
msgrcv(msgID,&receive,sizeof(receive.mtext),130,0);
msgrcv(msgID,&receive1,sizeof(receive1.mtext),181,0);
msgrcv(msgID,&receive2,sizeof(receive2.mtext),166,0);
printf("this is receiveMsg process,receive msg from messageQueue,content is %s\n",receive.mtext);
sleep(5);
printf("this is receiveMsg process,receive msg from messageQueue,content is %s\n",receive1.mtext);
sleep(5);
printf("this is receiveMsg process,receive msg from messageQueue,content is %s\n",receive2.mtext);
msgctl(msgID,IPC_RMID,NULL);
return 0;
}
共享内存
创建或引用一个共享内存shmget()
将共享内存链接到当前进程的地址空间shmat()
断开共享内存与当前进程的地址空间的链接shmdt()
控制共享内存shmctl
创建一块共享内存,writeShm进程每隔1秒向共享内存写入一个字母,readShm进程每隔一秒从共享内存读取一个字母,当从共享内存连续10s读不到数据后,会删除该共享内存
my_writeShm进程:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <unistd.h>
#include <string.h>
#include <sys/shm.h>
int main(int argc,char *argv[])
{
key_t key = ftok(".",'z');
if(key == -1){
perror("ftok");
exit(-1);
}
int shmID = shmget(key,1024*4,IPC_CREAT|0666);
if(shmID == -1){
perror("shmget");
exit(-1);
}
char *tmp = (char *)shmat(shmID,NULL,0);
memset(tmp,'\0',1024*4);
char *str = "abcdefghijklmnopqrstuvwxyz";
while(*str != '\0'){
sprintf(tmp,"%c",*str);
sleep(1);
*str++;
}
memset(tmp,'\0',1024*4);
shmdt(tmp);
//shmctl(shmID,IPC_RMID,NULL);
return 0;
}
my_readShm进程:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <unistd.h>
#include <string.h>
#include <sys/shm.h>
int main(int argc,char *argv[])
{
key_t key = ftok(".",'z');
if(key == -1){
perror("ftok");
exit(-1);
}
int shmID = shmget(key,1024*4,IPC_CREAT|0666);
if(shmID == -1){
perror("shmget");
exit(-1);
}
char *tmp = (char *)shmat(shmID,NULL,0);
while(1){
printf("shm content is %s\n",tmp);
sleep(1);
if(strlen(tmp) == 0){
sleep(10);
if(strlen(tmp) == 0){
break;
}
}
}
shmdt(tmp);
shmctl(shmID,IPC_RMID,NULL);
return 0;
}
管道、消息队列、共享内存的区别
将三者进程间的通信看作纸条上的通信
- 管道:一方拿走这个纸条进行写内容,另一方只能等对方写完这个纸条才能拿走这个纸条进行读内容,半双工通信
- 消息队列:在一个纸箱存放着多张纸条,纸条上有号码标注,每一张纸条都是独一无二,一方拿走了一张纸条进行写内容或读内容,同时另一方也拿走了一张纸条进行写内容或读内容,操作完后会把纸条放回纸箱中,全双工通信
- 共享内存:双方同时拿着纸条,一方在纸条写内容,另一方实时在纸条上读内容(实时性)
信号
向某个进程发送信号函数kill()
信号接收函数signal()
使用signal()函数实现Linux定时器,每隔2s输出一个字符串,使用kill()关闭这个定时器
signal()进程
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/time.h>
#include <signal.h>
static int i = 0;
void time_handle(int signum)
{
i++;
if(i == 2000){
printf("2s to hello haozige,my process pid is %d\n",getpid());
i = 0;
}
}
int main()
{
struct itimerval tmp;
tmp.it_value.tv_sec = 0;
tmp.it_value.tv_usec = 500*1000;
tmp.it_interval.tv_sec = 0;
tmp.it_interval.tv_usec = 500;
if(setitimer(ITIMER_REAL,&tmp,NULL) == -1){
perror("setitimer");
exit(-1);
}//SIGALRM
signal(SIGALRM,time_handle);
while(1);
return 0;
}
相关结构体
kill()进程
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <signal.h>
int main(int argc,char *argv[])
{
if(argc < 2){
printf("missing parameter -> process pid\n");
exit(-1);
}
pid_t pid = atoi(argv[1]);
kill(pid,SIGINT);
sleep(1);
printf("pid is %d process is done!\n",pid);
return 0;
}
高级信号发送函数sigqueue(),比kill()在发射信号时能携带更多信息
高级信号接收函数sigaction(),比signal()在接收信号时能获取更多信息
sigset_t sa_mask:
int sa_flags:
siginfo_t 结构:保存的其他信息
进程A使用sigqueue向进程B发送信号和携带信息:年龄21和年龄20两个整型数,进程B收到该信号后进行处理
进程A:sigqueue()
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <signal.h>
int main(int argc,char *argv[])
{
if(argc < 2){
printf("missing parameter -> process pid\n");
exit(-1);
}
pid_t pid = atoi(argv[1]);
int tmp = 0;
union sigval msg;
msg.sival_int = 21;
while(1){
sigqueue(pid,SIGUSR1,msg);
tmp++;
if(tmp == 5){
msg.sival_int = 19;
}
if(tmp == 10){
kill(pid,SIGINT);
break;
}
sleep(1);
}
return 0;
}
进程B:sigaction()
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/time.h>
#include <signal.h>
static int i = 0;
void time_handle(int signum,siginfo_t *tmp,void *context)
{
if(context != NULL){
printf("msg from pid %d process\n",tmp->si_pid);
printf("age is %d\n",tmp->si_value.sival_int);
printf("age is %d\n",tmp->si_int);
}else{
printf("Segment errors may occur\n");
printf("1%s",(char *)context);
}
}
int main()
{
struct sigaction sigMsg;
printf("my process pid is %d\n",getpid());
sigMsg.sa_flags = SA_SIGINFO;
sigMsg.sa_sigaction = time_handle;
sigemptyset(&sigMsg.sa_mask);//初始化信号集,清空该信号集里的所有信号
sigaction(SIGUSR1,&sigMsg,NULL);
while(1);
return 0;
}
信号量
信号量并非单个非负值,必须定义为多个信号量的集合,称为信号量集
创建一个信号量集semget()
控制信号量集或获取信号量的信息semctl()
信号量控制共享资源的操作,即PV操作semop()
信号量保证子进程先运行,父进程后运行,同理子进程先获得临界资源,父进程后获得临界资源
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <unistd.h>
//semctl()参数4,保存信号量的信息或者设置信号量的信息
union semun {
int val;
struct semid_ds *buf;
unsigned short *array;
struct seminfo *__buf;
};
//p操作
void handle_P(int id)
{
struct sembuf pBuf;
pBuf.sem_num = 0;
pBuf.sem_op = -1;
pBuf.sem_flg = SEM_UNDO;
if(semop(id,&pBuf,1) < 0){
perror("semop");
exit(-1);
}
}
//v操作
void handle_V(int id)
{
struct sembuf vBuf;
vBuf.sem_num = 0;
vBuf.sem_op = 1;
vBuf.sem_flg = SEM_UNDO;
if(semop(id,&vBuf,1) < 0){
perror("semop");
exit(-1);
}
}
int main(int argc,char *argv[])
{
key_t key = ftok(".",'z');
if(key == -1){
perror("ftok");
exit(-1);
}
int semID = semget(key,1,IPC_CREAT|0666);
if(semID < 0){
perror("semget");
exit(-1);
}
union semun tmp;
tmp.val = 0; //信号量初始值
if(semctl(semID,0,SETVAL,tmp) < 0){
perror("semctl");
exit(-1);
}
pid_t pid = fork();
if(pid > 0){
//如果父进程先运行,由于信号量初始值为0,执行p操作后父进程进入阻塞状态
printf("father process is wait\n");
handle_P(semID);//p操作
printf("child process lets father process cancel wait\n");
semctl(semID,0,IPC_RMID);
}else if(pid == 0){
//子进程运行
printf("child process function\n");
//5s后执行v操作唤醒父进程
printf("wake up the father process after 5s\n");
int i;
for(i=1;i<6;i++){
sleep(1);
printf("%d\n",i);
}
handle_V(semID);//v操作
}else{
perror("fork");
exit(-1);
}
return 0;
}
更多推荐
所有评论(0)