首页 > 系统相关 >Linux系统编程——进程控制

Linux系统编程——进程控制

时间:2022-11-08 20:06:13浏览次数:41  
标签:fork Linux int 编程 pid printf 进程 include


在学习Linux系统编程总结了笔记,并分享出来。

09-linux-day05(进程控制)

目录:一、学习目标二、进程1、进程和程序2、单道和多道程序设计3、进程的状态转化4、MMU 的作用5、PCB的概念6、获取环境变量7、进程控制函数fork8、fork创建子进程9、进程控制的命令10、创建n个子进程11、循环创建n个子进程控制顺序12、父子进程共享的内容13、父子进程不共享全局变量14、execlp函数介绍15、exec函数规律16、exel实现自定义程序17、孤儿进程与僵尸进程18、wait函数简单使用和说明19、wait回收并且查看死亡原因20、waitpid回收子进程21、用wait回收多个子进程22、waitpid回收多个子进程

一、学习目标

1、了解进程相关的概念

2、掌握fork/getpid/getppid函数的使用

3、熟练掌握ps/kill命令的使用

4、熟练掌握execl/execlp函数的使用

5、说出什么是孤儿进程什么是僵尸进程

6、熟练掌握wait函数的使用

7、熟练掌握waitpid函数的使用

二、进程

1、进程和程序

什么是程序?编译好的二进制文件。

什么是进程?运行着的程序。

  站在程序员的角度:运行一系列指令的过程。

  站在操作系统角度:分配系统资源的基本单位。

区别:

  程序占用磁盘,不占用系统资源。

  进程占用系统资源。

  一个程序对应多个进程,一个进程对应一个程序。

   程序没有生命周期,进程有生命周期。

2、单道和多道程序设计



Linux系统编程——进程控制_python



3、进程的状态转化



Linux系统编程——进程控制_嵌入式_02



进程的状态切换



Linux系统编程——进程控制_嵌入式_03



4、MMU 的作用

》MMU作用:1)虚拟内存和物理内存的映射;2)修改内存访问级别



Linux系统编程——进程控制_java_04




Linux系统编程——进程控制_java_05



用户空间映射到物理内存是独立的。

5、PCB的概念

Linux内核的进程控制块是task_struct结构体。

查找结构体:

> sudo grep -rn "struct task_struct {" /usr/

