目录
系统调用
PCB进程控制块进程描述符==struct task_struct
fork是写时拷贝技术,提高了效率
open read write close 操作文件的系统调用 实现在内核中
fopen fread fgets fwrite fclse 库函数 /usr/lib/libc.so
file.txt
abcdef
main.c 先open再fork
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<fcntl.h>
#include<assert.h>
int main()
{
int fd=open("file.txt",O_RDONLY);//父进程打开
assert(fd!=-1);
pid_t pid=fork();
assert(pid!=-1);
if(pid==0)
{
char buff[32]={0};
read(fd,buff,1);
printf("child buff=%s\n",buff);
sleep(1);
read(fd,buff,1);
printf("child buff=%s\n",buff);
}
else
{
char buff[32]={0};
read(fd,buff,1);
printf("parent buff=%s\n",buff);
sleep(1);
read(fd,buff,1);
printf("parent buff=%s\n",buff);
}
close(fd);
exit(0);
}
结果
stu@stu-virtual-machine:~/Linux/day09$ cat file.txt
abcdef
stu@stu-virtual-machine:~/Linux/day09$ ./main
parent buff=a
child buff=b
child buff=c
parent buff=d
文件偏移量
先open后fork
父进程打开的文件fork之后会被子进程复制过去(相当于子进程自己没有做open,而是继承了父进程的打开)
main.c 先fork再open
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<fcntl.h>
#include<assert.h>
int main()
{
pid_t pid=fork();
assert(pid!=-1);
int fd=open("file.txt",O_RDONLY);//父进程打开
assert(fd!=-1);
if(pid==0)
{
char buff[32]={0};
read(fd,buff,1);
printf("child buff=%s\n",buff);
sleep(1);
read(fd,buff,1);
printf("child buff=%s\n",buff);
}
else
{
char buff[32]={0};
read(fd,buff,1);
printf("parent buff=%s\n",buff);
sleep(1);
read(fd,buff,1);
printf("parent buff=%s\n",buff);
}
close(fd);
exit(0);
}
结果
stu@stu-virtual-machine:~/Linux/day09$ ./main
parent buff=a
child buff=a
parent buff=b
child buff=b
main.c 打印fd的值
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<fcntl.h>
#include<assert.h>
int main()
{
int fd=open("file.txt",O_RDONLY);//父进程打开
assert(fd!=-1);
printf("fd=%d\n",fd);
pid_t pid=fork();
assert(pid!=-1);
if(pid==0)
{
char buff[32]={0};
read(fd,buff,1);
printf("child buff=%s\n",buff);
sleep(1);
read(fd,buff,1);
printf("child buff=%s\n",buff);
}
else
{
char buff[32]={0};
read(fd,buff,1);
printf("parent buff=%s\n",buff);
sleep(1);
read(fd,buff,1);
printf("parent buff=%s\n",buff);
}
close(fd);
exit(0);
}
结果:fd=3
stu@stu-virtual-machine:~/Linux/day09$ ./main
fd=3
parent buff=a
child buff=b
parent buff=c
child buff=d
PCB里边有文件表—结构体数组(fork后父进程的文件表是什么样子子进程就是什么样子):记录打开的文件
一个进程启动起来,至少打开三个文件:
0 | 标准输入 | stdin |
---|---|---|
1 | 标准输出 | stdout |
2 | 标准错误输出 | stderr |
3 |
文件描述符总是一个非负数,fd至少是3
open之后得到以下:
file.txt这块是指针 浅拷贝
磁盘上的文件都有唯一的id值:ls -i
第一种情况
先open再fork
由图可以看出parent child打印abcd的原因
第二种情况
先fork再open
用户空间、内核空间
系统调用和库函数的区别
书第五章
替换进程
exec 系列
execl execlp execle 库函数
execv execvp 库函数
(上边5个都调用下边这个系统调用)
execve 系统调用
Linux 产生新进程: fork+exec
执行两次 ps -f
stu@stu-virtual-machine:~/Linux/day09$ ps -f
UID PID PPID C STIME TTY TIME CMD
stu 2412 2402 0 14:14 pts/0 00:00:00 bash
stu 2419 2412 0 14:14 pts/0 00:00:00 ps -f
stu@stu-virtual-machine:~/Linux/day09$ ps -f
UID PID PPID C STIME TTY TIME CMD
stu 2412 2402 0 14:14 pts/0 00:00:00 bash
stu 2425 2412 0 14:14 pts/0 00:00:00 ps -f
ps -f的ppid都是bash的pid,bash把自己fork复制一份再exec替换成ps -f(系统中的新进程都是这么来的 eg: ./main)
test.c
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<fcntl.h>
#include<assert.h>
int main()
{
printf("test pid=%d\n",getpid());
execl("/usr/bin/ps","ps","-f",(char*)0);
printf("exec err\n");
exit(0);
}
execl执行成功没有返回值,没成功继续执行原代码
结果
stu@stu-virtual-machine:~/Linux/day09$ ./test
test pid=3071
UID PID PPID C STIME TTY TIME CMD
stu 2412 2402 0 14:14 pts/0 00:00:00 bash
stu 3071 2412 0 14:42 pts/0 00:00:00 ps -f
man 3 exec
execl
int execl(const char* path, const char * arg,...,/*(char*) NULL*/);
path:新替换的程序的路径名称(eg.which ps)
arg :传给新程序主函数的第一个参数,一般为程序的名字
arg 后面是剩余参数列表,参数个数可变,必须以空指针作为最后一个参数
execlp
int execlp(const char* file, const char * arg,...,/*(char*) NULL*/);
自己去找ps的位置
execlp("ps","ps","-f",(char*)0);
execle
int execle(const char* path, const char * arg,...,char* const envp[]);
传环境变量
int main(int argc,char*argv[],char*envp[])
{
printf("test pid=%d\n",getpid());
execle("/usr/bin/ps","ps","-f",(char*)0,envp);
printf("exec err\n");
exit(0);
}
execv
int execv(const char * path, char* const argv[]);
可以写一个char* myargv[10]={“ps”,"-f"};保存
int main(int argc,char*argv[],char*envp[])
{
printf("test pid=%d\n",getpid());
//execl("/usr/bin/ps","ps","-f",(char*)0);
//execlp("ps","ps","-f",(char*)0);
//execle("/usr/bin/ps","ps","-f",(char*)0,envp);
char* myargv[10]={"ps","-f"};
execv("/usr/bin/ps",myargv);
printf("exec err\n");
exit(0);
}
execvp
int execvp(const char * file, char* const argv[]);
eg.
execvp("ps",myargv);
execve
int execve(const char * path, char* const argv[],char* const envp[]);
最终test.c
int main(int argc,char*argv[],char*envp[])
{
printf("test pid=%d\n",getpid());
//execl("/usr/bin/ps","ps","-f",(char*)0);
//execlp("ps","ps","-f",(char*)0);
//execle("/usr/bin/ps","ps","-f",(char*)0,envp);
char* myargv[10]={"ps","-f"};
//execv("/usr/bin/ps",myargv);
//execvp("ps",myargv);
execve("/usr/bin/ps",myargv,envp);
printf("exec err\n");
exit(0);
}