Linux系统编程(2)——进程编程

一、进程关键概念

  1. 什么是程序,什么是进程,有什么区别?
    • 程序是静态的概念,gcc xxx.c –o pro
    • 磁盘中生成pro文件,叫做程序
    • 进程是程序的一次运行活动,通俗点意思是程序跑起来了,系统中就多了一个进程
  2. 如何查看系统中有哪些进程?
    • 使用ps指令查看。 实际工作中,配合grep来查找程序中是否存在某一个进程。
      例如:ps -aux|grep init
    • 使用top指令查看,类似windows任务管理器
  3. 什么是进程标识符?
    • 每个进程都有一个非负整数表示的唯一ID,叫做pid,类似身份证。
    • Pid=0: 称为交换进程(swapper),作用—进程调度。
      Pid=1:init进程,作用—系统初始化。
    • 编程调用getpid函数获取自身的进程标识符
      getppid获取父进程的进程标识符
  4. 什么叫父进程,什么叫子进程
    • 进程A创建了进程B,那么A叫做父进程,B叫做子进程,父子进程是相对的概念,理解为人类中的父子关系。
  5. C程序的存储空间是如何分配?
    在这里插入图片描述
    在这里插入图片描述

二、使用fork函数创建一个进程

  1. 参数说明
    pid_t fork(void); 
    //fork函数调用成功,返回两次
    //返回值为0,     代表当前进程是子进程
    //返回值非负数,代表当前进程为父进程
    //调用失败,返回-1
    
  2. fork创建一个子进程的一般目的
    在这里插入图片描述
    在这里插入图片描述
  3. 源码
    #include <sys/types.h>
    #include <unistd.h>
    #include <stdio.h>
    #include <stdlib.h>
    
    int main()
    {
    	pid_t pid;
    
    	pid = fork();
    
    	if(pid == 0) {
    		while(1) {
    			printf("this is child process, pid = %d\n", getpid());
    			sleep(2);
    		}
    	}
    	else {
    		while(1) {
    			printf("this is father process, pid = %d\n", getpid());
    			sleep(2);
    		}
    	}
    
    	return 0;
    }
    

三、vfork函数也可以创建进程,与fork有什么区别

  1. vfork 直接使用父进程存储空间,不拷贝。
  2. vfork保证子进程先运行,当子进程调用exit退出后,父进程才执行。
  3. 源码:
    #include <sys/types.h>
    #include <unistd.h>
    #include <stdio.h>
    #include <stdlib.h>
    
    int main()
    {
    	pid_t pid;
    	int cnt = 0;
    	pid = vfork();
    
    	if(pid == 0) {
    		while(1) {
    			printf("this is child process, pid = %d\n", getpid());
    			sleep(1);
    			cnt++;
    			if(cnt == 3) {
    				exit(0);
    			}
    		}
    	}
    	else {
    		while(1) {
    			printf("this is father process, pid = %d\n", getpid());
    			sleep(1);
    			printf("cnt = %d\n",cnt);
    		}
    	}
    
    	return 0;
    }
    

四、exit进程退出

  1. 正常退出
    • Main函数调用return
    • 进程调用exit(),标准c库
    • 进程调用_exit()或者_Exit(),属于系统调用
    • 进程最后一个线程返回
    • 最后一个线程调用pthread_exit
  2. 异常退出
    • 调用abort
    • 当进程收到某些信号时,如ctrl+C
    • 最后一个线程对取消(cancellation)请求做出响应
      在这里插入图片描述
  3. 参数说明
    在这里插入图片描述
  4. 源码
    #include <sys/types.h>
    #include <unistd.h>
    #include <stdio.h>
    #include <stdlib.h>
    
    int main()
    {
    	pid_t pid;
    	int cnt = 0;
    	pid = fork();
    
    	if(pid == 0) {
    		while(1) {
    			printf("this is child process, pid = %d\n", getpid());
    			sleep(1);
    			cnt++;
    			if(cnt == 3) {
    				exit(0);
    			}
    		}
    	}
    	else {
    		while(1) {
    			printf("this is father process, pid = %d\n", getpid());
    			sleep(1);
    			printf("cnt = %d\n",cnt);
    		}
    	}
    
    	return 0;
    }
    