技巧:光标停留在{上,按%,可以到结构体的结尾。(400多行)



Linux系统编程——进程控制_java_06





Linux系统编程——进程控制_嵌入式_07



查看进程的资源上限:

>ulimit -a

6、获取环境变量



Linux系统编程——进程控制_python_08



查看所有的环境变量:(写法:key=value,且等号两端不能有空格)

>env

常见的环境变量:



Linux系统编程——进程控制_java_09





Linux系统编程——进程控制_嵌入式_10



>echo $HOME

>echo $PATH

》getenv函数——获取环境变量

man 2 getenv

char *getenv(const char *name);

>touch getenv.c

>vi getenv.c



1 #include<stdio.h>
2 #include<stdlib.h>
3
4 int main()
5 {
6 printf("homepath is [%s]\n", getenv("HOME"));
7 return 0;
8 }



>make

>./getenv

》setenv函数——设置环境变量的值

man 2 setenv

int setenv(const char *name, const char *value, int overwrite);

  参数overwrite取值:1-覆盖原环境变量;0-不覆盖(该参数常用于设置新环境变量。)

一般配置(.bashrc)文件!(如:export key=val;)

》unsetenv函数——删除环境变量name的定义

man 2 unsetenv

int unsetenv(const char *name);

  注意事项:name不存在仍返回0(成功),当name命名为“ABC=”时则会出错!

7、进程控制函数fork



Linux系统编程——进程控制_操作系统_11



>touch fork.c

>vi fork.c



1 #include<stdio.h>
2 #include<unistd.h>
3
4 int main()
5 {
6 printf("Begin ....\n");//此处缺少“\n”结果会不一样,这是由于printf有缓冲区机制,往屏幕上输出,会有行缓冲
7 pid_t pid = fork();
8 printf("End ....\n");
9 return 0;
10 }



>make

>./fork

(可以知道结束了两个进程。)

》getpid获得当前进程的PID和进程ID,,getppid——获得当前进程父进程的ID

man getpid

pid_t getpid(void);

pid_t getppid(void);

8、fork创建子进程

>vi fork.c



1 #include<stdio.h>
2 #include<unistd.h>
3 #include<stdlib.h>
4
5 int main()
6 {
7 printf("Begin ....\n");
8 pid_t pid = fork();
9 if(pid < 0){
10 perror("fork err");
11 exit(1);
12 }
13 if(pid == 0){
14 //子进程
15 printf("I am a child,pid = %d,ppid = %d\n",getpid(), getppid());
16 }
17 else if(pid > 0){
18 //父进程的逻辑
19 printf("childpid=%d,self=%d,ppid=%d\n",pid,getpid(),getppid());
20 sleep(1);//让父进程等待一段时间,再死去。
21 }
22
23 printf("End ....\n");
24 return 0;
25 }



>make

>./fork

9、进程控制的命令

>vi fork.c



1 #include<stdio.h>
2 #include<unistd.h>
3 #include<stdlib.h>
4
5 int main()
6 {
7 printf("Begin ....\n");
8 pid_t pid = fork();
9 if(pid < 0){
10 perror("fork err");
11 exit(1);
12 }
13 if(pid == 0){
14 //子进程
15 printf("I am a child,pid = %d,ppid = %d\n",getpid(), getppid());
16 while(1){
17 printf("I am a child\n");
18 sleep(1);
19 }
20 }
21 else if(pid > 0){
22 //父进程的逻辑
23 printf("childpid=%d,self=%d,ppid=%d\n",pid,getpid(),getppid());
24 while(1){
25 sleep(1);
26 }
27 }
28
29 printf("End ....\n");
30 return 0;
31 }



>make

>./fork

让子进程一直保持运行,打开另一个终端查看PPID和PID分析:

》ps aux——查看进程相关信息的指令

》ps ajx——可以查看更多进程相关的信息,从而追溯进程之间的血缘关系



Linux系统编程——进程控制_java_12



可以看到PPID、PID,可以看出shell进程是所有进程的父亲,继续追溯,可以看到最终父进程是init

(init进程是所有进程的祖先!)

>kill -9 2890

>ps ajx

杀死了父进程,init成为子进程2891新的父亲。

>kill 2891

杀死子进程

》kill指令——给进程发送一个信号

  kill -l 查看信号相关的信息

  SIGKILL 9号信号

  kill -9 pid——杀死进程



Linux系统编程——进程控制_linux_13



10、创建n个子进程

需求:让父进程创建n个子进程

>touch nfork.c

>vi nfork.c



1 #include<stdio.h>
2 #include<unistd.h>
3 #include<stdlib.h>
4
5 int main()
6 {
7 int n = 5;
8 int i = 0;
9 pid_t pid = 0;
10 for(i = 0; i < 5; i++){//父进程循环结束
11 pid = fork();
12 if(pid == 0){
13 //son
14 printf("I am child, pid=%d,ppid=%d\n",getpid(),getppid());
15 //break;//子进程退出循环的接口
16 }
17 else if(pid > 0){
18 //father
19 printf("I am father, pid=%d,ppid=%d\n",getpid(),getppid());
20 }
21 }
22 while(1){
23 sleep(1);
24 }
25 return 0;
26 }



>gcc nfork.c

>./a.out

>ps aux | grep a.out |grep -v grep | wc -l

(创建了32个进程!)

11、循环创建n个子进程控制顺序

》需求:精确控制各个子进程

>touch nfork1.c

>vi nfork1.c



1 #include<stdio.h>
2 #include<unistd.h>
3 #include<stdlib.h>
4
5 int main()
6 {
7 int n = 5;
8 int i = 0;
9 pid_t pid = 0;
10 for(i = 0; i < 5; i++){//父进程循环结束
11 pid = fork();
12 if(pid == 0){
13 //son
14 printf("I am child, pid=%d,ppid=%d\n",getpid(),getppid());
15 break;//子进程退出循环的接口
16 }
17 else if(pid > 0){
18 //father
19 //printf("I am father, pid=%d,ppid=%d\n",getpid(),getppid());
20 }
21 }
22
23 sleep(i);
24 if(i < 5){
25 printf("I am child, will exit,pid=%d,ppid=%d\n",getpid(),getppid());
26 }
27 else{
28 //father
29 printf("I am parent, will out,pid=%d,ppid=%d\n",getpid(),getppid());
30 }
31
32 return 0;
33 }



>gcc nfork1.c

>./a.out

(创建了5个进程!)

12、父子进程共享的内容



Linux系统编程——进程控制_操作系统_14





Linux系统编程——进程控制_java_15



13、父子进程不共享全局变量

>touch shared.c

>vi shared.c



1 #include<stdio.h>
2 #include<unistd.h>
3 #include<stdlib.h>
4
5 int var = 100;
6
7 int main()
8 {
9 pid_t pid = fork();
10 if(pid == 0){
11 //son
12 printf("var = %d,child,pid=%d,ppid=%d\n",var,getpid(),getppid());
13 var = 1001;
14 printf("var = %d,child,pid=%d,ppid=%d\n",var,getpid(),getppid());
15 }
16 else if(pid > 0){
17 //parent
18 sleep(1);//保证子进程能够修改var的值成功
19 printf("var = %d,parent,pid=%d,ppid=%d\n",var,getpid(),getppid());
20 }
21
22 return 0;
23 }



(让子进程改,看结果)

>gcc shared.c

>./a.out

(让父进程改,看结果)

>vi shared.c



1 #include<stdio.h>
2 #include<unistd.h>
3 #include<stdlib.h>
4
5 int var = 100;
6
7 int main()
8 {
9 pid_t pid = fork();
10 if(pid == 0){
11 //son
12 printf("var = %d,child,pid=%d,ppid=%d\n",var,getpid(),getppid());
13 var = 1001;
14 printf("var = %d,child,pid=%d,ppid=%d\n",var,getpid(),getppid());
15 sleep(3);
16 printf("var = %d,child,pid=%d,ppid=%d\n",var,getpid(),getppid());
17 }
18 else if(pid > 0){
19 //parent
20 sleep(1);//保证子进程能够修改var的值成功
21 printf("var = %d,parent,pid=%d,ppid=%d\n",var,getpid(),getppid());
22 var = 2000;
23 printf("var = %d,parent,pid=%d,ppid=%d\n",var,getpid(),getppid());
24 }
25
26 return 0;
27 }



>gcc shared.c

>./a.out

》结论:即使是全局变量,父子进程也不共享。(读时共享,写时复制。)

14、execlp函数介绍



Linux系统编程——进程控制_python_16



>man execl

》execl作用:执行其他程序

int execl(const char *path,const char *arg,...) /*(char *)NULL*/

》exelp作用:执行程序的时候,使用PATH环境变量,执行的程序可以不用加路径

int execlp(const char *file,const char *arg,...) /*(char *)NULL */

  file 要执行的程序

  arg 参数列表

  参数列表最后需要一个NULL作为结尾(哨兵)

  返回值:只有失败才返回

>touch execl.c

>vi execl.c



1 #include<stdio.h>
2 #include<unistd.h>
3
4 int main()
5 {
6 //int execlp(const char *file,const char *arg,...) /*(char *)NULL */
7 execlp("ls", "ls", "-l", "--color=auto",NULL);//如果使其执行错误,返回,把第一个ls改为非法的lsxxx
8 //不需要判断返回值
9 perror("exec err");
10 return 0;
11 }



>gcc execl.c

>./a.out

>vi execl.c



1 #include<stdio.h>
2 #include<unistd.h>
3
4 int main()
5 {
6 //int execlp(const char *file,const char *arg,...) /*(char *)NULL */
7 //execlp("ls", "ls", "-l", "--color=auto",NULL);
8 execl("/bin/ls", "ls", "-l", "--color=auto",NULL);
9 //不需要判断返回值
10 perror("exec err");
11 printf("hello\n");
12 return 0;
13 }



>gcc execl.c

>./a.out

15、exec函数规律



Linux系统编程——进程控制_python_17





Linux系统编程——进程控制_操作系统_18





Linux系统编程——进程控制_嵌入式_19



16、exel实现自定义程序



Linux系统编程——进程控制_linux_20





Linux系统编程——进程控制_嵌入式_21



>touch fpe.c

>vi fpe.c



1 #include<stdio.h>
2 #include<unistd.h>
3
4 int main()
5 {
6 int a = 10;
7 int b = a/0;
8 return 0;
9 }



>make

>./fpe

(报错:浮点数例外(核心已转储))

>touch execl.c

>vi execl.c



1 #include <stdio.h>
2 #include <unistd.h>
3
4 int main()
5 {
6 execl("./fpe", "fpe", NULL);
7
8 printf("bye bye!\n");
9 return 0;
10 }



>gcc execl.c

>./a.out

(报错:浮点数例外(核心已转储))

17、孤儿进程与僵尸进程

》孤儿进程:父亲死了,子进程被init进程领养。

》僵尸进程:子进程死了,父进程没有回收子进程的资源(PCB)。



Linux系统编程——进程控制_嵌入式_22



>touch orphan.c

>vi orphan.c



1 #include<stdio.h>
2 #include<unistd.h>
3
4 int main()
5 {
6 pid_t pid = fork();
7 if(pid == 0){
8 //子进程
9 while(1){
10 printf("I am a child,pid=%d,ppid=%d\n",getpid(), getppid());
11 sleep(1);
12 }
13 }
14 else if(pid > 0){
15 //父进程的逻辑
16 printf("I am parent,pid=%d,ppid=%d\n",getpid(),getppid());
17 sleep(5);
18 printf("I am parent,I will die!\n")
19 }
20
21 printf("End ....\n");
22 return 0;
23 }



>gcc orphan.c

>./a.out

(Ctrl+c无法结束子进程,因为子进程脱离shell,只能打开另一个终端,ps ajx查看pid,然后用kill -9 pid杀死)

>touch zoombie.c

>vi zoombie.c



1 #include<stdio.h>
2 #include<unistd.h>
3
4 int main()
5 {
6 pid_t pid = fork();
7 if(pid == 0){
8 //子进程
9 printf("I am child,pid=%d,ppid=%d\n",getpid(), getppid());
10 sleep(2);
11 printf("I am child,I will die\n");
12 }
13 else if(pid > 0){
14 //父进程的逻辑
15 while(1){
16 printf("I am parent,pid=%d\n",getpid());
17 sleep(1);
18 }
19 }
20 return 0;
21 }



>gcc orphan.c

>./a.out

(打开另一个终端查看 >ps aux | grep a.out)

(如果想研究下,使用man ps查看,然后用/zombie定位到)

》如何回收僵尸进程资源?

杀死父亲,init领养,负责回收。

18、wait函数简单使用和说明



Linux系统编程——进程控制_linux_23



》wait作用:1)阻塞等待;2)回收子进程资源;3)知道子进程的死亡原因

