05_管道
up、dup21、dup函数(复制文件描述符)1 #include <unistd。h>2 int dup(i
nt oldfd);dup函数的功能:从系统中寻找最小可用的文件描述符 作为oldfd的副本。新文件描述符 通
过dup的返回值返回。案例1:dup的基本案例2、dup2函数(复制文件描述符)1 #include <uni
std。h>2 int dup2(int oldfd, int newfd);dup2的功能:将newfd作为
oldfd的副本。如果newfd事先存在 dup2会先close(newfd),然后将newfd作为oldfd
的副本。知识点2【无名管道】1、无名管道的概述管道(pipe)又称无名管道。 无名管道是一种特殊类型的文件,在
应用层体现为两个打开的文件描述符。管道的特点:1、半双工,数据在同一时刻只能在一个方向上流动。2、数据只能从管
道的一端写入,从另一端读出。3、写入管道中的数据遵循先入先出的规则。4、管道所传送的数据是无格式的,这要求管道
的读出方与写入方必须事先约定好数据的格式, 如多少字节算一个消息等。5、管道不是普通的文件,不属于某个文件系统
,其只存在于内存中。6、管道在内存中对应一个缓冲区。不同的系统其大小不一定相同。7、从管道读数据是一次性操作,
数据一旦被读走,它就从管道中被抛弃,释放空间以便写更多的数据。8、管道没有名字,只能在具有血缘关系的进程之间使
用2、无名管道的创建1 #include <unistd。h>2 int pipe(int filedes[2
]);功能:经由参数filedes返回两个文件描述符参数:filedes为int型数组的首地址,其存放了管道的
文件描述符fd[0]、fd[1]。filedes[0]为读而打开,filedes[1]为写而打开管道,file
des[0]的输出是filedes[1]的输入。返回值:成功:返回 0失败:返回-1注意:在使用无名管道的时候
必须事先确定,谁发,谁收的问题。案例1:父进程发 子进程收1 #include <stdio。h>2 #in
clude <unistd。h>3 #include <string。h>4 #include <sys/wa
it。h>5 int main(int argc, char const *argv[])6 {7 //创建一
个无名管道8 int fd[2];9 pipe(fd);1011 //创建一个子进程12 //父进程发 子进程
收13 pid_t pid = fork();14 if (pid == 0) //子进程15 {16 //子
进程的写端无意义(可以事先关闭)17 close(fd[1]);1819 //子进程接收父进程消息20 pri
ntf("子进程%d正在等待父进程的消息\n", getpid());21 unsigned char buf
[128] = "";22 read(fd[0], buf, sizeof(buf));23 printf("
子进程%d读到的消息为:%s\n", getpid(), buf);2425 //子进程读完数据 应该关闭读端
26 close(fd[0]);2728 //显示退出29 _exit(‐1);30 }31 else if
(pid > 0) //父进程32 {33 //父进程的读端无意义(可以事先关闭)34 close(fd[0]
);3536 //写端写入数据37 printf("父进程:%d将3秒后写入数据hello pipe\n",
getpid());38 sleep(3);39 write(fd[1], "hello pipe", str
len("hello pipe"));40 printf("父进程:%d完成写入\n", getpid());
4142 //通信完成 应该关闭写端43 close(fd[1]);4445 //等待子进程退出46 wait
(NULL);47 }48 return 0;49 }503、无名管道读写的特点1、默认用read函数从管道中
读数据是阻塞的。2、调用write函数向管道里写数据,当缓冲区已满时write也会阻塞。3、通信过程中,读端口
全部关闭后,写进程向管道内写数据时,写进程会(收到SIGPIPE信号)退出。案例1:调用write函数向管道里
写数据,当缓冲区已满时write也会阻塞1 #include <stdio。h>2 #include <uni
std。h>3 #include <string。h>4 #include <sys/wait。h>5 int
main(int argc, char const *argv[])6 {7 //创建一个无名管道8 int
fd[2];9 pipe(fd);1011 //创建一个子进程12 //父进程发 子进程收13 pid_t
pid = fork();14 if (pid == 0) //子进程15 {16 getchar();171
8 //显示退出19 _exit(‐1);20 }21 else if (pid > 0) //父进程22 {
23 int i = 0;24 for (i = 0; i < 1000; i++)25 {26 char b
uf[128] = "";27 write(fd[1], buf, 128);28 printf("i=%d\
n", i + 1);29 }3031 //等待子进程退出32 wait(NULL);33 }34 retur
n 0;35 }案例2:通信过程中,读端口全部关闭后,写进程向管道内写数据时,写进程会(收到SIGPIPE信号
)退出1 #include <stdio。h>2 #include <unistd。h>3 #include
<string。h>4 #include <sys/wait。h>5 int main(int argc, c
har const *argv[])6 {7 //创建一个无名管道8 int fd[2];9 pipe(fd)
;1011 //创建一个子进程12 //父进程发 子进程收13 pid_t pid = fork();14 i
f (pid == 0) //子进程15 {16 //子进程的写端无意义(可以事先关闭)17 close(fd
[1]);1819 int i = 0;20 while (1)21 {22 //子进程接收父进程消息23 p
rintf("子进程%d正在等待父进程的消息\n", getpid());24 unsigned char b
uf[128] = "";25 read(fd[0], buf, sizeof(buf));26 printf
("子进程%d读到的消息为:%s\n", getpid(), buf);27 if (++i == 3)28
break;29 }3031 //子进程读完数据 应该关闭读端32 close(fd[0]);3334 //显
示退出35 _exit(‐1);36 }37 else if (pid > 0) //父进程38 {39 //
父进程的读端无意义(可以事先关闭)40 close(fd[0]);4142 while (1)43 {44 /
/写端写入数据45 printf("父进程:%d将1秒后写入数据hello pipe\n", getpid()
);46 sleep(1);47 write(fd[1], "hello pipe", strlen("hel
lo pipe"));48 printf("父进程:%d完成写入\n", getpid());49 }5051
//通信完成 应该关闭写端52 close(fd[1]);5354 //等待子进程退出55 wait(NUL
L);56 }57 return 0;58 }594、无名管道综合案例1 #include <stdio。h>
2 #include <unistd。h>3 #include <string。h>4 #include <s
ys/wait。h>5 int main(int argc, char const *argv[])6 {7
//创建无名管道8 int fd[2];9 pipe(fd);1011 //创建两个子进程12 int i =
0;13 for (i = 0; i < 2; i++)14 {15 pid_t pid = fork();
16 if (pid == 0)17 break;18 }1920 if (i == 0) //子进程121
{22 //ps ‐A 写端23 //读端无意义 可以事先关闭24 close(fd[0]);25 //1要作
为fd[1]的副本26 dup2(fd[1], 1);27 //执行ps ‐A28 execlp("ps",
"ps", "‐A", NULL);29 _exit(‐1);30 }31 else if (i == 1)
//子进程232 {33 //grep bash 读端34 //写端无意义 可以事先关闭35 close(fd
[1]);36 //0作为fd[0]的副本37 dup2(fd[0], 0);38 //执行grep bash
39 execlp("grep", "grep", "bash", NULL);40 _exit(‐1);41
}42 else if (i == 2) //父进程43 {44 //关闭管道读写端45 close(fd[
0]);46 close(fd[1]);47 while (1)48 {49 pid_t pid = wait
pid(‐1, NULL, WNOHANG);50 if (pid > 0)51 {52 printf("子进
程%d退出了\n", pid);53 }54 else if (pid == 0)55 {56 continu
e;57 }58 else if (pid < 0)59 {60 break;61 }62 }63 }6465
return 0;66 }知识点3【有名管道、命名管道】(重要)主要用于没有血缘关系的进程间通信。1、有名管
道的概述FIFO其特点是:1、半双工,数据在同一时刻只能在一个方向上流动。2、写入FIFO中的数据遵循先入先出
的规则。3、FIFO所传送的数据是无格式的,这要求FIFO的读出方与写入方必须事先约定好数据的格式,如多少字节
算一个消息等。4、FIFO在文件系统中作为一个特殊的文件而存在,但FIFO中的内容却存放在内存中。5、管道在内
存中对应一个缓冲区。不同的系统其大小不一定相同。6、从FIFO读数据是一次性操作,数据一旦被读,它就从FIFO
中被抛弃,释放空间以便写更多的数据。7、当使用FIFO的进程退出后,FIFO文件将继续保存在文件系统中以便以后
使用。 8、FIFO有名字,不相关的进程可以通过打开命名管道进行通信(重要、重要、重要)2、有名管道的API1
、创建有名管道FIFO文件的创建1 #include <sys/types。h>2 #include <sys
/stat。h>3 int mkfifo( const char *pathname, mode_t mode
);参数:pathname:FIFO的路径名+文件名。mode:mode_t类型的权限描述符。返回值:成功:返
回 0失败:如果文件已经存在,则会出错且返回-1read。c1 #include <stdio。h>2 #in
clude <sys/types。h>3 #include <sys/stat。h>4 #include <s
tring。h>5 #include <fcntl。h>6 #include <unistd。h>7 int
main(int argc, char const *argv[])8 {9 //创建有名管道(保存两个进程
识别相同目录)10 mkfifo("my_fifo", 0666);1112 //open以读的方式的打开 有
名管道(阻塞 到 对方 以写的方式打开)13 int fd = open("my_fifo", O_RDONL
Y);14 if (fd < 0)15 {16 perror("open");17 return 0;18 }
19 printf("写端open成功了\n");2021 //循环的读取数据22 while (1)23 {
24 //接收数据25 char buf[128] = "";26 read(fd, buf, sizeof(
buf));27 printf("收到数据位:%s\n", buf);2829 //退出循环30 if (st
rcmp(buf, "bye") == 0)31 break;32 }3334 close(fd);35 re
turn 0;36 }37write。c1 #include <stdio。h>2 #include <sys
/types。h>3 #include <sys/stat。h>4 #include <fcntl。h>5 #
include <string。h>6 #include <unistd。h>7 int main(int a
rgc, char const *argv[])8 {9 //创建有名管道(保存两个进程 识别相同目录)10
mkfifo("my_fifo", 0666);1112 //open以写的方式的打开 有名管道(阻塞 到 对
方 以读的方式打开)13 int fd = open("my_fifo", O_WRONLY);14 if (
fd < 0)15 {16 perror("open");17 return 0;18 }19 printf(
"写端open成功了\n");2021 //循环写入数据22 while (1)23 {24 //获取键盘输入
25 char buf[128] = "";26 printf("请输入需要发送的数据:");27 fgets
(buf, sizeof(buf), stdin);28 buf[strlen(buf) ‐ 1] = 0;2
930 //发送数据31 write(fd, buf, strlen(buf));3233 //退出循环34
if (strcmp(buf, "bye") == 0)35 break;36 }3738 close(fd)
;39 return 0;40 }案例1:将收发合并成有一个代码1 #include <stdio。h>2 #
include <sys/types。h>3 #include <sys/stat。h>4 #include
<fcntl。h>5 #include <string。h>6 #include <unistd。h>7 in
t main(int argc, char const *argv[])8 {9 //创建有名管道(保存两个进
程 识别相同目录)10 mkfifo("my_fifo", 0666);1112 #ifdef WRITE13
int fd = open("my_fifo", O_WRONLY);14 #endif15 #ifdef
READ16 int fd = open("my_fifo", O_RDONLY);17 #endif18 i
f (fd < 0)19 {20 perror("open");21 return 0;22 }23 prin
tf("写端open成功了\n");2425 #ifdef WRITE26 //循环写入数据27 while
(1)28 {29 //获取键盘输入30 char buf[128] = "";31 printf("请输入需
要发送的数据:");32 fgets(buf, sizeof(buf), stdin);33 buf[strl
en(buf) ‐ 1] = 0;3435 //发送数据36 write(fd, buf, strlen(bu
f));3738 //退出循环39 if (strcmp(buf, "bye") == 0)40 break;
41 }42 #endif4344 #ifdef READ45 //循环的读取数据46 while (1)47
{48 //接收数据49 char buf[128] = "";50 read(fd, buf, sizeo
f(buf));51 printf("收到数据位:%s\n", buf);5253 //退出循环54 if (
strcmp(buf, "bye") == 0)55 break;56 }57 #endif58 close(
fd);59 return 0;60 }3、有名管道读写的特点阻塞方式打开管道:1、open以只读方式打开FI
FO时,要阻塞到某个进程为写而打开此FIFO2、open以只写方式打开FIFO时,要阻塞到某个进程为读而打开此
FIFO。3、open以只读、只写方式打开FIFO时会阻塞,调用read函数从FIFO里读数据时read也会阻
塞。4、通信过程中若写进程先退出了,则调用read函数从FIFO里读数据时不阻塞;若写进程又重新运行,则调用r
ead函数从FIFO里读数据时又恢复阻塞。5、通信过程中,读进程退出后,写进程向命名管道内写数据时,写进程也会
(收到SIGPIPE信号)退出。6、调用write函数向FIFO里写数据,当缓冲区已满时write也会阻塞。以
非阻塞方式打开管道:1、先以只读方式打开:如果没有进程已经为写而打开一个FIFO, 只读open成功,并且op
en不阻塞。 2、先以只写方式打开:如果没有进程已经为读而打开一个FIFO,只写open将出错返回-1。3、r
ead、write读写命名管道中读数据时不阻塞。4、通信过程中,读进程退出后,写进程向命名管道内写数据时,写进
程也会(收到SIGPIPE信号)退出。注意: open函数以可读可写方式打开FIFO文件时的特点:1、open
不阻塞。2、调用read函数从FIFO里读数据时read会阻塞。3、调用write函数向FIFO里写数据,当缓
冲区已满时write也会阻塞4、单机QQ聊天程序题目:实现单机QQ聊天 提示: 父进程创建子进程,实现多任务。
父进程负责发信息(向FIFO里写数据),子进程负责接收信息( 从FIFO里读数据)。 打开命名管道的用阻塞的
方法打开。07_bob。c1 #include <stdio。h>2 #include <sys/types。
h>3 #include <sys/stat。h>4 #include <fcntl。h>5 #include
<string。h>6 #include <unistd。h>7 #include <sys/wait。h>
8 int main(int argc, char const *argv[])9 {10 //创建两个有名管
道11 mkfifo("bob_to_lucy", 0666);12 mkfifo("lucy_to_bob"
, 0666);1314 int i = 0;15 for (; i < 2; i++)16 {17 pid_
t pid = fork();18 if (pid == 0)19 break;20 }2122 if (i
== 0) //子进程1 负责fa消息 (bob 发给 lucy)23 {24 int fd = open("
bob_to_lucy", O_WRONLY);25 if (fd < 0)26 {27 perror("op
en");28 _exit(‐1);29 }3031 //获取键盘输入32 while (1)33 {34 /
/获取键盘输入35 char buf[128] = "";36 printf("\rbob:");37 fge
ts(buf, sizeof(buf), stdin);38 buf[strlen(buf) ‐ 1] = 0
;3940 //发送数据41 write(fd, buf, strlen(buf));4243 //退出循环4
4 if (strcmp(buf, "bye") == 0)45 break;46 }4748 //退出进程4
9 close(fd);50 _exit(‐1);51 }52 else if (i == 1) //子进程2
负责shou消息 (lucy 发给 bob)53 {54 int fd = open("lucy_to_bo
b", O_RDONLY);55 if (fd < 0)56 {57 perror("open");58 _e
xit(‐1);59 }60 //循环的读取数据61 while (1)62 {63 //接收数据64 cha
r buf[128] = "";65 read(fd, buf, sizeof(buf));66 printf
("\rlucy:%s\n\rbob:", buf);6768 //退出循环69 if (strcmp(buf
, "bye") == 0)70 break;71 }72 close(fd);73 _exit(‐1);74
}75 else if (i == 2) //父进程 负责 回收资源76 {77 while (1)78 {
79 pid_t pid = waitpid(‐1, NULL, WNOHANG);80 if (pid >
0)81 {82 printf("子进程%d退出了\n", pid);83 }84 else if (pid
== 0)85 {86 continue;87 }88 else if (pid < 0)89 {90 bre
ak;91 }92 }93 }94 return 0;95 }9607_lucy。c1 #include <s
tdio。h>2 #include <sys/types。h>3 #include <sys/stat。h>4
#include <fcntl。h>5 #include <string。h>6 #include <uni
std。h>7 #include <sys/wait。h>8 int main(int argc, char
const *argv[])9 {10 //创建两个有名管道11 mkfifo("bob_to_lucy",
0666);12 mkfifo("lucy_to_bob", 0666);1314 int i = 0;15
for (; i < 2; i++)16 {17 pid_t pid = fork();18 if (pid
== 0)19 break;20 }2122 if (i == 0) //子进程1 负责fa消息 (lucy
发给 bob)23 {24 int fd = open("lucy_to_bob", O_WRONLY);25
if (fd < 0)26 {27 perror("open");28 _exit(‐1);29 }3031
//获取键盘输入32 while (1)33 {34 //获取键盘输入35 char buf[128] =
"";36 printf("\rlucy:");37 fgets(buf, sizeof(buf), stdi
n);38 buf[strlen(buf) ‐ 1] = 0;3940 //发送数据41 write(fd,
buf, strlen(buf));4243 //退出循环44 if (strcmp(buf, "bye")
== 0)45 break;46 }4748 //退出进程49 close(fd);50 _exit(‐1);
51 }52 else if (i == 1) //子进程2 负责shou消息 (bob 发给 lucy)53
{54 int fd = open("bob_to_lucy", O_RDONLY);55 if (fd <
0)56 {57 perror("open");58 _exit(‐1);59 }60 //循环的读取数据6
1 while (1)62 {63 //接收数据64 char buf[128] = "";65 read(f
d, buf, sizeof(buf));66 printf("\rbob:%s\n\rlucy:", buf
);6768 //退出循环69 if (strcmp(buf, "bye") == 0)70 break;71
}72 close(fd);73 _exit(‐1);74 }75 else if (i == 2) //父
进程 负责 回收资源76 {77 while (1)78 {79 pid_t pid = waitpid(‐1
, NULL, WNOHANG);80 if (pid > 0)81 {82 printf("子进程%d退出了
\n", pid);83 }84 else if (pid == 0)85 {86 continue;87 }
88 else if (pid < 0)89 {90 break;91 }92 }93 }94 return
0;95 }合并一个:1 #include <stdio。h>2 #include <sys/types。h>
3 #include <sys/stat。h>4 #include <fcntl。h>5 #include <
string。h>6 #include <unistd。h>7 #include <sys/wait。h>8
int main(int argc, char const *argv[])9 {10 //创建两个有名管道1
1 mkfifo("bob_to_lucy", 0666);12 mkfifo("lucy_to_bob",
0666);1314 int i = 0;15 for (; i < 2; i++)16 {17 pid_t
pid = fork();18 if (pid == 0)19 break;20 }2122 if (i ==
0) //子进程1 负责fa消息23 {24 #ifdef BOB25 int fd = open("bob
_to_lucy", O_WRONLY);26 #endif // DEBUG27 #ifdef LUCY28
int fd = open("lucy_to_bob", O_WRONLY);29 #endif // DE
BUG30 if (fd < 0)31 {32 perror("open");33 _exit(‐1);34
}3536 //获取键盘输入37 while (1)38 {39 //获取键盘输入40 char buf[12
8] = "";41 #ifdef BOB42 printf("\rbob:");43 #endif // D
EBUG44 #ifdef LUCY45 printf("\rlucy:");46 #endif // DEB
UG47 fflush(stdout);48 fgets(buf, sizeof(buf), stdin);4
9 buf[strlen(buf) ‐ 1] = 0;5051 //发送数据52 write(fd, buf,
strlen(buf));5354 //退出循环55 if (strcmp(buf, "bye") == 0
)56 break;57 }5859 //退出进程60 close(fd);61 _exit(‐1);62 }
63 else if (i == 1) //子进程2 负责shou消息64 {65 #ifdef BOB66
int fd = open("lucy_to_bob", O_RDONLY);67 #endif // DEB
UG68 #ifdef LUCY69 int fd = open("bob_to_lucy", O_RDONL
Y);70 #endif // DEBUG71 if (fd < 0)72 {73 perror("open"
);74 _exit(‐1);75 }76 //循环的读取数据77 while (1)78 {79 //接收数
据80 char buf[128] = "";81 read(fd, buf, sizeof(buf));82
#ifdef BOB83 printf("\rlucy:%s\n\rbob:", buf);84 #endi
f // DEBUG85 #ifdef LUCY86 printf("\rbob:%s\n\rlucy:",
buf);87 #endif // DEBUG88 fflush(stdout);89 //退出循环90 if
(strcmp(buf, "bye") == 0)91 break;92 }93 close(fd);94
_exit(‐1);95 }96 else if (i == 2) //父进程 负责 回收资源97 {98 w
hile (1)99 {100 pid_t pid = waitpid(‐1, NULL, WNOHANG);
101 if (pid > 0)102 {103 printf("子进程%d退出了\n", pid);104
}105 else if (pid == 0)106 {107 continue;108 }109 else
if (pid < 0)110 {111 break;112 }113 }114 }115116 return
0;117 }