五、wait、waitpid等待子进程

  1. 为啥要等待子进程退出
    子进程退出状态不被收集,变成僵死进程(僵尸进程)。
  2. 父进程等待子进程退出,并收集子进程的退出状态,通过WEXITSTATUS()函数进行解析。
    在这里插入图片描述
  3. 相关函数参数说明
    在这里插入图片描述
    在这里插入图片描述区别:waip使调用者阻塞,waitpid有一个选项,可以使调用者不阻塞
    status参数:是一个整型数指针
    非空:子进程退出状态放在它所指向的地址中。
    空:不关心退出状态
    在这里插入图片描述
  4. wait函数源码
    #include <sys/types.h>
    #include <unistd.h>
    #include <stdio.h>
    #include <stdlib.h>
    
    int main()
    {
    	pid_t pid;
    	int cnt = 0;
    	int status = 10;
    	pid = fork();
    
    	if(pid == 0) {
    		while(1) {
    			printf("this is child process, pid = %d\n", getpid());
    			sleep(1);
    			cnt++;
    			if(cnt == 3) {
    				exit(3);
    			}
    		}
    	}
    	else {
    		wait(&status);
    		printf("child process status's = %d\n", WEXITSTATUS(status));
    		while(1) {
    			printf("this is father process, pid = %d\n", getpid());
    			sleep(1);
    			printf("cnt = %d\n",cnt);
    		}
    	}
    
    	return 0;
    }
    
  5. waitpid函数源码
    #include <sys/types.h>
    #include <unistd.h>
    #include <stdio.h>
    #include <stdlib.h>
    
    int main()
    {
    	pid_t pid;
    	int cnt = 0;
    	int status = 10;
    	pid = fork();
    
    	if(pid == 0) {
    		while(1) {
    			printf("this is child process, pid = %d\n", getpid());
    			sleep(1);
    			cnt++;
    			if(cnt == 3) {
    				exit(3);
    			}
    		}
    	}
    	else {
    		waitpid(pid, &status, WNOHANG);	
    		printf("child process status's = %d\n", WEXITSTATUS(status));
    		while(1) {
    			printf("this is father process, pid = %d\n", getpid());
    			sleep(1);
    			printf("cnt = %d\n",cnt);
    		}
    	}
    
    	return 0;
    }
    

六、孤儿进程

父进程如果不等待子进程退出,在子进程之前就结束了自己的“生命”,此时子进程叫做孤儿进程。
Linux避免系统存在过多孤儿进程,init进程收留孤儿进程,变成孤儿进程的父进程。

七、exec族函数

  1. 参考博文https://blog.csdn.net/u014530704/article/details/73848573

  2. exec族函数函数的作用:
    我们用fork函数创建新进程后,经常会在新进程中调用exec函数去执行另外一个程序。当进程调用exec函数时,该进程被完全替换为新程序。因为调用exec函数并不创建新进程,所以前后进程的ID并没有改变。

  3. 功能:
    在调用进程内部执行一个可执行文件。可执行文件既可以是二进制文件,也可以是任何Linux下可执行的脚本文件。

  4. 函数族:
    exec函数族分别是:execl, execlp, execle, execv, execvp, execvpe

  5. 函数原型:

    #include <unistd.h>
    extern char **environ;
    
    int execl(const char *path, const char *arg, ...);
    int execlp(const char *file, const char *arg, ...);
    int execle(const char *path, const char *arg,..., char * const envp[]);
    int execv(const char *path, char *const argv[]);
    int execvp(const char *file, char *const argv[]);
    int execvpe(const char *file, char *const argv[],char *const envp[]);
    
  6. 返回值:
    exec函数族的函数执行成功后不会返回,调用失败时,会设置errno并返回-1,然后从原程序的调用点接着往下执行。

  7. 参数说明:
    path: 可执行文件的路径名字
    arg: 可执行程序所带的参数,第一个参数为可执行文件名字,没有带路径且arg必须以NULL结束
    file: 如果参数file中包含/,则就将其视为路径名,否则就按 PATH环境变量,在它所指定的各目录中搜寻可执行文件。

  8. exec族函数参数极难记忆和分辨,函数名中的字符会给我们一些帮助:
    l : 使用参数列表
    p: 使用文件名,并从PATH环境进行寻找可执行文件
    v: 应先构造一个指向各参数的指针数组,然后将该数组的地址作为这些函数的参数。
    e: 多了envp[]数组,使用新的环境变量代替调用进程的环境变量

  9. 为什么要用exec族函数,有什么作用
    在这里插入图片描述

  10. execl函数源码
    mainecho.c

    #include <stdio.h>
    
    int main(int argc, char *argv[])
    {
    	int i;
    	for(i=0; i<argc; i++) {
    		printf("argv[%d] = %s\n", i, argv[i]);
    	}
    }
    
    
    #include <sys/types.h>
    #include <unistd.h>
    #include <stdio.h>
    #include <stdlib.h>
    
    int main()
    {
    	printf("before execl\n");
    	if(execl("./bin/mainecho", "echoarg", "abc", NULL) == -1) {
    		printf("excel failed\n");
    		perror("why\n");
    	}
    	printf("after execl\n");
    
    
    	return 0;
    }
    
    #include <sys/types.h>
    #include <unistd.h>
    #include <stdio.h>
    #include <stdlib.h>
    
    int main()
    {
    	printf("before execl\n");
    	if(execl("/bin/ls", "-l", NULL) == -1) {
    		printf("excel failed\n");
    		perror("why\n");
    	}
    	printf("after execl\n");
    
    
    	return 0;
    }
    
    
  11. execlp函数源码

    #include <sys/types.h>
    #include <unistd.h>
    #include <stdio.h>
    #include <stdlib.h>
    
    int main()
    {
    	printf("before execl\n");
    	if(execlp("ls", "-l", NULL) == -1) {
    		printf("excel failed\n");
    		perror("why\n");
    	}
    	printf("after execl\n");
    
    
    	return 0;
    }
    
  12. execv函数源码

    #include <sys/types.h>
    #include <unistd.h>
    #include <stdio.h>
    #include <stdlib.h>
    
    int main()
    {
    	char *argv[] = {"/bin/ls","-l", NULL};
    	printf("before execl\n");
    	if(execv("/bin/ls", argv) == -1) {
    		printf("excel failed\n");
    		perror("why\n");
    	}
    	printf("after execl\n");
    
    
    	return 0;
    }
    
  13. execvp函数源码

    #include <sys/types.h>
    #include <unistd.h>
    #include <stdio.h>
    #include <stdlib.h>
    
    int main()
    {
    	char *argv[] = {"ls","-l", NULL};
    	printf("before execl\n");
    	if(execvp("ls", argv) == -1) {
    		printf("excel failed\n");
    		perror("why\n");
    	}
    	printf("after execl\n");
    
    
    	return 0;
    }
    

