进程概念
- 用户角度:进程是程序一次动态的执行过程。
- 操作系统:进程是操作系统分配资源的最小单位。
硬件上有中断技术出现,产生了分时系统。
进程有自己的运行状态和地址空间(虚拟地址空间)。
描述进程—PCB
进程信息被放在一个叫进程控制块的数据结构中,可以理解为进程属性的集合。
Linux操作系统下的PCB是:task_struct
tast_struct内容分类:
- 标识符
- 状态
- 优先级
- 程序计数器
- 内存指针
- 上下文数据
- I/O状态信息
- 记账信息
- 其他信息
进程和程序区别:
程序:完成特定任务的一系列指令的有序集合。
进程与程序的区别;
- 进程是动态的运行过程,程序是静止的。
- 进程有PCB.
- 进程是短暂的,程序相对永久。
- 一个进程只能对应一个程序,一个程序可以对应多个进程。
创建进程
fork():
- 分配进程标识符
- 创建PCB
- 复制父进程的环境
- 给子进程分配内存空间,将父进程的数据拷贝到该地址空间
- 将子进程置为就绪状态,放入就绪队列。
代码:
1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<unistd.h>
4
5 int main()
6 {
7 int ret=fork();
8 if(ret<0)
9 {
10 perror("fork");
11 return 1;
12 }
13 else if(ret==0)//子进程
14 {
15 printf(" i am child %d!,ret :%d\n",getpid(),ret);
16 }
17 else
18 {
19
20 printf(" i am father %d!,ret :%d\n",getpid(),ret);
21 }
22 sleep(1);
23 return 0;
24 }
vfork():创建子进程,子进程和父进程共享地址空间,保证子进程先运行,在它调用exec或exit(0)之后父进程才能被调度。
1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<unistd.h>
4
5 int main()
6 {
7 pid_t pid;
8 pid=fork();
9 if(pid<0)
10 {
11 perror("fork"),exit(1);
12 }
13 else if(pid==0)//子进程
14 {
15 // sleep(5);
16 printf(" i am child %d\n",getpid());
17 }
18 else//父进程
19 {
20
21 printf(" i am father %d\n",getpid());
22 }
23
24 exit(0);
25 }
进程终止
正常终止:
- 从main返回
- 调用exit
1 #include<stdio.h>
2 #include<unistd.h>
3 #include<stdlib.h>
4 void handler()
5 {
6 printf("这是一个锦囊\n");
7 }
8 void handler2()
9 {
10
11 printf("这是一个锦囊2\n");
12 }
13 int main()
14 {
15 atexit(handler);
16 atexit(handler2);
17 printf("hehe\n");
18 fprintf(stderr,"error");
19 sleep(2);
20 exit(0);
21 }
22
23
- _exit
调用_exit函数
1 #include<stdio.h>
2 #include<unistd.h>
3 #include<stdlib.h>
4 void handler()
5 {
6 printf("这是一个锦囊\n");
7 }
8 void handler2()
9 {
10
11 printf("这是一个锦囊2\n");
12 }
13 int main()
14 {
15 atexit(handler);
16 atexit(handler2);
17 printf("hehe\n");
18 fprintf(stderr,"error");
19 sleep(2);
20 _exit(0);
21 }
22
23
由此可以看出exit最后也会调用_exit,但还做了其他工作:
- 执行用户通过atexit或 on_exit定义的清理函数
- 关闭所有打开的流,所有缓存数据均被写入
调用_exit
异常终止:
kill pid:杀死进程
- ctrl+c:信号终止
- abort():使程序不正常结束
进程等待
父进程通过进程等待的方式,回收子进程资源,获取子进程退出信息。
wait
waitpid
- 进程的阻塞等待:
1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<unistd.h>
4 #include<sys/wait.h>
5
6 int main()
7 {
8 pid_t pid=fork();
9 if(pid<0)
10 {
11 perror("fork"),exit(1);
12 }
13 else if(pid==0)//子进程
14 {
15 printf("I am child ,pid is %d\n",getpid());
16 sleep(5);
17 exit(666);
18 }
19 else
20 {
21 int status=0;
22 pid_t ret=waitpid(-1,&status,0);//阻塞式等待,等价于wait(&status)
23 printf("this is a wait test\n");
24 if(WIFEXITED(status)&&ret==pid)
25 {
26 printf("wait child 5s success,child return code id %d\n",WEXITSTATUS(status));
27 }
28 else
29 {
30 printf("wait child failed,return \n");
31 return 1;
32 }
33
34 }
35 return 0;
36 }
- 进程的非阻塞等待:
int main()
7 {
8 pid_t pid=fork();
9 if(pid<0)
10 {
11 perror("fork"),exit(1);
12 }
13 else if(pid==0)//子进程
14 {
15 printf("I am child ,pid is %d\n",getpid());
16 sleep(5);
17 exit(666);
18 }
19 else
20 {
21 int status=0;
22 pid_t ret=0;
23 do
24 {
25 ret=waitpid(-1,&status,WNOHANG);//非阻塞等待,
26 if(ret==0)
27 {
28 printf("child is ruining\n");
29 }
30 sleep(1);
31 }while(ret==0);
32 if(WIFEXITED(status)&&ret==pid)
33 {
34 printf("wait child 5s success,child return code id %d\n",WEXITSTATUS(status));
35 }
36 else
37 {
38 printf("wait child failed,return \n");
39 return 1;
40 }
41
42 }
43 return 0;
44 }
此外wait还可以回收僵尸子进程
所谓僵尸子进程:一个进程被终止,但未被其父进程回收的进程。
waitpid可以被一个进程用来等待他的子进程终止。
进程替换
替换函数
当进程调用一种exec函数时,该进程的用户空间代码和数据完全被新数据替换,从新程序的启动例程开始执行。调用exec并不创建新进程,故调用exec前后该进程ID不变。
例如;execve函数
execve函数加载并运行可执行目标文件filename,且带参数列表argv 和环境变量列表envp,只有调用出错时,返回到调用程序,若调用成功则加载新的程序从启动代码开始执行,不再返回。
argv 变量指向一个以NULL结尾的指针数组,其中每个指针都指向一个参数串;envp变量也指向一个以NULL结尾的指针数组,其中每个指针都指向一个环境变量串,其中每个串都是形如“‘NAME=VALUE”。
1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<unistd.h>
4
5 //l(list):表示参数采用列表
6 //v(vector):参数用数组
7 //p(path):有p自动搜索环境变量PATH
8 //e(env):表示自己维护环境变量
9 int main()
10 {
11 char *const argv[]={"ps","-ef",NULL};
12 char *const envp[]={"PATH=/bin:/usr/bin","TERM=console",NULL};
13 printf("before exec\n");
14 //execl("/bin/ps","ps","-ef",NULL);
15 //带p的:可以使用环境变量PATH,无需写全路径
16 //execlp("ps","ps","-ef",NULL,envp);
17 //带e的,需要自己组装环境变量
18 //execle("ps","ps","-ef",NULL,envp);
19 //execv("/bin/ps",argv);
20 //execvp("ps",argv);
21 execve("/bin/ps",argv,envp);
22
23 printf("thanks to \n");
24 exit(0);
25 }
用进程相关操作实现简易的myshell
1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<unistd.h>
4 #include<sys/wait.h>
5 #include<string.h>
6 #include<ctype.h>
7
8 #define MAXLINE 1024
9
10 char *argv[8];
11 int argc;
12 char cmdline[MAXLINE+1];
13 int init()
14 {
15 argc=0;
16 memset(cmdline,0x00,sizeof(cmdline));
17 }
18 int read_cmd()
19 {
20 return fgets(cmdline,MAXLINE,stdin)==NULL ? 0:1;
21 }
22 int parse_cmd()
23 {
24 int flag=0;
25 int i;
26 for(i=0;cmdline[i]!='\0';i++)
27 {
28 if(flag==0&&!isspace(cmdline[i]))
29 {
30 flag=1;
31 argv[argc]=cmdline+i;
32 argc++;
33 }
34 else if(isspace(cmdline[i]))
35 {
36 flag=0;
37 cmdline[i]='\0';
38 }
39 }
40 argv[argc]=NULL;
41 }
42 int execute_cmd()
43 {
44 if(fork()==0)
45 {
46 execvp(argv[0],argv);
47 exit(1);
48 }
49 wait(NULL);
50 }
51 void print_cmd()
52 {
53 int i;
54 printf("argc=%d\n",argc);
55 for(i=0;i<argc;i++)
56 {
57 printf("\targv[%d]=%s\n",i,argv[i]);
58 }
59 }
60 int main()
61 {
62 while(1)
63 {
64 init();
65 printf("shell>");
66 if(read_cmd()==0)//从键盘上读取
67 {
68 break;
69 }
70 parse_cmd();//解析
71 print_cmd();
72 execute_cmd();//执行
73 }
74 }