一、管道(无名,有名)
1.管道是单向的、先进先出的,它把一个进程的输出和另一个进程的输入连接在一起。
2.一个进程(写进程)在管道的尾部写入数据,另一个进程(读进程)从管道的头部读出数据
3.数据被一个进程读出后,将被从管道中删除, 进程试图读空管道时,进程将阻塞。
1、无名管道(用于父进程和子进程之间的通信)
无名管道创建: int pipe(int filedis[2]);
当一个管道建立时,它会创建两个文件描述符:
filedis[0] 用于读管道, filedis[1] 用于写管道
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
void ReadData(int fd)
{
int ret;
char buf[32] = {0};
while (1)
{
ret = read(fd, buf, sizeof(buf));
if (-1 == ret)
{
perror("read");
exit(1);
}
if (!strcmp(buf, "bye"))
{
break;
}
printf("read from pipe : %s\n", buf);
memset(buf, 0, sizeof(buf));
}
close(fd);
}
void WriteData(int fd)
{
int ret;
char buf[32] = {0};
while (1)
{
scanf("%s", buf);
ret = write(fd, buf, strlen(buf));
if (-1 == ret)
{
perror("wirte");
exit(1);
}
if (!strcmp(buf, "bye"))
{
break;
}
memset(buf, 0, sizeof(buf));
}
close(fd);
}
int main()
{
int ret, fd[2] = {0};
pid_t pid;
ret = pipe(fd); //创建一个无名管道
if (-1 == ret)
{
perror("pipe");
exit(1);
}
pid = fork();
if (-1 == pid)
{
perror("fork");
exit(1);
}
else if (0 == pid)
{
close(fd[1]); //关闭写端口
ReadData(fd[0]); //fd[0]读数据
}
else
{
close(fd[0]); //关闭读端口
int status;
WriteData(fd[1]); //fd[1]写数据
wait(&status);
}
return 0;
}
2、有名管道(用于任意两个进程之间)
#有名管道的创建:
注意:管道名称为:“fifo.tmp”,此项操作关系到内核,所以需要在home目录或根目录下操作。
创建用于写的文件:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
int ret, fd;
char buf[32] = {0}; //管道写数据
fd = open("fifo.tmp", O_WRONLY);
if (-1 == fd)
{
perror("open");
exit(1);
}
while (1)
{
scanf("%s", buf);
ret = write(fd, buf, strlen(buf));
if (-1 == ret)
{
perror("read");
exit(1);
}
if (!strcmp(buf, "bye"))
{
break;
}
memset(buf, 0, sizeof(buf));
}
close(fd);
return 0;
}
创建有名管道并且用于读取:(应先写);
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
int ret, fd;
char buf[32] = {0}; //从管道读取数据
ret = mkfifo("fifo.tmp", 666); //创建有名管道
if (ret == -1)
{
perror("mkfifo");
exit(1);
}
fd = open("fifo.tmp", O_RDONLY);
if (-1 == fd)
{
perror("open");
exit(1);
}
while (1)
{
ret = read(fd, buf, sizeof(buf));
if (-1 == ret)
{
perror("read");
exit(1);
}
if (!strcmp(buf, "bye"))
{
break;
}
printf("%s\n", buf);
memset(buf, 0, sizeof(buf));
}
close(fd);
unlink("fifo.tmp");
return 0;
}
3.alarm(闹钟函数)
信号机制是Unix系统中最为古老的进程间通信机制,很多条件可以产生一个信号:
1.当用户按某些按键时,产生信号
2.硬件异常产生信号
#alarm(闹钟):
int alarm( int seconds)
alarm(1);
1秒之后发一个SIGALRM信号
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
void print(int num)
{
alarm(1);
printf("helloworld\n");
}
int main()
{
alarm(1);
signal(SIGALRM, print);
while (1);
return 0;
}
4、消息队列
创建消息队列(msgget)
进程A用来发送数据(msgsnd)
进程B用来接收数据(msgrcv)
ipcs -q :查看所有进程
ipcrm -q -----------:删除进程
创建用于发送的消息队列:
注意:由于share文件夹里的权限不够,此项操作关系到内核,所以只能在根目录或home目录里操作
#include<stdio.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>
#include<stdlib.h>
#include<string.h>
#define MSGKEY 2048
struct msgbuf
{
long mtype; /* message type, must be > 0 */
char mtext[64]; /* message data */
};
int main()
{
struct msgbuf mbuf;
int ret;
int msgid;
msgid = msgget( MSGKEY,IPC_CREAT | IPC_EXCL );
if( -1 == msgid )
{
perror("msgget");
exit(1);
}
while(1)
{
memset(&mbuf, 0, sizeof(mbuf));
mbuf.mtype = 1;
scanf("%s",mbuf.mtext);
ret = msgsnd( msgid, &mbuf, sizeof(mbuf.mtext), 0 );
if( -1 == ret )
{
perror("msgsnd");
exit(1);
}
if( !strcmp(mbuf.mtext,"bye") )
{
break;
}
}
msgctl(msgid, IPC_RMID, NULL);
return 0;
}
创建用于接收的队列:
#include<stdio.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>
#include<stdlib.h>
#include<string.h>
#define MSGKEY 2048
struct msgbuf
{
long mtype; /* message type, must be > 0 */
char mtext[64]; /* message data */
};
int main()
{
struct msgbuf mbuf;
int ret;
int msgid;
msgid = msgget( MSGKEY, 0 );
if( -1 == msgid )
{
perror("msgget");
exit(1);
}
while(1)
{
memset(&mbuf, 0, sizeof(mbuf));
mbuf.mtype = 1;
ret = msgrcv( msgid, &mbuf, sizeof(mbuf.mtext), 1, 0 );
if( -1 == ret )
{
perror("msgsnd");
exit(1);
}
if( !strcmp(mbuf.mtext,"bye") )
{
break;
}
printf("%s\n",mbuf.mtext);
}
msgctl(msgid, IPC_RMID, NULL);
return 0;
}
#通过四个进程实现两个程序可以互相发送信息的功能
注意,此项操作关系到内核,所以需要在home目录或者根目录先进行创建
创建程序
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#define MSGKEY 1234
struct msgbuf {
long mtype; /* message type, must be > 0 */
char mtext[64]; /* message data */
};
int main()
{
struct msgbuf mbuf;
int ret;
int msgid = msgget(MSGKEY, IPC_CREAT | IPC_EXCL);
if (-1 == msgid)
{
perror("msgget");
exit(1);
}
pid_t pid = fork();
if (-1 == pid)
{
perror("fork");
exit(1);
}
else if (0 == pid) //子进程发送
{
while (1)
{
memset(&mbuf, 0, sizeof(mbuf));
mbuf.mtype = 1; //消息类型
scanf("%s", mbuf.mtext);
ret = msgsnd(msgid, &mbuf, sizeof(mbuf.mtext), 0);
if (-1 == ret)
{
perror("msgsnd");
exit(1);
}
if (!strcmp(mbuf.mtext, "bye"))
{
mbuf.mtype = 2;
msgsnd(msgid, &mbuf, sizeof(mbuf.mtext), 0);
break;
}
}
}
else //父进程接收
{
while (1)
{
memset(&mbuf, 0, sizeof(mbuf));
ret = msgrcv(msgid, &mbuf, sizeof(mbuf.mtext), 2, 0);
if (-1 == ret)
{
perror("msgrcv");
exit(1);
}
if (!strcmp(mbuf.mtext, "bye"))
{
kill(pid, 2);
break;
}
printf("\t\t\t%s\n", mbuf.mtext);
memset(&mbuf, 0, sizeof(mbuf));
}
}
sleep(1);
msgctl(msgid, IPC_RMID, NULL);
return 0;
}
接收
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#define MSGKEY 1234
struct msgbuf {
long mtype; /* message type, must be > 0 */
char mtext[64]; /* message data */
};
int main()
{
struct msgbuf mbuf;
int ret;
int msgid = msgget(MSGKEY, 0);
if (-1 == msgid)
{
perror("msgget");
exit(1);
}
pid_t pid = fork();
if (-1 == pid)
{
perror("fork");
exit(1);
}
else if (0 == pid) //子进程发送
{
while (1)
{
memset(&mbuf, 0, sizeof(mbuf));
mbuf.mtype = 2; //消息类型
scanf("%s", mbuf.mtext);
ret = msgsnd(msgid, &mbuf, sizeof(mbuf.mtext), 0);
if (-1 == ret)
{
perror("msgsnd");
exit(1);
}
if (!strcmp(mbuf.mtext, "bye"))
{
mbuf.mtype = 1;
msgsnd(msgid, &mbuf, sizeof(mbuf.mtext), 0);
break;
}
}
}
else //父进程接收
{
while (1)
{
memset(&mbuf, 0, sizeof(mbuf));
ret = msgrcv(msgid, &mbuf, sizeof(mbuf.mtext), 1, 0);
if (-1 == ret)
{
perror("msgrcv");
exit(1);
}
if (!strcmp(mbuf.mtext, "bye"))
{
kill(pid, 2);
break;
}
printf("\t\t\t%s\n", mbuf.mtext);
memset(&mbuf, 0, sizeof(mbuf));
}
}
return 0;
}
5.内存共享
是被多个进程共享的一部分物理内存.共享内存是进程间共享数据的一种最快的方法
功能和效果:首先运行程序A,那么A中的整形数据count会自加,延迟usleep(1000 000) = 1s,此时在运行程序B(通过A复制而来并进行过删减修改)那么B中的count也会自加。但此时应为共享内存的原因,A和B中的count 会 “你来我往” 的个加1,从而B开始运行时,A会自增加 2个,B也会自增加 2个。并且程序B中的count 会从A 已经打印出的数字之后 开始自加 2,程序A也是如此。
1、申请共享内存 shmget
2、映射 shmat
3、使用 strcpy
4、解除映射 shmdt
5、销毁共享内存 shmctl
程序A:
#include<stdio.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<stdlib.h>
#define SHMKEY 1024
#define SHMSIZE 4096
int main()
{
int shmid;
void *shmaddr;
int count = 0;
shmid = shmget( SHMKEY, SHMSIZE, IPC_CREAT | IPC_EXCL );
if( -1 == shmid )
{
perror("shmget");
exit(1);
}
shmaddr = shmat(shmid, NULL, 0);
if( NULL == shmaddr )
{
perror("shmat");
exit(1);
}
*(int *)shmaddr = count;
while(1)
{
count = *(int *)shmaddr;
if(count >= 100)
{
break;
}
printf("Process A : count = %d\n",count );
count ++;
*(int *)shmaddr = count;
usleep(1000000);
}
shmctl(shmid, IPC_RMID ,0);
return 0;
}
程序B:
#include<stdio.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<stdlib.h>
#define SHMKEY 1024
#define SHMSIZE 4096
int main()
{
int shmid;
void *shmaddr;
int count = 0;
shmid = shmget( SHMKEY, SHMSIZE, 0 );
if( -1 == shmid )
{
perror("shmget");
exit(1);
}
shmaddr = shmat(shmid, NULL, 0);
if( NULL == shmaddr )
{
perror("shmat");
exit(1);
}
while(1)
{
count = *(int *)shmaddr;
if(count >= 100)
{
break;
}
printf("Process B : count = %d\n",count );
count ++;
*(int *)shmaddr = count;
usleep(1000000);
}
return 0;
}
如果将UNsleep至于 打印或者循环之前,则会出现以下情况;
请在这里注意:
代码 "usleep(1000 000)"的位置。如果将 "usleep(1000 000)" 置于
count = *(int *)shmaddr;
和
*(int *)shmaddr = count;
之间的话,将不会出现自加 2 的效果。
解决方法:引入信号量
#信号量
信号量(Semaphore),有时被称为信号灯,是在多线程环境下使用的一种设施,是可以用来保证两个或多个关键代码段不被并发调用。在进入一个关键代码段之前,线程必须获取一个信号量;一旦该关键代码段完成了,那么该线程必须释放信号量。其它想进入该关键代码段的线程必须等待直到第一个线程释放信号量。为了完成这个过程,需要创建一个信号量VI,然后将Acquire Semaphore VI以及Release Semaphore VI分别放置在每个关键代码段的首末端。确认这些信号量VI引用的是初始创建的信号量。
接下来将 信号量 添加 进置上方代码,但是运行的效果不变。
程序A:
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#define SHMKEY 1234
#define SHMSIZE 4096 //以页为单位分配共享内存
#define SEMKEY 1234
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 sem_p(int semid)
{
int ret;
struct sembuf sbuf;
sbuf.sem_num = 0; //第一个 从0开始
sbuf.sem_op = -1; //p操作
sbuf.sem_flg = SEM_UNDO;
ret = semop(semid, &sbuf, 1);
if (-1 == ret)
{
perror("semop");
return;
}
}
void sem_v(int semid)
{
int ret;
struct sembuf sbuf;
sbuf.sem_num = 0; //第一个 从0开始
sbuf.sem_op = 1; //v操作
sbuf.sem_flg = SEM_UNDO;
ret = semop(semid, &sbuf, 1);
if (-1 == ret)
{
perror("semop");
return;
}
}
int main()
{
int shmid, semid, ret;
void *shmaddr;
int count = 0;
shmid = shmget(SHMKEY, SHMSIZE, IPC_CREAT | IPC_EXCL); //创建共享内存
if (-1 == shmid)
{
perror("shmget");
exit(1);
}
semid = semget(SEMKEY, 1, IPC_CREAT | IPC_EXCL); //创建信号量
if (semid == -1)
{
perror("semget");
exit(1);
}
union semun unsem;
unsem.val = 1; //初始化成二值信号量
ret = semctl(semid, 0, SETVAL, unsem); //初始化信号量
if (-1 == ret)
{
perror("semctl");
exit(1);
}
shmaddr = shmat(shmid, NULL, 0); //映射到虚拟地址空间
if (NULL == shmaddr)
{
perror("shmat");
exit(1);
}
*(int *)shmaddr = count; //数据写到共享内存
while (1)
{
sem_p(semid); //p操作 拔钥匙
count = *(int *)shmaddr; //读取数据
usleep(100000);
if (count >= 100)
{
break;
}
printf("Process A : count = %d\n", count);
count++;
*(int *)shmaddr = count; //写回共享内存
sem_v(semid); //v操作 加一操作 插钥匙
}
shmdt(shmaddr); //解除映射
shmctl(shmid, IPC_RMID, NULL);
semctl(semid, 0, IPC_RMID);
return 0;
}
程序B:
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#define SHMKEY 1234
#define SHMSIZE 4096 //以页为单位分配共享内存
#define SEMKEY 1234
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 sem_p(int semid)
{
int ret;
struct sembuf sbuf;
sbuf.sem_num = 0; //第一个 从0开始
sbuf.sem_op = -1; //p操作
sbuf.sem_flg = SEM_UNDO;
ret = semop(semid, &sbuf, 1);
if (-1 == ret)
{
perror("semop");
return;
}
}
void sem_v(int semid)
{
int ret;
struct sembuf sbuf;
sbuf.sem_num = 0; //第一个 从0开始
sbuf.sem_op = 1; //v操作
sbuf.sem_flg = SEM_UNDO;
ret = semop(semid, &sbuf, 1);
if (-1 == ret)
{
perror("semop");
return;
}
}
int main()
{
int shmid, semid, ret;
void *shmaddr;
int count = 0;
shmid = shmget(SHMKEY, SHMSIZE, 0); //创建共享内存
if (-1 == shmid)
{
perror("shmget");
exit(1);
}
semid = semget(SEMKEY, 1, 0); //创建信号量
if (semid == -1)
{
perror("semget");
exit(1);
}
shmaddr = shmat(shmid, NULL, 0); //映射到虚拟地址空间
if (NULL == shmaddr)
{
perror("shmat");
exit(1);
}
while (1)
{
sem_p(semid); //p操作 拔钥匙
count = *(int *)shmaddr; //读取数据
usleep(100000);
if (count >= 100)
{
break;
}
printf("Process B : count = %d\n", count);
count++;
*(int *)shmaddr = count; //写回共享内存
sem_v(semid); //v操作 加一操作 插钥匙
}
shmdt(shmaddr); //解除映射
return 0;
}
解决上述问题,结果为
shmdt (shmaddr); 解除映射;
shmctl (shmid, IPC_RMID, NULL);
semctl (semid, 0, IPC_RMID);