- 匿名管道
int pipe(int fds[]);
//失败的返回值是1
//fds[0]从管道里读
//fds[1]从管道里写
举一个例子:(子进程往管道里写,父进程从管道里拿)
#include<stdio.h>
2 #include<stdlib.h>
3 #include<unistd.h>
4 #include<string.h>
5 #include<error.h>
6
7 int main()
8 {
9 int fds[2];
10 if(pipe(fds) == -1)
11 {
12
13 perror("pipe");
14 exit(1);
15 }
16
17 pid_t pid = fork();
18
19
20 if(pid == 0)//让子进程写
21 {
22 //子进程
23 close(fds[0]);//关闭读端
24 write(fds[1],"你好\n",5);
25 close(fds[1]);
26 exit(0);
27
28 }
29 else
30 if(pid > 0)//父进程读
31 {
32 //父进程
33 close(fds[1]);
34 char buf[100] = {};
35 int r = read(fds[0],buf,100);
36 if(r == 0)
37 {
38 printf("read EOF\n");
39 }
40 else
41 if(r == -1) //读失败
42 {
43 perror("read");
44 exit(1);
45 }
46 if(r > 0)
47 {
48 printf("buf = %s",buf);
49
50 }
51 close(fds[0]);
52 exit(0);
53 }
54 else
55 if(pid < 0)
56 {
57 perror("fork");
58 exit(1);
59 }
60
61
62 }
- 命名管道
由于匿名管道只能是有血缘关系的进程使用这个管道。为了能让没有血缘关系的进程使用同一个管道,所以就要用到命名管道。
创建管道的命令:mkfifo name.p //name 是要创建管道的名字
在程序中如何创建管道:
int mkfifo(const char *name,mode_t mode);
//name 是管道的名字
//mode 是管道文件的权限,如0666,0644,等
返回值:失败返回-1
打开管道文件:
读: int fd = open(name,O_RDONLY);
写: int fd = open(name,O_WRONLY);
管道内核的缓存大小是65536
往管道每次写不能超过4096
- 消息队列
创建消息队列:
创建消息队列: msgget(key_t key,int flag)
//key_t key 相当于文件名
//int flag 打开:0,创建:IPC_CREAT | 0644
返回值:消息队列的id,相当于文件描述符
往消息队列里发送消息:
int msgsnd(int id,const viod *msgp,size_t len,int flag);
//int id mesget 返回的值
/void *msgp 要发送的消息在哪里
//size_t len 发送消息的大小
//int flag 0
返回值:成功返回0,失败返回-1
写消息队列的时候,这个结构体要自己定义:
struct msgbuf{
long channel; //消息类型(通道号),必须 >=1
char mtext[100]; //自己的消息的结构,啥样都可以
}
1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<sys/ipc.h>
4 #include<sys/msg.h>
5 #include<string.h>
6
7 struct msgbuf
8 {
9 long mtype; //消息类型(通道号),必须 >=1
10 char mtext[1000]; //自己的消息的结构,啥样都可以
11 };
12
13 int main(int argc,char *argv[])
14 {
15 // ./msgsnd type 要输入两个参数,这个文件的执行程序 ,第二个是消息队列的通道号
16 if(argc != 2)
17 {
18 fprintf(stderr,"usage:%s type\n",argv[0]);
19 exit(1);
20 }
21
22 int id = msgget(1234,0); //获得要发送到的那个消息队列的id
23 if(id == -1)
24 {
25 perror("msgget");
26 exit(1);
27 }
28 struct msgbuf mb;
29 memset(&mb,strlen(mb.mtext),0);
30
31 mb.mtype = atoi(argv[1]); //把字符型的消息队列通道号转换为整型
32 printf("msg:");
33 fgets(mb.mtext,999,stdin); //从标准输入里获取要写入的内容
34
35 int r = msgsnd(id,&mb,strlen(mb.mtext),0); //往消息队列里发内容
36 if( r == -1)
37 {
38 perror("msgsnd");
39 exit(1);
40 }
41
42 }
从消息队列里取消息:
ssize_t msgrcv(int id,void *mssgp,size_t len,long mtype,int flag);
//int id 消息队列的id
//void *mssgp 取出来的消息放在那里
//size_t len 装消息地方的大小,不包括类型
//long mtype //取哪个消息的类型(通道号)
//int flag 0(有消息我就读,没消息我就等)
1
2 #include<stdio.h>
3 #include<stdlib.h>
4 #include<sys/ipc.h>
5 #include<sys/msg.h>
6 #include<string.h>
7
8 struct msgbuf
9 {
10 long mtype;
11 char mtext[1000];
12 };
13
14 int main(int argc,char *argv[])
15 {
16 // ./msgsnd type //第一个取消息这个执行程序,第二个消息通道
17 if(argc != 2)
18 {
19 fprintf(stderr,"usage:%s type\n",argv[0]);
20 exit(1);
21 }
22
23 int id = msgget(1234,0); //获取这个消息队列的id
24 if(id == -1)
25 {
26 perror("msgget");
27 exit(1);
28 }
29 struct msgbuf mb;
30 memset(&mb,0x00,sizeof(mb));
31
32 if(msgrcv(id,&mb,1000,atoi(argv[1]),0) == -1)
33 {
34 perror("msgrcv");
35 exit(1);
36 }
37
38 printf("%s\n",mb.mtext);
39 }
- 共享内存
特点:
1.双向通信
2.用于随意进程
- 不存在“面向字节流”或者“面向数据快”的概念,其实只是一块内存,可以随意的在上面读写数据
- 没有同步互斥
- 生命周期随内核
创建或打开共享内存:
shmget(key_t key,size_t size,int flag);
//key_t key 相当于文件名
//size_t size 共享内存段的大小
//int flag 创建:IPC_CREAT | 0644 打开:0
#include<stdio.h>
2 #include<stdlib.h>
3 #include<sys/ipc.h>
4 #include<sys/shm.h>
5
6 struct stu
7 {
8 int id;
9 char name[20];
10 };
11
12 int main(void)
13 {
14 int shmid = shmget(1234,sizeof(struct stu),IPC_CREAT | 0644);
15
16 if(shmid == -1)
17 {
18 perror("shmget");
19 exit(1);
20 }
21
22 printf("creat shm ok\n");
23
24 }
让共享内存和本进程建立关系(挂载:attach):
shmad(int id,const char *shmadder,int flag);
//int id 共享内存的id
//const char *shmaddr 想让操作系统挂载到这个地址空间,若写NULL ,则让操作系统自己选择
//int flag) 0
返回值: 实际挂载到的虚拟地址的起始位置
卸载掉共享内存:
int shmdt(void *shmadder);
//只是和本进程没有关了,并没有删除
删除共享内存:
int shmctl(int id,int cmd,NULL);
//int cmd ------IPC_RMID 删除共享内存
- 信号量
信号量是计数器
信号量是用于信号的同步和互斥
PV操作:申请资源、释放资源
原语
PV操作
上锁– :P
解锁++ :V
创建或打开信号量:
int semget(key_t key,int names,int flags);
// int nsems ----------信号量集中信号量的个数
//int flags -----打开0,创建IPC_CREAT | 0644
信号量的销毁:
semctl(snmid,0,IPC_EMID);
设置信号量初值:
semctl(int semid,int semnum,int cmd,su);
//int semnu 信号量集中的第几个信号量
//int cmd ---SETVAL
//su ---信号量初值
union semun
{
int val; //value for SETVAL
}
查看信号量的值:
int semctl(semid,int semnum,int cmd,0)
//int semnum -----信号量集中的第几个信号量
//int cmd, -----GETVAL
PV操作:
semop(int semid,struct sembuf sb[],int len);
struct sembuf{
short sem_num, //信号量的下标
short sem_op, //1 V操作, -1 P操作
short sem_flg, //0,若取SEM_UNDO--------进程结束后把申请的信号量给你还原,IPC_NOWAIT -------- 进程不会挂起,P操作也不会成功
1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<sys/ipc.h>
4 #include<sys/sem.h>
5 #include<unistd.h>
6
7
8 union semun
9 {
10 int val;
11 };
12
13 void P(int semid) //P操作
14 {
15 struct sembuf buf[1];
16 buf[0].sem_num = 0;
17 buf[0].sem_op = -1;
18 buf[0].sem_flg = 0;
19
20 int ret = semop(semid,buf,1);
21 if(ret<0)
22 {
23 perror("P 失败"),
24 exit(1);
25 }
26 }
27
28 void V(int semid) //V操作
29 {
30 struct sembuf buf[1];
31 buf[0].sem_num = 0;
32 buf[0].sem_op = 1;
33 buf[0].sem_flg = 0;
34
35 int ret = semop(semid,buf,1);
36 if(ret < 0) perror("V 失败"),exit(1);
37 }
38
39 int main()
40 {
41 int semid = semget(1234,1,IPC_CREAT|0666); //创建信号量
42 if(semid < 0) perror("semget fail"),exit(1);
43
44 union semun su;
45 su.val = 1; //给信号量赋值为1
46 semctl(semid,0,SETVAL,su);
47
48 pid_t pid = fork();
49 if(pid > 0)
50 {
51 //父进程
52 while(1)
53 {
54
55 P(semid); //-1
56 printf("A");
57 fflush(stdout);
58 usleep(12324);
59 printf("A ");
60 fflush(stdout);
61 usleep(323423);
62 V(semid); //+1
63 }
64
65
66 }
67 else
68 if(pid == 0)
69 {
70 //子进程
71 while(1)
72 {
73
74 P(semid); //-1
75 printf("B");
76 fflush(stdout);
77 usleep(12324);
78 printf("B ");
79 fflush(stdout);
80 usleep(323423);
81 V(semid); //+1
82 }
83 }
84 else
85 {
86 perror("fork");
87 exit(1);
88 }
89 }
- 进程互斥
由于各进程要求共享资源,而且有些资源需要互斥使用,因此各进程间竞争使用这些资源,进程的这种关系为进程的互斥。系统中某些资源一次只允许一个进程使用,称这样的资源为临界资源或互斥资源。在进程中涉及到互斥资源的程序段叫临界区。互斥是一种独占关系,如任一时刻,进程1和进程2只能有一个写文件C。
- 进程同步
是指多个进程需要相互配合共同完成一项任务。在大多数情况下,同步已经实现了互斥,特别是所有写入资源的情况必定是互斥的。少数情况是指可以允许多个访问者同时访问资源,如“第一类读写者模型”。同步是一种时序关系,如规定了进程1处理完事情A后,进程2才能处理事情B。
如果A和B互相包含,那A只能等于B,而同步和互斥的是不对等的。但从广义上来看,互斥是一种特殊的同步,同步是一种更为复杂的互斥关系。
- 临界资源
系统中同时存在有许多进程,他们共享各种资源,然而有许多资源在某一时刻只能允许一个进程使用。例如打印机,磁带机等硬件设备和变量,队列等数据结构,如果有多个进程同时去使用这些资源就会造成混乱。因此必须保护这些资源,避免两个进程同时访问这类资源。我们把某段时间只能允许一个进程使用的资源成为临界资源。
- 生产者与消费者
有两个进程分别为消费者进程和生产者进程,对同一个临界资源进行访问,生产者不断的将生产的产品加入缓冲区,而消费者不断的消费缓冲区中的资源,利用信号量实现两个进程的同步与互斥。
在生产者往缓冲区加入产品时,需要两个信号量,一个是互斥信号量,使得在生产者往缓存里放产品的时候其他进程不能对临界资源进行操作,另一个信号量是指示缓存区是否已满的信号量,从而判断生产者能否往缓冲区加入产品;而消费者从缓冲区中消费资源的时候,也需要两个信号量,一个信号量是互斥信号量,使得消费者在从缓冲区中取产品的时候其他进程不能对临界资源进行操作,另外一个信号量是指示缓冲区是否为空,从而判断消费者能否对缓冲区进行操作。
由以上分析,可知在该问题中共需要三个信号量,一个是用于互斥的信号量mutux=1; 一个是用于指示缓冲区空位置数目的empty=N;另外一个是指示缓冲区填满位置的信号量full = 0;
用于同步的信号量一定是位于不同进程中,用于互斥的信号量一定是位于同一个进程中。