1.错误处理函数:
#include<stdio.h>
#include<errno.h>
#include<string.h>
int main(void)
{
FILE *Fr = fopen("./2.txt", "r");
//全局变量errno会实时更新,装载的是最新被调用的系统回调的操作状态
//如下面操作,如果2.txt打开成功,errno装载的值表示的是success,否则其
//装载的值表示的是failed
if(NULL == Fr)
{
printf("1.errno:%d\n",errno);
printf("1.errno:%s\n",strerror(errno));
perror("1.fopen error");
}
else
{
printf("2.errno:%d\n",errno);
printf("2.errno:%s\n",strerror(errno));
perror("2.fopen error");
}
return 0;
}
2.进程的创建:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
int main(void)
{
int i = 0;
pid_t pid = -1;
int ret = -1;
//连续创建3个子进程
for(i=0; i<3; i++)
{
pid = fork();
if(-1 == pid)
{
perror("fork");
break;
}
//fork成功,子进程中返回0;父进程中返回子进程ID
if(0 == pid)
{
break;
}
}
if(0 == i)
{
printf("第1个子进程 getpid:%d\n", getpid());
}
else if(1 == i)
{
printf("第2个子进程 getpid:%d\n", getpid());
}
else if(2 == i)
{
printf("第3个子进程 getpid:%d\n", getpid());
}
else
{
//父进程 阻塞 等全部子进程结束,将回收其资源并结束阻塞
while(-1 != (ret = waitpid(-1, NULL, 0)))
{
printf("父进程等待子进程pid:%d\n", ret);
}
}
return 0;
}
3.无名管道
(1)无名管道用于父子进程或者兄弟进程间通信;
(2)系统中使用ulimit -a;命令可以查看管道对应的缓冲区大小;
使用管道需要注意以下4种特殊情况(假设都是阻塞I/O操作,没有设置O_NONBLOCK标志):
(1)如果所有指向管道写端的文件描述符都关闭了(管道写端引用计数为0),而仍然有进程从管道的读端读数据,那么管道中剩余的数据都被读取后,再次read会返回0,就像读到文件末尾一样。
(2)如果有指向管道写端的文件描述符没关闭(管道写端引用计数大于0),而持有管道写端的进程也没有向管道中写数据,这时有进程从管道读端读数据,那么管道中剩余的数据都被读取后,再次read会阻塞,直到管道中有数据可读了才读取数据并返回。
(3)如果所有指向管道读端的文件描述符都关闭了(管道读端引用计数为0),这时有进程向管道的写端write,那么该进程会收到信号SIGPIPE,通常会导致进程异常终止。当然也可以对SIGPIPE信号实施捕捉,不终止进程。具体方法信号章节详细介绍。
(4)如果有指向管道读端的文件描述符没关闭(管道读端引用计数大于0),而持有管道读端的进程也没有从管道中读数据,这时有进程向管道写端写数据,那么在管道被写满时再次write会阻塞,直到管道中有空位置了才写入数据并返回。
总结:
读管道:
管道中有数据,read返回实际读到的字节数。
管道中无数据:
管道写端被全部关闭,read返回0 (相当于读到文件结尾)
写端没有全部被关闭,read阻塞等待(不久的将来可能有数据递达,此时会让出cpu)
写管道:
管道读端全部被关闭, 进程异常终止(也可使用捕捉SIGPIPE信号,使进程不终止)
管道读端没有全部关闭:
管道已满,write阻塞。
管道未满,write将数据写入,并返回实际写入的字节数。
3-1.无名管道的创建:
#include<stdio.h>
#include<unistd.h>
int main(void)
{
int ret = -1;
int fd[2] = {0};
//创建一个无名管道,fd[0]用于读,fd[1]用于写
ret = pipe(fd);
if(-1 == ret)
{
perror("pipe");
goto err0;
}
//返回两个最小的待使用文件描述符供使用
printf("fd[0]: %d fd[1]: %d\n", fd[0], fd[1]);
//关闭文件描述符
close(fd[0]);
close(fd[1]);
return 0;
err0:
return 1;
return 0;
}
3-2.无名管道的使用初探:
子进程写,父进程读。
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/wait.h>
#define SIZE 128
//父子进程间使用无名管道通讯
int main(void)
{
int ret = -1;
int fd[2] = {0};
char buf[SIZE] = {0};
pid_t pid = -1;
//创建一个无名管道,fd[0]用于读,fd[1]用于写
ret = pipe(fd);
if(-1 == ret)
{
perror("pipe");
goto err0;
}
//创建子进程
pid = fork();
if(-1 == pid)
{
perror("fork");
goto err0;
}
//子进程 写管道
if(0 == pid)
{
memset(buf, 0, SIZE);
strcpy(buf, "hello man");
close(fd[0]);//关闭读端
ret = write(fd[1], buf, strlen(buf));
if(-1 == ret)
{
perror("write");
close(fd[1]);
exit(1);
}
printf("子进程写管道字节数 ret = %d\n", ret);
close(fd[1]);
exit(0);
}
//父进程 读管道
close(fd[1]);//关闭写端
//wait(NULL);//等待子进程退出
memset(buf, 0, SIZE);
ret = read(fd[0], buf, SIZE);//子进程写端未关闭而管道内又无内容时,这里会阻塞等待有内容时再读取
if(-1 == ret)
{
perror("read");
goto err1;
}
printf("从管道中读取的数据:%s\n", buf);
close(fd[0]);
return 0;
err1:
close(fd[0]);
close(fd[1]);
err0:
return 1;
}
3-3 无名管道的使用
子进程循环写,父进程只读一次。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#define SIZE 128
//父子进程使用无名管道通讯
int main(void)
{
int ret = -1;
int status = -1;
int fd[2] = {0};
char buf[SIZE];
pid_t pid = -1;
//创建一个无名管道
//fd[0]: 读
//fd[1]: 写
ret = pipe(fd);
if (-1 == ret)
{
perror("pipe");
goto err0;
}
//创建子进程
pid = fork();
if (-1 == pid)
{
perror("fork");
goto err1;
}
//子进程 不停地写管道
if (0 == pid)
{
//关闭读端
close(fd[0]);
printf("hello world child process\n");
while(1)
{
//sleep(1);
memset(buf, 0, SIZE);
strcpy(buf, "hello blackview");
printf("hello child process while\n");
//写管道
//1. 如果管道被写满了 那么写管道就会阻塞
//2. 如果管道的读端全部被关闭,那么写管道直接被signal杀死
ret = write(fd[1], buf, strlen(buf));
if (ret <= 0)
{
perror("write");
break;
}
printf("子进程写管道字节数 ret = %d\n", ret);
}
printf("子进程退出...\n");
close(fd[1]);
exit(0);
}
//父进程 读管道
//关闭写端
close(fd[1]);
//关闭读端
close(fd[0]);
//等待子进程退出
wait(&status);
if (WIFSIGNALED(status))
{
printf("child proces is killed by signal:%d\n", WTERMSIG(status));
}
#if 0
memset(buf, 0, SIZE);
//从管道中读取数据 管道默认是阻塞
ret = read(fd[0], buf, SIZE);
if (-1 == ret)
{
perror("read");
goto err1;
}
printf("从管道中读取的数据: %s\n", buf);
close(fd[0]);
#endif
return 0;
err1:
close(fd[0]);
close(fd[1]);
err0:
return 1;
}
3-4.无名管道的使用
验证:
1.如果管道中没有数据,那么读管道进程默认会阻塞;
2.如果写端被全部关闭,那么读管道的进程读管道函数会结束阻塞并返回0。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#define SIZE 128
//父子进程使用无名管道通讯
int main(void)
{
int ret = -1;
int status = -1;
int fd[2] = {0};
char buf[SIZE];
pid_t pid = -1;
//创建一个无名管道
//fd[0]: 读
//fd[1]: 写
ret = pipe(fd);
if (-1 == ret)
{
perror("pipe");
goto err0;
}
//创建子进程
pid = fork();
if (-1 == pid)
{
perror("fork");
goto err1;
}
//子进程 不停地写管道
if (0 == pid)
{
//关闭读端
close(fd[0]);
#if 0 //验证;管道无内容,读端默认阻塞
printf("hello world child process\n");
//睡眠10秒
sleep(10);
//sleep(1);
memset(buf, 0, SIZE);
strcpy(buf, "hello blackview");
ret = write(fd[1], buf, strlen(buf));
if (ret <= 0)
{
perror("write");
}
printf("子进程写管道字节数 ret = %d\n", ret);
printf("子进程退出...\n");
#endif
close(fd[1]);
exit(0);
}
//父进程 读管道
//关闭写端
close(fd[1]);
#if 0
//等待子进程退出
wait(&status);
if (WIFSIGNALED(status))
{
printf("child proces is killed by signal:%d\n", WTERMSIG(status));
}
#endif
memset(buf, 0, SIZE);
//从管道中读取数据 管道默认是阻塞
//1. 如果管道中没有数据,那么读管道进程会阻塞
//2. 如果写端全部关闭,那么读管道的进程读管道返回0
ret = read(fd[0], buf, SIZE);
printf("ret:%d\n", ret);
if (-1 == ret)
{
perror("read");
goto err1;
}
printf("从管道中读取的数据: %s\n", buf);
close(fd[0]);
return 0;
err1:
close(fd[0]);
close(fd[1]);
err0:
return 1;
}
3-5 无名管道的使用
读端非阻塞属性的使用(管道无内容,读端不等待,直接返回-1)。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <fcntl.h>
#define SIZE 128
//父子进程使用无名管道通讯
int main(void)
{
int ret = -1;
int status = -1;
int arg = -1;
int fd[2] = {0};
char buf[SIZE];
pid_t pid = -1;
//创建一个无名管道
//fd[0]: 读
//fd[1]: 写
ret = pipe(fd);
if (-1 == ret)
{
perror("pipe");
goto err0;
}
//创建子进程
pid = fork();
if (-1 == pid)
{
perror("fork");
goto err1;
}
//子进程 不停地写管道
if (0 == pid)
{
//关闭读端
close(fd[0]);
#if 0
printf("hello world child process\n");
//睡眠10秒
sleep(10);
//sleep(1);
memset(buf, 0, SIZE);
strcpy(buf, "hello blackview");
ret = write(fd[1], buf, strlen(buf));
if (ret <= 0)
{
perror("write");
}
printf("子进程写管道字节数 ret = %d\n", ret);
printf("子进程退出...\n");
#endif
sleep(10);
close(fd[1]);
exit(0);
}
//父进程 读管道
//关闭写端
close(fd[1]);
#if 0
//等待子进程退出
wait(&status);
if (WIFSIGNALED(status))
{
printf("child proces is killed by signal:%d\n", WTERMSIG(status));
}
#endif
//获取文件描述符属性
arg = fcntl(fd[0], F_GETFL);
//设置非阻塞
arg = arg | O_NONBLOCK;
//设置文件描述符属性
fcntl(fd[0], F_SETFL, arg);
memset(buf, 0, SIZE);
//从管道中读取数据 管道默认是阻塞
//1. 如果写端没有关闭, 读端设置为非阻塞,如果没有数据读, 那么就直接返回-1
ret = read(fd[0], buf, SIZE);
printf("ret:%d\n", ret);
if (-1 == ret)
{
perror("read");
goto err1;
}
printf("从管道中读取的数据: %s\n", buf);
close(fd[0]);
return 0;
err1:
close(fd[0]);
close(fd[1]);
err0:
return 1;
}
3-6无名管道的使用
写端非阻塞属性的使用(管道已满写端不阻塞等待,直接返回)
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <fcntl.h>
#define SIZE 128
//父子进程使用无名管道通讯
int main(void)
{
int ret = -1;
int status = -1;
int arg = -1;
int fd[2] = {0};
char buf[SIZE];
pid_t pid = -1;
//创建一个无名管道
//fd[0]: 读
//fd[1]: 写
ret = pipe(fd);
if (-1 == ret)
{
perror("pipe");
goto err0;
}
//创建子进程
pid = fork();
if (-1 == pid)
{
perror("fork");
goto err1;
}
//子进程 不停地写管道
if (0 == pid)
{
//关闭读端
close(fd[0]);
printf("hello world child process\n");
//获取文件描述符属性
arg = fcntl(fd[1], F_GETFL);
//设置非阻塞
arg = arg | O_NONBLOCK;
//设置文件描述符属性
fcntl(fd[1], F_SETFL, arg);
while(1)
{
//sleep(1);
memset(buf, 0, SIZE);
strcpy(buf, "hello blackview");
ret = write(fd[1], buf, strlen(buf));
if (ret <= 0)
{
perror("write");
break;
}
printf("子进程写管道字节数 ret = %d\n", ret);
}
printf("子进程退出...\n");
close(fd[1]);
exit(0);
}
//父进程 读管道
//关闭写端
close(fd[1]);
//等待子进程退出
wait(&status);
if (WIFSIGNALED(status))
{
printf("child proces is killed by signal:%d\n", WTERMSIG(status));
}
memset(buf, 0, SIZE);
//从管道中读取数据 管道默认是阻塞
//1. 如果写端没有关闭, 读端设置为非阻塞,如果没有数据读, 那么就直接返回-1
ret = read(fd[0], buf, SIZE);
printf("ret:%d\n", ret);
if (-1 == ret)
{
perror("read");
goto err1;
}
printf("从管道中读取的数据: %s\n", buf);
close(fd[0]);
return 0;
err1:
close(fd[0]);
close(fd[1]);
err0:
return 1;
}
3-7 无名管道的使用
查看读、写端缓冲区大小。
#include <stdio.h>
#include <unistd.h>
int main(void)
{
int ret = -1;
int fd[2] = {0};
//创建无名管道
ret = pipe(fd);
if (-1 == ret)
{
perror("pipe");
goto err0;
}
//输出管道缓冲区
printf("_PC_PIPE_BUF: %ld\n", fpathconf(fd[0], _PC_PIPE_BUF));
printf("_PC_PIPE_BUF: %ld\n", fpathconf(fd[1], _PC_PIPE_BUF));
close(fd[0]);
close(fd[1]);
return 0;
err0:
return 1;
}
4.有名管道
命名管道(FIFO)和无名管道(pipe)有一些特点是相同的,不一样的地方在于:
(1)FIFO 在文件系统中作为一个特殊的文件而存在,但 pipe 中的内容却存放在内存中。
(2)当使用 FIFO 的进程退出后,FIFO 文件将继续保存在文件系统中以便以后使用。
(3)FIFO 有名字,不相关的进程可以通过打开命名管道进行通信。
4-1.有名管道的创建:
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
//创建一个有名管道
int main(void)
{
int ret = -1;
ret = mkfifo("./my_fifo", 0644);//有名管道的名字和权限
if(-1 == ret)
{
perror("mkfifo");
goto err0;
}
puts("mkfifo ok...");
return 0;
err0:
return 1;
}
4-2.有名管道的使用1
一个进程写,一个进程读。
write.c
#include<stdio.h>
#include<sys/types.h>
#include<string.h>
#include<sys/stat.h>
#include<fcntl.h>
#define SIZE (128)
int main(void)
{
int ret = 0;
int fd = -1;
char buf[SIZE] = {0};
if(0 != access("./myfifo", F_OK))//管道不存在则创建它
{
ret = mkfifo("./myfifo", 0777);
if(0 != ret)
{
perror("mkfifo");
goto err0;
}
}
fd = open("./myfifo", O_WRONLY);
if(-1 == fd)
{
perror("open myfifo as write-only");
goto err0;
}
sprintf(buf, "%s", "Nothing is impossible for a willing heart!");
ret = -1;
ret = write(fd, buf, strlen(buf));//写完后默认是会阻塞的,等待管道内容被读取之后才继续往下执行
if(-1 == ret)
{
perror("write to myfifo");
goto err1;
}
printf("write data length is [%d] ?= [%lu]\n", ret, strlen(buf));
close(fd);
return 0;
err1:
close(fd);
err0:
return 1;
}
read.c
#include<stdio.h>
#include<sys/types.h>
#include<string.h>
#include<sys/stat.h>
#include<fcntl.h>
#define SIZE (128)
int main(void)
{
int ret = 0;
int fd = -1;
char buf[SIZE] = {0};
if(0 != access("./myfifo", F_OK))//管道不存在则创建它
{
ret = mkfifo("./myfifo", 0777);
if(0 != ret)
{
perror("mkfifo");
goto err0;
}
}
fd = open("./myfifo", O_RDONLY);
if(-1 == fd)
{
perror("open myfifo as read-only");
goto err0;
}
ret = -1;
ret = read(fd, buf, SIZE);//如果管道中无内容,默认是阻塞等待读取的
if(-1 == ret)
{
perror("read data from myfifo");
goto err1;
}
printf("buf[%s], length[%d]\n", buf, ret);
close(fd);
return 0;
err1:
close(fd);
err0:
return 1;
}
4-3.有名管道的使用2
一个进程循环写入,一个进程循环读取。
write.c
#include<stdio.h>
#include<sys/types.h>
#include<string.h>
#include<sys/stat.h>
#include<fcntl.h>
#define SIZE (128)
int main(void)
{
int ret = 0;
int fd = -1;
char buf[SIZE] = {0};
int cnt = 0;
if(0 != access("./myfifo", F_OK))
{
ret = mkfifo("./myfifo", 0644);
if(0 != ret)
{
perror("mkfifo");
goto err0;
}
}
fd = open("./myfifo", O_WRONLY);
if(-1 == fd)
{
perror("open myfifo as write-only");
goto err0;
}
while(1)//循环向管道写入数据
{
memset(buf, 0, SIZE);
sprintf(buf, "%d %s", cnt++, "Nothing is impossible for a willing heart!");
ret = -1;
ret = write(fd, buf, strlen(buf));
if(-1 == ret)
{
perror("write data to myfifo");
close(fd);
return 1;
}
printf("write data length is [%d] ?= [%lu]\n", ret, strlen(buf));
sleep(1);
}
return 0;
err0:
return 1;
}
read.c
#include<stdio.h>
#include<sys/types.h>
#include<string.h>
#include<sys/stat.h>
#include<fcntl.h>
#define SIZE (128)
int main(void)
{
int ret = 0;
int fd = -1;
char buf[SIZE] = {0};
if(0 != access("./myfifo", F_OK))
{
ret = mkfifo("./myfifo", 0644);
if(-1 == fd)
{
perror("mkfifo");
goto err0;
}
}
fd = open("./myfifo", O_RDONLY);
if(-1 == fd)
{
perror("open myfifo as read-only");
goto err0;
}
while(1)//循环读取管道中的数据
{
memset(buf, 0, SIZE);
ret = -1;
ret = read(fd, buf, SIZE);
if(-1 == ret)
{
perror("read data from myfifo");
close(fd);
return 1;
}
puts(buf);
}
return 0;
err0:
return 1;
}
4-4.有名管道的使用3
两个进程A和B聊天。
A.c
#include<stdio.h>
#include<sys/types.h>
#include<string.h>
#include<sys/stat.h>
#include<fcntl.h>
#define SIZE (128)
int main(void)
{
int ret = 0;
int fd = -1;
int fd2 = -1;
char buf[SIZE] = {0};
if(0 != access("./fifo_A", F_OK))
{
ret = mkfifo("./fifo_A", 0644);
if(0 != ret)
{
perror("create fifo_A");
goto err0;
}
}
if(0 != access("./fifo_B", F_OK))
{
ret = mkfifo("./fifo_B", 0644);
if(0 != ret)
{
perror("create fifo_B");
goto err0;
}
}
fd = open("./fifo_A", O_WRONLY);//此处会默认阻塞等待别处以只读方式打开(如果这里先只写打开的话),再继续往下执行
if(-1 == fd)
{
perror("open fifo_A as write-only");
goto err0;
}
fd2 = open("./fifo_B", O_RDONLY);//同理
if(-1 == fd2)
{
perror("open fifo_B as read-only");
goto err1;
}
puts("ok, Let's talk:");
//printf("ok, Let's talk:\n");
//return 1;
while(1)//先写再读
{
memset(buf, 0, SIZE);
if(NULL == fgets(buf, SIZE, stdin))
{
perror("get datas from terminal");
}
else
{
ret = write(fd, buf, strlen(buf));
if(-1 == ret)
{
perror("write data to fifo_A");
}
}
memset(buf, 0, SIZE);
ret = read(fd2, buf, SIZE);
if(-1 == ret)
{
perror("read data from fifo_B");
}
else
{
puts(buf);
}
}
close(fd);
close(fd2);
return 0;
err1:
close(fd);
err0:
return 1;
}
B.c
#include<stdio.h>
#include<sys/types.h>
#include<string.h>
#include<sys/stat.h>
#include<fcntl.h>
#define SIZE (128)
int main(void)
{
int ret = 0;
int fd = -1;
int fd2 = -1;
char buf[SIZE] = {0};
if(0 != access("./fifo_A", F_OK))
{
ret = mkfifo("./fifo_A", 0644);
if(0 != ret)
{
perror("create fifo_A");
goto err0;
}
}
if(0 != access("./fifo_B", F_OK))
{
ret = mkfifo("./fifo_B", 0644);
if(0 != ret)
{
perror("create fifo_B");
goto err0;
}
}
fd = open("./fifo_A", O_RDONLY);//此处会默认阻塞等待别处以只写方式打开(如果这里先只读打开的话),再继续往下执行
if(-1 == fd)
{
perror("open fifo_A as write-only");
goto err0;
}
fd2 = open("./fifo_B", O_WRONLY);//同理
if(-1 == fd2)
{
perror("open fifo_B as read-only");
goto err1;
}
puts("ok, Let's talk:");
while(1)//先读再写
{
memset(buf, 0, SIZE);
ret = read(fd, buf, SIZE);
if(-1 == ret)
{
perror("read data from fifo_A");
}
else
{
puts(buf);
}
memset(buf, 0, SIZE);
if(NULL == fgets(buf, SIZE, stdin))
{
perror("get datas from terminal");
}
else
{
ret = write(fd2, buf, strlen(buf));
if(-1 == ret)
{
perror("write data to fifo_B");
}
}
}
close(fd);
close(fd2);
return 0;
err1:
close(fd);
err0:
return 1;
}
4-5.有名管道的使用4
两个进程A和B聊天,加入线程防阻塞。
A.c
#include<stdio.h>
#include<sys/types.h>
#include<string.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<pthread.h>
#define SIZE (128)
void* writeData(void* arg)
{
int ret = -1;
int fd = *(int*)arg;
char buf[SIZE] = {0};
while(1)
{
memset(buf, 0, SIZE);
if(NULL == fgets(buf, SIZE, stdin))
{
perror("get datas from terminal");
}
else
{
ret = write(fd, buf, strlen(buf));
if(-1 == ret)
{
perror("write data to fifo_A");
}
}
//usleep(200 * 1000);
}
return NULL;
}
int main(void)
{
int ret = 0;
int fd = -1;
int fd2 = -1;
char buf[SIZE] = {0};
pthread_t writeThread;
if(0 != access("./fifo_A", F_OK))
{
ret = mkfifo("./fifo_A", 0644);
if(0 != ret)
{
perror("create fifo_A");
goto err0;
}
}
if(0 != access("./fifo_B", F_OK))
{
ret = mkfifo("./fifo_B", 0644);
if(0 != ret)
{
perror("create fifo_B");
goto err0;
}
}
fd = open("./fifo_A", O_WRONLY);//此处会默认阻塞等待别处以只读方式打开(如果这里先只写打开的话),再继续往下执行
if(-1 == fd)
{
perror("open fifo_A as write-only");
goto err0;
}
fd2 = open("./fifo_B", O_RDONLY);//同理
if(-1 == fd2)
{
perror("open fifo_B as read-only");
goto err1;
}
puts("ok, Let's talk:");
ret = pthread_create(&writeThread, NULL, (void*)writeData, (void*)&fd);//创建线程循环向A写入
if(0 != ret)
{
perror("pthread_create");
goto err2;
}
while(1)//循环从B读取
{
memset(buf, 0, SIZE);
ret = read(fd2, buf, SIZE);
if(-1 == ret)
{
perror("read data from fifo_B");
}
else
{
printf("B:%s", buf);
}
//usleep(200 * 1000);
}
close(fd);
close(fd2);
return 0;
err2:
return ret;
err1:
close(fd);
err0:
return 1;
}
B.c
#include<stdio.h>
#include<sys/types.h>
#include<string.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<pthread.h>
#define SIZE (128)
void* readData(void* arg)
{
int ret = -1;
int fd = *(int*)arg;
char buf[SIZE] = {0};
while(1)
{
memset(buf, 0, SIZE);
ret = read(fd, buf, SIZE);
if(-1 == ret)
{
perror("read data from fifo_A");
}
else
{
printf("A:%s", buf);
}
//usleep(200*1000);
}
return NULL;
}
int main(void)
{
int ret = 0;
int fd = -1;
int fd2 = -1;
char buf[SIZE] = {0};
pthread_t readThread;
if(0 != access("./fifo_A", F_OK))
{
ret = mkfifo("./fifo_A", 0644);
if(0 != ret)
{
perror("create fifo_A");
goto err0;
}
}
if(0 != access("./fifo_B", F_OK))
{
ret = mkfifo("./fifo_B", 0644);
if(0 != ret)
{
perror("create fifo_B");
goto err0;
}
}
fd = open("./fifo_A", O_RDONLY);//此处会默认阻塞等待别处以只写方式打开(如果这里先只读打开的话),再继续往下执行
if(-1 == fd)
{
perror("open fifo_A as write-only");
goto err0;
}
fd2 = open("./fifo_B", O_WRONLY);//同理
if(-1 == fd2)
{
perror("open fifo_B as read-only");
goto err1;
}
puts("ok, Let's talk:");
ret = pthread_create(&readThread, NULL, (void*)readData, (void*)&fd);//创建线程循环从A读取
if(0 != ret)
{
perror("pthread_create");
goto err2;
}
while(1)//循环向B写入
{
memset(buf, 0, SIZE);
if(NULL == fgets(buf, SIZE, stdin))
{
perror("get datas from terminal");
}
else
{
ret = write(fd2, buf, strlen(buf));
if(-1 == ret)
{
perror("write data to fifo_B");
}
}
//usleep(200*1000);
}
close(fd);
close(fd2);
return 0;
err2:
return ret;
err1:
close(fd);
err0:
return 1;
}
5.内存映射
1.映射函数mmap的使用
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
int main(void)
{
int fd = -1;
int len = 0;
void *addr = NULL;
//打开文件
fd = open("txt", O_RDWR | O_CREAT, 0644);
//fd = open("txt", O_RDONLY);
if (-1 == fd){
perror("open");
goto err0;
}
//获取文件长度
len = lseek(fd, 0, SEEK_END);
if (-1 == len){
perror("lseek");
goto err1;
}
printf("fd:%d len: %d\n", fd, len);
//映射
//MAP_PRIVATE : 对映射区的写入操作会产生一个映射区的复制(copy - on - write), 对此区域所做的修改不会写回原文件。
//addr = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
addr = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (MAP_FAILED == addr){
perror("mmap");
goto err1;
}
//addr++;
printf("映射ok....\n");
//关闭文件
close(fd);
strcpy(addr, "hello itcast");
printf("%s\n", (char*)(addr + 1));
//解除映射
munmap(addr, len);
return 0;
err1:
close(fd);
err0:
return 1;
}
2.用于父子进程通信
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
int main(void)
{
int fd = -1;
int len = 0;
pid_t pid = -1;
void *addr = NULL;
//打开文件
fd = open("txt", O_RDWR | O_CREAT, 0644);
//fd = open("txt", O_RDONLY);
if (-1 == fd)
{
perror("open");
goto err0;
}
//获取文件长度
len = lseek(fd, 0, SEEK_END);
if (-1 == len)
{
perror("lseek");
goto err1;
}
printf("fd:%d len: %d\n", fd, len);
//映射
//MAP_PRIVATE : 对映射区的写入操作会产生一个映射区的复制(copy - on - write), 对此区域所做的修改不会写回原文件。
//addr = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
addr = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (MAP_FAILED == addr)
{
perror("mmap");
goto err1;
}
//addr++;
printf("映射ok....\n");
//关闭文件
close(fd);
//创建一个子进程
pid = fork();
if (-1 == pid)
{
perror("fork");
goto err0;
}
//子进程
if (0 == pid)
{
//写映射区
strcpy(addr, "hello itcast");
printf("子进程退出....\n");
//解除映射
munmap(addr, len);
exit(0);
}
//等待子进程退出
wait(NULL);
//父进程
printf("%s\n", (char*)addr);
//解除映射
munmap(addr, len);
return 0;
err1:
close(fd);
err0:
return 1;
}
3.普通进程间通信
3.1 read.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
int main(void)
{
int fd = -1;
int len = 0;
void *addr = NULL;
//打开文件
fd = open("txt", O_RDWR | O_CREAT, 0644);
if (-1 == fd)
{
perror("open");
goto err0;
}
//获取文件长度
len = lseek(fd, 0, SEEK_END);
if (-1 == len)
{
perror("lseek");
goto err1;
}
printf("fd:%d len: %d\n", fd, len);
//映射
//MAP_PRIVATE : 对映射区的写入操作会产生一个映射区的复制(copy - on - write), 对此区域所做的修改不会写回原文件。
//addr = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
addr = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (MAP_FAILED == addr)
{
perror("mmap");
goto err1;
}
//addr++;
printf("映射ok....\n");
//关闭文件
close(fd);
printf("addr: %s\n", (char*)addr);
//解除映射
munmap(addr, len);
return 0;
err1:
close(fd);
err0:
return 1;
}
3.2 write.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
int main(void)
{
int fd = -1;
int len = 0;
void *addr = NULL;
//打开文件
fd = open("txt", O_RDWR | O_CREAT, 0644);
//fd = open("txt", O_RDONLY);
if (-1 == fd)
{
perror("open");
goto err0;
}
//获取文件长度
len = lseek(fd, 0, SEEK_END);
if (-1 == len)
{
perror("lseek");
goto err1;
}
printf("fd:%d len: %d\n", fd, len);
//映射
//MAP_PRIVATE : 对映射区的写入操作会产生一个映射区的复制(copy - on - write), 对此区域所做的修改不会写回原文件。
//addr = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
addr = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (MAP_FAILED == addr)
{
perror("mmap");
goto err1;
}
//addr++;
printf("映射ok....\n");
//关闭文件
close(fd);
strcpy(addr, "hello itcast");
//解除映射
munmap(addr, len);
return 0;
err1:
close(fd);
err0:
return 1;
}
4.匿名映射
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
int main(void)
{
int len = 0;
pid_t pid = -1;
void *addr = NULL;
len = 1024;
//创建一个匿名映射
addr = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
if (MAP_FAILED == addr)
{
perror("mmap");
goto err1;
}
//addr++;
printf("创建匿名映射ok....\n");
//创建一个子进程
pid = fork();
if (-1 == pid)
{
perror("fork");
goto err0;
}
//子进程
if (0 == pid)
{
//写映射区
strcpy(addr, "hello itcast");
printf("子进程退出....\n");
//解除映射
munmap(addr, len);
exit(0);
}
//等待子进程退出
wait(NULL);
//父进程
printf("%s\n", (char*)addr);
//解除映射
munmap(addr, len);
return 0;
err1:
err0:
return 1;
}