八、exec配合fork使用

  • 实现功能: 当父进程检测到输入为1的时候,创建子进程把配置文件的字段值修改掉。
  • 源码: Charge_Config.c
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <stdio.h>
    #include <unistd.h>
    #include <string.h>
    #include <stdlib.h>
    
    int main(int argc, char **argv)
    {
    	int fdSrc;
    	char *Readbuf=NULL;
    
    	if(argc != 2) {
    		printf("param error\n");
    		exit(-1);
    	}
    
    	fdSrc = open(argv[1], O_RDWR);
    	int size = lseek(fdSrc, 0, SEEK_END);
    	lseek(fdSrc, 0, SEEK_SET);
    	Readbuf = (char *)malloc((sizeof(char)) * size + 8);
    
    	read(fdSrc, Readbuf, size);
    
    	char *str = strstr(Readbuf, "LENG=");
    	str = str+strlen("LENG=");
    	*str = '5';
    
    	lseek(fdSrc, 0, SEEK_SET);
    
    	write(fdSrc, Readbuf, size);
    
    	close(fdSrc);
    
    	free(Readbuf);
    
    	return 0;
    }
    
  • main.c
    #include <sys/types.h>
    #include <unistd.h>
    #include <stdio.h>
    
    int main()
    {
    	pid_t pid;
    	int data;
    	while(1) {
    		printf("pleas input a data\n");
    		scanf("%d", &data);
    		if(data == 1) {
    			pid = fork();
    			if(pid == 0) {
    				while(1) {
    					printf("this is child process pid = %d\n", getpid());
    					
    					execl("./Change_Config", "Change_Config", "Config", NULL);
    
    				}
    			}
    			else {
    				wait(NULL);
    			}
    		}
    		else {
    			printf("father process donothing\n");
    		}
    	}
    
    	return 0;
    }
    

九、system函数

  1. 函数说明
    在这里插入图片描述
    system()函数的返回值如下:成功,则返回进程的状态值;当sh不能执行时,返回127;失败返回-1。
  2. 函数源码
    #include <sys/types.h>
    #include <unistd.h>
    #include <stdio.h>
    #include <stdlib.h>
    int main()
    {
    	pid_t pid;
    	int data;
    	while(1) {
    		printf("pleas input a data\n");
    		scanf("%d", &data);
    		if(data == 1) {
    			pid = fork();
    			if(pid == 0) {
    				while(1) {
    					printf("this is child process pid = %d\n", getpid());
    					
    					//execl("./Change_Config", "Change_Config", "Config", NULL);
    					system("./Change_Config Config");
    					exit(0);
    				}
    			}
    			else {
    				wait(NULL);
    			}
    		}
    		else {
    			printf("father process donothing\n");
    		}
    	}
    
    	return 0;
    }
    

十、popen函数

  1. 函数说明
    在这里插入图片描述
    比system在应用中的好处: 可以获取运行的输出结果。
  2. 函数源码
    #include <stdio.h>
    //FILE *popen(const char *command, const char *type);
    //size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
    int main(void)
    {
    	FILE *pd;
    	char buf[1024] = {0};
    	pd = popen("ps", "r");
    
    	int nread = fread(buf, 1024, 1, pd);
    
    	printf("nread = %d, buf = \n", nread);
    	printf("%s\n", buf);
    
    	pclose(pd);
    
    	return 0;
    }
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值