man 2 wait

pid_t wait(int *status);

  status 传出参数

  返回值:成功返回终止的子进程ID,失败返回-1

子进程的死亡原因:

  正常死亡 WIFEXITED

    如果WIFEXITED为真,使用WEXITSTATUS得到退出状态。

  非正常死亡WIFSIGNALED

    如果WIFSIGNALED为真,使用WTERMSIG得到信号

19、wait回收并且查看死亡原因

>touch wait.c

>vi wait.c



1 #include<stdio.h>
2 #include<unistd.h>
3 #include<sys/types.h>
4 #include<sys/wait.h>
5
6 int main()
7 {
8 pid_t pid = fork();
9 if(pid == 0){
10 //子进程
11 printf("I am child,I will die\n");
12 sleep(2);
13 while(1){//迁怒,等待kill杀死,非正常死亡
14 printf("I am child,guo lai da wo!\n");
15 sleep(1);
16 }
17 //正常死亡的两种情况
18 //return 101;
19 //exit(101);
20 }
21 else if(pid > 0){
22 //父进程的逻辑
23 printf("I am parent,wait for child die!\n");
24 int status;
25
26 pid_t wpid = wait(&status);
27 printf("wait ok,wpid=%d,pid=%d\n",wpid,pid);
28 if(WIFEXITED(status)){
29 printf("child exit with %d\n",WEXITSTATUS(status));
30 }
31 if(WIFSIGNALED(status)){
32 printf("child killed by %d\n",WTERMSIG(status));
33 }
34 while(1){
35 sleep(1);
36 }
37 }
38 return 0;
39 }



