0x00思路
为了给TinyHTTPd源码分析打下基础,先写一个简单父子进程管道通信的样例
1,先定义,并绑定read,write函数和缓冲区,创建pipe管道数组(下标0对于读段,1对应写端,这是强制规定的)
2.父进程fork后,父子进程各有一套1中的变量,且代码执行是同步的,只不过储存空间不在一起,然后对父子进程的管道进行操作使之可以通信
0x01函数
write
#include <unistd.h> ssize_t write(int fd, const void *buf, size_t count);
将buf所指的内存中的count个字节,写入到文件描述符fd所指的文件中去。
read
number = read(handle, buffer ,n);
上述read调用函数中,各个参数的定义如下:
handle: 这是一个已经打开的文件标识符,表示从这个文件句柄所代表的文件读取数据。
buffer: 指缓冲区,即读取的数据会被放到这个缓冲区中去。
n: 表示调用一次read操作,应该读多少数量的字符。
number:表示系统实际所读取的字符数量。
pipe
头文件: #include<unistd.h> 函数原型:int pipe(int filedes[2]);
函数说明:pipe()会建立管道,并将文件描述词由参数filedes数组返回。
filedes[0]为管道里的读取端
filedes[1]则为管道的写入端。
返回值: 若成功则返回零,否则返回-1,错误原因存于errno中。
错误代码:
EMFILE 进程已用完文件描述词最大量
ENFILE 系统已无文件描述词可用。
EFAULT 参数 filedes 数组地址不合法。
fork
头文件:#include <unistd.h> #include<sys/types.h> 函数原型:pid_t fork( void);
创建一个新的进程。(pid_t 是一个宏定义,其实质是int 被定义在#include<sys/types.h>中)
fork后发生了什么
1.由fork创建的新进程被称为子进程(child process)。该函数被调用一次,但返回两次。两次返回的区别是子进程的返回值是0,
2.而父进程的返回值则是新进程(子进程)的进程 id。将子进程id返回给父进程的理由是:因为一个进程的子进程可以多于一个,没有一个函数使一个进程可以获得其所有子进程的进程id。
3.对子进程来说,之所以fork返回0给它,是因为它随时可以调用getpid()来获取自己的pid;也可以调用getppid()来获取父进程的id。(进程id为0的总是由交换进程使用,所以一个子进程的进程id不可能为0)。
4.fork之后,操作系统会复制一个与父进程完全相同的子进程。虽说是父子关系,但是在操作系统看来,他们更像兄弟关系,这2个进程共享代码空间,但是数据空间是互相独立的,子进程数据空间中的内容是父进程的完整拷贝,指令指针也完全相同,子进程拥有父进程当前运行到的位置(两进程的程序计数器pc值相同。也就是说,子进程是从fork返回处开始执行的)
5.但有一点不同,如果fork成功,子进程中fork的返回值是0,父进程中fork的返回值是子进程的进程号,如果fork不成功,父进程会返回错误。
可以这样想象,2个进程一直同时运行,而且步调一致,在fork之后,他们分别作不同的工作,也就是分岔了。
注意:
fork()函数主要是以父进程为蓝本复制一个进程,其ID号和父进程的ID号不同。对于结果fork出来的子进程的父进程ID号是执行fork()函数的进程的ID号;
例如:
父进程, fork返回值是:17025, ID:17024 ,父进程ID:16879
子进程, fork返回值是:0, ID:17025 ,父进程ID:17024
close
用这个函数来关闭管道的一端
0x02重要变量
1.result异常变量,用来进行判断一些函数的异常返回情况
2.pid进程变量(在fork函数的使用过程中很重要)
3.read/write buffer 在文件读写的时候创建缓冲区i
4.fd[2]pipe读写端数组 实现管道通信
0x03遇到的问题
1.对管道通信pipe读写端在不同进程下是怎样的关系不明确
2.fork函数两个返回值的利用实现父子进程过程不清楚
3.为什么TinyHTTPd要两个管道,而这个简单例子只需要一个管道
0x04解决问题
1.见下图
2.见下图
3.因为这个样例不需要双向通信,子进程并没有反馈给父进程
0x05代码
#include<stdio.h> #include<stdlib.h> #include<sys/types.h> #include<unistd.h> #include<string.h> int main(void){ // pid_t pid; int i=0; int result = -1; int fd[2],nbytes; char string[100]; char readbuffer[80]; //int *write_fd = &fd[1]; //int *read_fd = &fd[0]; printf("Please input data:"); scanf("%s",string); result = pipe(fd); !!!!用fd建立了pip管道链接 if(-1 == result) { perror("pipe"); return -1; } /*pid=fork(); !!!!!!!父子进程分开 if(-1 == pid) //此处为了验证父子进程是否创建成功,如果未创建成功,则返回-1 { perror("fork"); return -1; } else if(0 == pid)//如果是子进程 { printf(“this is child %d\n”, getpid()); close(*read_fd);//关掉子进程的读端,只剩下写端 result = write(*write_fd,string,strlen(string));//向string写入,写入到文件描述符pipe写端 return 0; }*/ else//如果不是子进程 { printf(“this is parent %d\n”, getpid()); close(*write_fd);//关闭父进程的pip写端,只留下读端 nbytes = read(*read_fd,readbuffer,sizeof(readbuffer)-1);//从read_fd中读入,先放入readbuffer printf("receive %d data:%s\n",nbytes,readbuffer); } return 0; }