fork函数讲解
- 进程复制
- fork基本使用
- 简单分页 逻辑页 物理页 页表
- fork的三个面试练习题
- 缓冲区
- 僵死进程
- 孤儿进程
- 写时拷贝
- 进程替换
- exexc 介绍
- 简易命令解释器
- strtok()函数讲解
进程复制
fork基本使用
父进程fork后,新的进程产生,新的进程就继续从fork往后的程序往下执行,新的进程不会继续执行fork,父进程中fork复制了该进程,然后fork的返回值是子进程的pid ,子进程中fork的返回值被强制为0,所以可以通过fork的返回值来判断
#include<stdio.h>
#include<unistd.h>
int main(){
int n=0;
char*s=NULL;
pid_t id=fork();
if(id==-1){
printf("fork error\n");
exit(1);
}
if(id==0){
n=3;
s="child";
}
else{
n=7;
s="parent";
}
for(int i=0;i<n;i++){
printf("s=%s,pid=%d\n",s,getpid());
sleep(1);
}
exit(0);
}
for(int i=0;i<n;i++){
printf("s=%s,pid=%d,ppid=%d,n=%d,&n=%p\n",s,getpid(),getppid(),n,&n);
sleep(1);
}
解释一下,为啥在父进程和子进程中n的地址是相同的:fork一份产生子进程,n的位置,即距离起始位置的偏移量恰好和父进程相同,在父进程中n的那块空间存储7,子进程中那块空间存储3,两者互不影响。6
简单分页 逻辑页 物理页 页表
4K为一个页面
程序中看到的都是逻辑地址,实际上是偏移量,调试看到的都是逻辑地址,我们不知道物理地址,每次是映射到物理地址,一般是查表
内核用1G,
fork的三个面试练习题
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
int main(){
fork()||fork();
printf( "1\n");
}
第一个fork执行后,赋值一个进程,在第一个进程中,fork返回值大于0,在或||表达式中,第一个为真,后面就不执行了,直接返回真,子进程中,第一个fork返回值为0,在或||表达式中,第一个为假,后面继续执行,执行fork,这里没有写if语句,往后面执行就行,最终打印111
int main()
{
for(int i = 0 ; i < 2; i++ )
{
fork();
printf("a\n");
}
exit(0);
}
结果为aaaaaa
这个和上面的一个区别就是少了一个\n ,
int main()
{
for(int i = 0 ; i < 2; i++ )
{
fork();
printf("a");
}
exit(0);
}
缓冲区
缓冲区(可以想象成一个字符数组),使程序的开销变小,缓冲区满足三个条件,可以提交
1缓冲区满了,
2或者用户强制刷新(用’\n’),
3程序结束
exit(0);退出进程,
1刷缓冲区,2调用_exit(0)退出进程
fflush(stdout);//stdin stdout stderr
该函数可以和\n达到同样的效果,\n可以刷新屏幕,但是文件不行,文件刷新就只能用fflush(stdout)
fork复制进程的时候,连缓冲区也要复制一份,所以a是8个
僵死进程
子进程先结束,但是父进程没有调用wait获取子进程的退出码,会导致子进程的进程控制块结构体一直在占用id
处理僵死进程
调用wait,把父进程阻塞住了,得到子进程的退出码为3
让父进程死掉,变成孤儿进程,让其他进程接管子进程,
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<stirng.h>
#include<sys/wait.h>
int main(){
int n=0;
char*s=NULL;
pid_t id=fork();
if(id==-1){
printf("fork error\n");
exit(1);
}
if(id==0){
n=3;
s="child";
}
else{
n=7;
s="parent";
int val=0;
pid_t child_pid=wait(&val);
//printf("child_pid=%d,val=%d\n",child_pid,val>>8);
if(WIFEXITED(val)){//是否正常结束
printf("child_pid=%d,val=%d\n",child_pid,WEXITSTATUS(val));//去除退出码
}
for(int i=0;i<n;i++){
printf("s=%s,pid=%d\n",s,getpid());
sleep(1);
}
exit(3);
}
孤儿进程
子进程的父进程结束,该进程就变成了孤儿进程,系统会在给该进程找一个父进程,会自动获取该子进程的退出码
写时拷贝
fork时,先不着急复制,先共享,等到需要修改该页面数据的时候,再复制该页面一份,延迟拷贝
进程替换
exexc 介绍
每一个新的进程产生都是,先复制,再替换
把当前进程替换成另一个进程
没有返回值,替换成功,如果有返回值,说明替换失败,
int execl(const char* path, const char * arg,(char*)0...);
*path:新替换的程序的路径名称
*arg :传给新程序主函数的第一个参数,一般为程序的名字
*arg 后面是剩余参数列表,参数个数可变,必须以空指针作为最后一个参数
int execlp(const char* file, const char * arg,...);
会在bin路径下寻找,能找到就能启动,找不到就失败
简易命令解释器
strtok()函数讲解
char*strtok(char*str,const char*delim);
str是原本的字符串,这里不能是常量,因为会修改,实现机理是将每个分隔符换成’\0’,返回每个字符串的首地址,delim是分隔符,是个常量
strtok里面有个静态偏移量指针,第二次调用时,将NULL传入,就可以通过静态偏移量指针找到目前的位置,继续分隔,当到末尾时,s就返回为空
获取用户的id,返回值为0就是管理员
uid_t getuid(void)
通过uid我们可以获取用户的很多详细信息,都在passwd的结构体里面,
struct passwd*ptr=getpwuid(uid);
用ptr指向这个结构体,可以访问结构体所有的属性,