1. fork
1.1 通过 fork 创建的父子进程对于fork之前打开的fd,共享文件偏移量。这是因为,父进程fork一个子进程后,会有自己的进程表项,因此二者各有一套相同的文件描述符表,他们共享了文件表项,因而也就共享了偏移量。此外,close 的关闭采用的是引用计数,当执行close时,是把该fd 窒息那个的的内核中的文件表现的引用计数减1,仅当引用计数为0 时,才是真正的销毁该结构。
#include
#include
#include
#include
#define ERR_EXIT(m) \do { \perror(m);\exit(EXIT_FAILURE);\}while(0)/** 父子继承共享文件偏移量*/int main(int argc, const char *argv[])
{int fd &#61; open("test.txt", O_RDONLY);if(fd &#61;&#61; -1){ERR_EXIT("open");}pid_t pid;if((pid &#61; fork()) <0){ERR_EXIT("fork");}else if(pid &#61;&#61; 0){char buf[10] &#61; {0};read(fd, buf, 3);printf("in Child buf &#61; %s\n", buf);close(fd); //此处引用计数 减 1}else{sleep(3);char buf[10] &#61; {0};read(fd, buf, 3);printf("in parent buf &#61; %s\n", buf);close(fd);}return 0;
}
1.2 目前我们碰到的共享文件偏移量的情况有 2 种&#xff1a;
a&#xff09;通过dup 等手段复制fd&#xff0c;此时两个fd 共享文件偏移量&#xff08;文件表项&#xff09;&#xff1b;
b&#xff09;fork 父子进程&#xff0c;二者共享文件偏移量。
1.3 shell的工作原理&#xff1a;当我们在键盘上敲入”ls”的时候
a)shell&#xff08;bash、zsh&#xff09;先fork一个子进程
b)将子进程的代码使用exec替换为“ls”
c)shell负责该子进程的回收
1.4 对于经典的fork&#43;exec的组合模式&#xff0c;fork出子进程再进行替换&#xff0c;那么复制完整的子进程的地址空间是无意义的。所以提出两种解决方案&#xff1a;
a)vfork&#xff1a;vfork的目的就是为了exec&#xff1b;
b)对于fork采用写时复制技术。
1.5 fork的写时复制技术&#xff1a;
a)fork子进程时&#xff0c;仅仅复制页表项&#xff0c;而不是具体的进程空间。同时将地址空间设为只读。
b)每当任何一方试图修改地址空间时&#xff0c;就自己复制一份。
1.6 写时复制&#xff08;COW&#xff09;使得父子进程&#xff0c;在物理上共享地址空间的&#xff0c;但是在逻辑上地址空间是相互独立的。
2.关于父子进程的处理
2.1 处理僵尸进程的手段&#xff1a;
a)处理SIGCHLD信号&#xff1b;
b)采用wait、waitpid。
2.2 如果没有任何子进程&#xff0c;那么执行wait时&#xff0c;会立刻返回-1&#xff0c;同时errno为ECHLD。否则阻塞&#xff0c;使用WNOHANG可以避免阻塞。
2.3 waitpid不是按照顺序回收子进程。
2.3.1 不按照顺序回收的例子。
#include
#include
#include
#include
#include
#include
#define ERR_EXIT(m) \do { \perror(m);\exit(EXIT_FAILURE);\}while(0)
#define N 10
int main(int argc, const char *argv[])
{int i;pid_t pid;for(i &#61; 0; i
}
2.3.2 若要顺序回收&#xff0c;可以用waitpid 一次等待每个特定的pid&#xff0c;若没有等到&#xff0c;会一直阻塞。
#include
#include
#include
#include
#include
#include
#define ERR_EXIT(m) \do { \perror(m);\exit(EXIT_FAILURE);\}while(0)
#define N 10
/** 顺序回收**/
int main(int argc, const char *argv[])
{int i;pid_t pid[N];for(i &#61; 0; i
}
2.4 system与exec区别&#xff1a;
a)exec替换的是当前进程
b)system则是创建子进程&#xff0c;然后调用exec替换
2.4.1 exec的例子。
#include
#include
#include
#include
#include
#define ERR_EXIT(m) \do { \perror(m);\exit(EXIT_FAILURE);\}while(0)int main(int argc, const char *argv[])
{printf("Enter main\n");execlp("ls", "ls", "-l", NULL);//替换当前子进程printf(" Leave main\n");
}
2.4.2 system的例子。
#include
#include
#include
#include
#include
#define ERR_EXIT(m) \do { \perror(m);\exit(EXIT_FAILURE);\}while(0)int main(int argc, const char *argv[])
{printf("Enter main\n");system("ls -l");printf(" Leave main\n");
}
2.5 system的实现&#xff1a;
a)创建子进程
b)子进程采用exec进行进程替换
c)父进程回收子进程&#xff0c;注意EINTR
2.6 守护进程与普通进程的区别&#xff1a;&#xff08;这个有时间要再看一遍&#xff09;
a)守护进程不属于shell所在的会话组
b)当shell退出的时候&#xff0c;守护进程不受影响