>gcc wait.c

>./a.out

(打开另一个终端,使用ps aux|grep a.out查看pid,然后用kill -9 pid杀死子进程,原终端将会收到:child killed by 9;用kill pid杀死子进程,原终端将会收到:child killed by 15)

20、waitpid回收子进程

man waitpid

pid_t waitpid(pid_t pid, int *status, int options);

  pid

    <-1 组id

    1 回收任意

    0 回收和调用进程组id相同组内的子进程

    >0 回收指定的pid

  options

    0与wait相同,也会阻塞

    WNOHANG 如果当前没有子进程退出的,会立刻返回

  返回值

    如果设置了WNOHANG,那么如果没有子进程退出,返回0

      如果有子进程退出,返回退出的pid

    失败返回-1(没有子进程)

>touch waitpid.c

>vi waitpid.c



1 #include<stdio.h>
2 #include<unistd.h>
3 #include<sys/types.h>
4 #include<sys/wait.h>
5 #include<stdlib.h>
6
7 int main()
8 {
9 pid_t pid = fork();
10 if(pid == 0){
11 //子进程
12 printf("I am child,pid=%d\n",getpid());
13 sleep(2);
14 }
15 else if(pid > 0){
16 //父进程的逻辑
17 printf("I am parent,pid=%d\n",getpid());
18
19 int ret;
20 while((ret = = waitpid(-1,NULL,WNOHANG)) == 0){
21 sleep(1);
22 }
23 printf("ret = %d\n",ret);
24 ret = waitpid(-1,NULL,WNOHANG);
25 if(ret < 0){
26 perror("wait err");
27 }
28 while(1){
29 sleep(1);
30 }
31 }
32 return 0;
33 }



