无名管道pipe(半双工):(仅限同一个程序运行)
创建无名管道会生成特殊文件,只存在于内存中
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
//int pipe(int pipefd[2]);
//ssize_t write(int fd, const void *buf, size_t count);
int main()
{
int pid;
int fd[2];
char buf[128];
if(pipe(fd) == -1){ //创建无名管道
printf("create pipe failed!\n");
}
pid = fork();//创建进程
if(pid < 0){
printf("creat fork failed!\n");
}else if(pid > 0){//父进程
sleep(3); //延迟3秒
printf("This is father!\n");
close(fd[0]); //关闭读通道
write(fd[1],"Hello , From Father!",strlen("Hello , From Father!"));
//写入通道给子进程
wait();//等待子进程退出收集exit信号
}else{ //子进程
printf("This is Child!\n");
close(fd[1]);//关闭写通道
read(fd[0],buf,128);//读通道父进程发送的数据
printf("read from father:%s\n",buf);//打印读取的数据
exit(0);//退出信号
}
return 0;
}
运行效果如下: (因为父进程等待3秒,所以子进程先运行,等待3秒后父进程运行)
命名管道fifo:(可不同程序运行)
创建命名管道会以特殊设备文件形式存在于文件系统中,如以下我创建的file
程序A:(mkfifo)创建管道(read)读
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
// int mkfifo(const char *pathname, mode_t mode);
int main()
{
char buf[30] = {0};
int nread = 0;
if((mkfifo("./file",0600) == -1) && errno!= EEXIST)//创建命名管道,可读可写
{
printf("mkfifo file failed!\n");
perror("why:");//perror函数获取失败信息
}
int fd = open("./file",O_RDONLY);//只读的方式打开管道file
while(1){
nread = read(fd,buf,30);//读管道中的数据
printf("read=%dbyte from buf:%s\n",nread,buf);//打印读取的字节数,和数据内容
}
close(fd);//关闭文件
return 0;
}
程序B:(write)写
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
// int mkfifo(const char *pathname, mode_t mode);
int main()
{
int cnt = 0;
char *buf = "Ivan hen shuai!"; //要写入的字符串
int fd = open("./file",O_WRONLY);//只写的方式打卡file
printf("write open success\n");
while(1){ //写入5次后跳出循环
write(fd,buf,strlen(buf));//写入
sleep(1);//延时一秒等待写入缓存
if(cnt == 5){
break;
}
}
close(fd);//关闭文件
return 0;
}
运行效果如下: (先运行程序A,等待数据写入读数据,再运行程序B,写入数据,最后程序A打印读取的数据)(fiforead为程序A,fifowrite为程序B)
消息队列:msg(可不同程序运行)
再Linux内核中,有专门的结构体链表,来管理和存储消息队列
思路:1、A如何加消息到队列
2、B如何从队列拿到消息
程序A: msgget
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <string.h>
//ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);
struct msgbuf {
long mtype; /* message type, must be > 0 */
char mtext[128]; /* message data */
};
struct msgbuf readbuf;
int main()
{
int key;
key = ftok(".",'z');//通过ftok函数解析成key
printf("key=%x\n",key);
//每一个消息队列都有一个id号
int msgID = msgget(key,IPC_CREAT|0777);//创建/获取消息队列,可读可写可执行
if(msgID == -1){
printf("get que failed!\n");
}
//将写入队列的内容读出来,消息类型长度888,编号0
msgrcv(msgID,&readbuf,sizeof(readbuf.mtext),888,0);
//打印读取的数据
printf("read from que:%s\n",readbuf.mtext);
//创建数据,是结构体类型,消息类型长度988
struct msgbuf sendbuf = {988,"Thank you for reach!\n"};
//msgsend函数将结构体的数据写入队列
msgsnd(msgID,&sendbuf,strlen(sendbuf.mtext),0);
//用完程序,将不用的垃圾队列移除
msgctl(msgID,IPC_RMID,NULL);
return 0;
}
程序B: msgsend(整体和msgget差不多,却决于谁先发送,谁先读)
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <string.h>
//ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);
struct msgbuf {
long mtype; /* message type, must be > 0 */
char mtext[128]; /* message data */
};
struct msgbuf readbuf;
struct msgbuf sendbuf = {888,"This is maessage from queue!\n"};
int main()
{
int key;
key = ftok(".",'z');
printf("key=%x\n",key);
int msgID = msgget(key,IPC_CREAT|0777);
if(msgID == -1){
printf("get que failed!\n");
}
//msgsend函数将队列里的数据读出来
msgsnd(msgID,&sendbuf,strlen(sendbuf.mtext),0);
printf("send over!\n");
//发送数据到队列
msgrcv(msgID,&readbuf,sizeof(readbuf.mtext),988,0);
printf("return from que:%s\n",readbuf.mtext);
//讲垃圾队列移除
msgctl(msgID,IPC_RMID,NULL);
return 0;
}
运行效果如下:(先运行程序A,等待数据写入读数据,再运行程序B,写入数据,最后程序AB打印读取的数据)(msgget为程序A,msgsend为程序B)
内存共享:shm(可不同程序运行)
内存共享,顾名思义就是他们可以共享某一个内存
思路:1、先创建/获取一个共享内存
2、文件连接这个内存
3、释放内存
4、将内存移除
程序A: shaget
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// int shmget(key_t key, size_t size, int shmflg);
int main()
{
int shmID;
key_t key;
char *shmaddr;
key = ftok(".",1);//获取key值
shmID = shmget(key,1024*4,IPC_CREAT|0666);//创建共享内存,可读可写,生成一个内存的id号
if(shmID == -1){
printf("get failed!\n");
}
/*连接这个内存,第一个0是默认,第二行0是代表映射进去的内容可读可写
生成一个指针变量,并指向内存*/
shmaddr = shmat(shmID,0,0);
printf("shmat ok!\n");//连接成功
strcpy(shmaddr,"Ivan nb123!");//将字符串写入指针变量
sleep(5);//等待5秒
shmdt(shmaddr);//释放内存
shmctl(shmID,IPC_RMID,0);//将内存移除
printf("quit\n");
return 0;
}
程序B: sharead
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// int shmget(key_t key, size_t size, int shmflg);
int main()
{
int shmID;
key_t key;
char *shmaddr;
key = ftok(".",1);
shmID = shmget(key,1024*4,0);//获取
if(shmID == -1){
printf("get failed!\n");
}
shmaddr = shmat(shmID,0,0);//连接这个内存
printf("data:%s\n",shmaddr);//将内存中的数据打印出来
printf("shmat ok!\n");
// sleep(5);
shmdt(shmaddr);//释放内存
//shmctl(key,IPC_RMID,0);//因为程序已经有了移除内存操作,所以这里不用
printf("quit\n");
return 0;
}
运行效果如下:(先运行程序A,等待数据写入读数据到内存,再运行程序B,读取数据,最后程序B打印读取的数据)(shmg为程序A,shmr为程序B)
信号:sig(可不同程序运行)
信号对于Linux来说,就像单片机中的硬件中断,这个信号为“软中断”
信号处理有三种方式:忽略,捕捉信号,默认动作
(SIGKILL和SIGSTOP不能被忽略,他们是ctl+C和ctl+Z)
每一个信号都有名字和编号,都已SIG开头
用kill -l来查看linux中有哪些信号
比如说当A程序无法退出时,我们可以再cmd另一端调用这个信号
如KILL (pid:-9,就是SIGKILL)(A程序的pid号)
前提是我们让程序A绑定了这个KILL这个信号来杀死这个进程
以上是低级信号的操作,下面直接上高级信号操作:
直接上代码demo
程序A: sigaction收信号
#include <signal.h>
#include <stdio.h>
//int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);
//函数指针,第一个为程序的num,第二个为结构体,第三个非空可接收数据
void handler(int signum, siginfo_t *info, void *context)
{
printf("get signum:%d\n",signum);//打印出程序的num
if(context != NULL){//非空可接收数据
printf("get data:%d\n",info->si_int);//打印出结构体里面的整型数据
printf("get data:%d\n",info->si_value.sival_int);//可自选
printf("form:%d\n",info->si_pid);//将程序B的pid打印出来
}
}
int main()
{
struct sigaction act;
printf("pid = %d\n",getpid());//打印出本程序的pid号
act.sa_sigaction = handler;//额外接收数据,为函数指针
act.sa_flags = SA_SIGINFO;//收信号默认
//第一参数是自定义信号量,第二个是一个结构体,绑定函数,第三个为备份,可NULL
sigaction(SIGUSR1,&act,NULL);//处理信号函数注册
while(1);
return 0;
}
程序B: sigaqueue发信号
#include <signal.h>
#include <stdio.h>
//int sigqueue(pid_t pid, int sig, const union sigval value);
int main(int argc,char **argv)
{
int signum;
int pid;
signum = atoi(argv[1]); //因为signum为整型,调用atoi来强制转换ASI码
pid = atoi(argv[2]); //因为pid为整型 ,调用atoi来强制转换ASI码
union sigval value;
value.sival_int = 100;
sigqueue(pid,signum,value); //发信号,第三个参数为联合体,存储要发的数据
printf("pid=%d,done\n",getpid());
return 0;
}
运行效果如下:(程序B为发送信号,程序A为接收信号)
信号量
它不是一个通信方式,它是一个以锁的形式附加在以上其他通信方式的用法
它来决定谁先运行,谁后运行,
可理理解为:
钥匙:信号量,信号量集
房间:临界资源(打印机)
有两个操作:
1、P操作(pgetkey)拿钥匙
2、V操作(vputbackkey)把钥匙放回去
上代码理解:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <stdio.h>
//int semget(key_t key, int nsems, int semflg);
//int semop(int semid, struct sembuf *sops, unsigned nsops)
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 pGetKey(int id)
{
struct sembuf sops;
sops.sem_num = 0;//信号量的编号
sops.sem_op = -1; //取钥匙操作
sops.sem_flg = SEM_UNDO;//当进程自动终止的时候,取消对锁的操作(阻塞)
semop(id, &sops, 1);
printf("get keys!\n");
}
void vPutBackKey(int id)
{
struct sembuf sops;
sops.sem_num = 0;//信号量的编号
sops.sem_op = 1; //取钥匙操作
sops.sem_flg = SEM_UNDO;//当进程自动终止的时候,取消对锁的操作(阻塞)
semop(id, &sops, 1);
printf("put back keys!\n");
}
int main()
{
int semid;
key_t key;
key = ftok(".",2);
//信号量集合中有一个信号量
semid = semget(key,1,IPC_CREAT|0666);//创建/获取信号量
union semun initsem;
initsem.val = 0;
//操作第0个信号量 编号0
semctl(semid,0,SETVAL,initsem);
//SETVAL设置信号量的值,设置为initsem
int pid = fork();
if(pid > 0){
//去拿锁
pGetKey(semid);
printf("This is Father\n");
//锁放回去
vPutBackKey(semid);
semctl(semid,0,IPC_RMID);//销毁锁
}else if(pid == 0){
//这里不拿锁,因为初始状态没有锁,等父进程不执行,来到这里
printf("This is Child\n");
//这时调用拿锁操作,锁池就有一把锁
vPutBackKey(semid);
}else{
printf("fork error!\n");
}
return 0;
}
65,1 底端
运行效果如下:(因为初始化没有钥匙,父进程没有拿到钥匙,子进程先运行,然后将钥匙放入,父进程拿到钥匙再运行)