>gcc waitpid.c

>./a.out

21、用wait回收多个子进程

>touch nfork_wait.c

>vi nfork_wait.c



1 #include<stdio.h>
2 #include<unistd.h>
3 #include<sys/types.h>
4 #include<sys/wait.h>
5 #include<stdlib.h>
6
7 int main()
8 {
9 int n = 5;
10 int i = 0;
11 pid_t pid;
12 for(i = 0; i < 5; i++){
13 pid = fork();
14 if(pid == 0){
15 printf("I am child,pid=%d\n",getppid());
16 break;
17 }
18 }
19 sleep(i);
20 if(i == 5){
21 for(i = 0; i < 5; i++){
22 pid_t wpid = wait(NULL);
23 printf("wpid = %d\n",wpid);
24 }
25 while(1){
26 sleep(1);
27 }
28 }
29 return 0;
30 }



>gcc nfork_wait.c

>./a.out

(打开另一个终端,使用ps aux|grep a.out查看pid,看回收是否成功)

22、waitpid回收多个子进程

>touch nfork_waitpid.c

>vi nfork_waitpid.c



1 #include<stdio.h>
2 #include<unistd.h>
3 #include<sys/types.h>
4 #include<sys/wait.h>
5 #include<stdlib.h>
6
7 int main()
8 {
9 int n = 5;
10 int i = 0;
11 pid_t pid;
12 for(i = 0; i < 5; i++){
13 pid = fork();
14 if(pid == 0){
15 printf("I am child,pid=%d\n",getppid());
16 break;
17 }
18 }
19 if(i == 5){
20 //parent
21 printf("I am parent!\n");
22 //如何使用waitpid回收?-1代表子进程都死了,都回收了
23 while(1){
24 pid_t wpid = waitpid(-1, NULL, WNOHANG);
25 if(wpid == -1){
26 break;
27 }
28 else if(wpid > 0){
29 printf("waitpid wpid=%d\n",wpid);
30 }
31 }
32 while(1){
33 sleep(1);
34 }
35 }
36 if(i < 5){
37 //sleep(i);
38 printf("I am child,i = %d,pid=%d\n",i,getpid());
39 }
40 return 0;
41 }



>gcc nfork_waitpid.c

>./a.out

(打开另一个终端,使用ps aux|grep a.out查看pid,看回收是否成功)

》作业



Linux系统编程——进程控制_python_24



>touch fork_write.c

>vi fork_write.c



1 #include<stdio.h>
2 #include<unistd.h>
3 #include<sys/types.h>
4 #include<sys/wait.h>
5 #include<stdlib.h>
6 #include<fcntl.h>
7 #include<string.h>
8
9 int main(int argc,char *argv[])
10 {
11 if(argc != 2){
12 printf("./a.out filename\n");
13 return -1;
14 }
15 int fd = open(argv[1],O_RDWR);
16 if(fd < 0){
17 perror("open err");
18 exit(1);
19 }
20 pid_t pid = fork();
21 if(pid == 0){
22 //son
23 write(fd,"hello\n",6);
24 }
25 else if(pid > 0){
26 //parent
27 sleep(1);
28 write(fd,"world\n",6);
29 wait(NULL);
30 }
31
32 return 0;
33 }



>touch 11

>make

>ls -lrt

>./fork_write 11

>cat 11



1 hello
2 world



(没有覆盖,说明写时共享文件描述符,读写位置一样,一个进程写了,读写位置改变了,另一个进程受影响,所以可以跟着写。)

在学习Linux系统编程总结了笔记,并分享出来。

标签:fork,Linux,int,编程,pid,printf,进程,include
From: https://blog.51cto.com/u_15405812/5834768

相关文章

  • Linux系统编程——信号
    在学习Linux系统编程总结了笔记,并分享出来。09-linux-day07(信号)目录:一、学习目标二、进程通信——信号1、信号的概念回顾2、阻塞信号集、未决信号集、信号产生3、raise和a......
  • Linux系统编程——进程间通信
    在学习Linux系统编程总结了笔记,并分享出来。09-linux-day06(进程间通信)目录:一、学习目标二、进程通信——管道1、管道的概念2、管道通信举例3、父子进程实现ps、grep命令4......
  • Linux命令基础——makefile+gdb+IO
    在学习Linux命令基础总结了笔记,并分享出来。08-linux-day03(makefile-gdb-IO)目录:附:ftp工具介绍——FlashFXP一、学习目标二、makefile1、makefile编写12、makefile编写23、......
  • Linux命令基础——08-linux-day02(vim-gcc-library)
    在学习Linux命令基础总结了笔记,并分享出来。08-linux-day02(vim-gcc-library)目录:一、学习目标二、vim1、vim光标的移动2、vim删除内容3、vim复制粘贴与可视模式4、vim查找......
  • Linux命令基础——stat-readdir-dup2
    在学习Linux命令基础总结了笔记,并分享出来。08-linux-day04(stat-readdir-dup2)目录:一、学习目标二、文件和目录操作1、打开最大文件数量2、stat函数介绍3、stat函数介绍2与......
  • Linux命令基础——vim+gcc+ibrary
    在学习Linux命令基础总结了笔记,并分享出来。08-linux-day02(vim-gcc-library)目录:一、学习目标二、vim1、vim光标的移动2、vim删除内容3、vim复制粘贴与可视模式4、vim查找......
  • Linux高并发网络编程开发——tcp三次握手-并发
    在学习Linux高并发网络编程开发总结了笔记,并分享出来。10-Linux系统编程-第11天(tcp三次握手-并发)  一、学习目标1、熟练掌握三次握手建立连接过程2、熟练掌握四次挥手断开......
  • Linux 中使用脚本启动 Java 服务
    Linux中使用脚本启动Java服务#!/bin/sh#服务启动参数#JAVA_OPTS="-Xms512m-Xmx512m-XX:MetaspaceSize=512m-XX:MaxMetaspaceSize=1024m-XX:ParallelGCThreads=......
  • 面向对象编程(四)
    面向对象编程(四)一、面向对象的魔法方法1.魔法方法简介在类中,有一些内置好的特定的方法,方法名是“__xx__”,在进行特定的操作时会被调用,这些方法被称为魔法方法,不需......
  • dotnet Core 在linux 下设置成Service
    1、新建.service文件cd/etc/systemd/system//进入改目录touchCore.service//新建Core服务文件viCore.service//编辑2、插入下面代码注意自己的服务名,以及项......