栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 系统运维 > 运维 > Linux

关于进程和线程的

Linux 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

关于进程和线程的

介绍
  • 本文是我在学习进程时总结的一些笔记,后续还会更新,我会把我遇到的一些问题也会提出来,也会对一些知识点进行总结,或者以画图方式进行理解,如果你也在学习进程知识,可以拿以参考。如果过程中发现我的错误,请务必私信我哈,非常感谢!!!

  • 加油,屏幕对面的你‍

进程 进程

!!!这块笔记来自b站一个老师的视频,老师讲的非常有思路,告诉你进程从出生到牺牲都经历了什么‍点我跳转!!!

资源分配的单位(搞清楚进程的资源,就搞清楚了进程)

进程在Linux中的数据结构

PCB(进程控制块)


  • pstree 树形式,显示进程关系
  • Linux中用三种不同数据结构来描述它,在不同场景都能更高效(空间换时间)
进程生命周期


僵尸

      僵尸、僵尸的清除

#include 
#include 
#include 
#include 

int main(void)
{
	pid_t pid,wait_pid;
	int status;

	pid = fork();

	if (pid==-1)	{
		perror("Cannot create new process");
		exit(1);
	} else 	if (pid==0) {
		printf("child process id: %ldn", (long) getpid());
		pause();
		_exit(0);
	} else {
#if 0 
		printf("ppid:%dn", getpid());
		while(1);
#endif
		do {
			wait_pid=waitpid(pid, &status, WUNTRACED | WCONTINUED);

			if (wait_pid == -1) {
				perror("cannot using waitpid function");
				exit(1);
			}

			if (WIFEXITED(status))
				printf("child process exites, status=%dn", WEXITSTATUS(status));

			if(WIFSIGNALED(status))
				printf("child process is killed by signal %dn", WTERMSIG(status));

			if (WIFSTOPPED(status))
				printf("child process is stopped by signal %dn", WSTOPSIG(status));

			if (WIFCONTINUED(status))
				printf("child process resume running....n");

		} while (!WIFEXITED(status) && !WIFSIGNALED(status));

		exit(0);
	}
}
  • 子进程死后,父进程收尸(#if 0)
  • 子进程死后,父进程不给予处理(#uf 1)

    执行 ps aux 查看父、子进程情况

    这时候,把子进程的父进程杀死,僵尸就没了(原因往后看)

    再执行ps aux父、子进程就都没了

暂停和继续进程

#include

int main(int argc, const char *argv[])
{
	int i=0;

	while(1){
		volatile j;
		for (i=0; i < 100000; i++){
			printf("666 %dn", j++);
		}
	}
	return 0;
}
  • Ctrl +C 、killall a.out
    杀掉进程a.out
  • Ctrl + Z 暂停
  • fg(front) 进程放在前台继续执行
  • bg (back) 进程放在后台继续执行
  • ‍点我了解前边三个命令
  • 后台进程的终止

方法一:
  通过jobs命令查看job号(假设为num),然后执行kill %num
方法二:
  通过ps命令查看job的进程号(PID,假设为pid),然后执行kill pid

前台进程的终止:

ctrl+c

kill的其他作用
  kill除了可以终止进程,还能给进程发送其它信号,使用kill -l 可以察看kill支持的信号。

  • cpulimit -l 10 -p pid号 把某个进程的CPU利用率限制为10%

  • 问题

执行a.out,后cpu利用率总是在3%左右,但是程序在飞快运行。执行cpulimit依然没效果

睡眠

  • 深度睡眠视频里会说
内存泄漏

进程管理 别人的
  • ‍点我查看进程管理笔记
  • ‍课程同步笔记
我的

      查看系统中所有进程

  • ps aux
    查看系统中所有进程,使用BSD操作系统格式。(Unix)
  • ps -le
    查看系统中所有进程,使用Linux标准命令格式。
    ps aux 输出信息

      查看系统健康状态

  • -d 秒数: 指定top命令每隔几秒更新。默认是3秒 在top命令的交互模式当中可以执行的命令
  • ?或h: 显示交互模式的帮助
  • P: 以CPU使用率排序,默认就是此项
  • M: 以内存的使用率排序
  • N: 以PID排序
  • q: 退出top

      查看、修改进程优先级

命令作用
nice按用户指定的优先级,运行进程
renice改变正在运行进程的优先级
  • 注意:
    进程的nice值默认是0,范围为- 20~20,nice值越小则优先级越高。普通用户设置的nice值只能为0 ~ 20,且只能增加nice值

  • nice的应用

先创建一个睡眠的进程

#include
#include
#include

int main(){
	printf("Hi bro!!n");

	while(1){
		usleep(100000); 
	}
	_exit(0);
}

然后

linux@linux:~/Lesson/level5/day1$ gcc -o nice nice.c //1.编译
linux@linux:~/Lesson/level5/day1$ nice -n 19 ./nice //2.修改优先级后运行
Hi bro!!
^C
linux@linux:~/Lesson/level5/day1$ ps -ef|grep nice //3.查看PID(进程的ID)
linux 18325 18036 0 06:23 pts/0 00:00:00 grep --color=auto nice
linux@linux:~/Lesson/level5/day1$ top -p 18325 //4.根据PID查看它的NI(优先级)是否修改

      问题

  • 修改优先级这块我的现象不对,需要问老师
进程相关函数 fork


示例

#include 
#include 
#include  

int main(void)
{
	pid_t pid,wait_pid;
	int status;

	pid = fork();

	if (pid==-1)	{
		perror("Cannot create new process");
		exit(1);
	} else 	if (pid==0) {
		printf("an");
	} else {
		printf("bn");
	}

	printf("cn");
	while(1);
}
exit/_exit/return

描述

C 库函数 void exit(int status) 立即终止调用进程。任何属于该进程的打开的文件描述符都会被关闭,该进程的子进程由进程 1 继承,初始化,且会向父进程发送一个 SIGCHLD 信号。

函数原型

#include 
#include 
void exit(int status);//用stdlib.h头文件
void _exit(int status);//用unistd.h头文件
  • exit结束后会刷新流的缓冲区

参数

  • status – 返回给父进程的状态值。

返回值

  • 该函数不返回值。

示例

#include 
#include 

int main ()
{
   printf("程序的开头....n");
   
   printf("退出程序....n");
   exit(0);

   printf("程序的结尾....n");

   return(0);
}

程序的开头....
退出程序....

exec

exec族函数函数的作用

  • 我们用fork函数创建子进程后,由于子进程和父进程的内容一样,没必要存在两个一样的,所以我们经常会在子进程中调用exec函数去执行另外一个程序。
  • 当进程调用exec函数时,子进程被完全替换为新程序。
  • 因为调用exec函数并不创建新进程,所以前后进程的ID并没有改变。

exec族函数定义

      功能:

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

      函数原型:

#include 
int execl(const char *path,const *arg,...);//arg...传递给执行的程序的参数列表
int execlp(const char *file,const char *arg,...);//arg...传递给执行的程序的参数列表

int execv(const char *path,char *const argv[]);//arg...封装成指针数组的形式
int execvp(const char*file,char *const argv[]);//arg...封装成指针数组的形式 

      参数:

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

      返回值:

  • exec函数族的函数执行成功后不会返回,调用失败时,会设置errno并返回-1,然后从原程序的调用点接着往下执行。

示例

      执行ls命令,显示/etc目录下所有文件的详细信息

方式1:execl
if(execl("/bin/ls","ls","-a","-l","/etc",NULL)<0{//一定要以NULL结尾,并判断函数是否执行成功
	perror("execl");
}	

方式2:execlp
if(execlp("ls","ls","-a","-l","/etc",NULL)<0{//会自动在PATH路劲搜索
	perror("execlp");
}	
方式3:execv
char *arg[]={"ls","-a","-l","/etc",NULL};//将要传递的参数放在数组中
if(execv("/bin/ls",arg)<0){
	perror("execv");
}
方式3:execvp
if(execvp("ls",arg)<0){
	perror("execvp");//会自动在PATH路径中搜索
}

system

函数原型

#include 
int system(const char *command)

返回值:

  • 成功时返回command的返回值;失败时返回EOF(自动创建一个子进程,子进程执行命令)
  • 当前进程(父进程)等待command执行结束后才继续执行。

示例

#include
#include

int main(){

	system("ls -a -l ./");

	printf("Hello!n");

	return 0;
}

linux@linux:~/Lesson/level5/day2$ gcc system.c
linux@linux:~/Lesson/level5/day2$ ./a.out
total 24
drwxrwxr-x 2 linux linux 4096 Oct 10 09:29 .
drwxrwxr-x 4 linux linux 4096 Oct 10 08:49 ..
-rwxrwxr-x 1 linux linux 7331 Oct 10 09:29 a.out
-rw-rw-r-- 1 linux linux 480 Oct 10 09:29 exec.c
-rw-rw-r-- 1 linux linux 130 Oct 10 09:29 system.c
Hello!

wait/waitpid

功能

  • 回收进程
  • 子进程结束时由父进程回收
  • 孤儿进程由init进程回收
  • 若孤儿进程没有回收会出现僵尸进程

wait 函数原型

#include 
pid_t wait(int *status);

参数

  • status指定保存子进程返回值和结束方式的地址

返回值:

  • 成功时返回回收子进程的进程号;失败时返回EOF

  • 若子进程没有结束,父进程一直阻塞

  • 若有多个子进程,那个进程先结束就先回收

  • status为NULL表示直接释放子进程PCB,不接收返回值

示例

#include
#include
#include

int main()
{
	int status;
	pid_t pid, pr;

	pid = fork();
	if (pid < 0){
		perror("fork");
		exit(-1);
	}
	else if(pid == 0){
		sleep(1);
		exit(1);
	}
	else{
		pr = wait(NULL); //不用&status,不关心程序怎么结束的
		printf("process %d is over!n", pr);
	}

	return 0;
}
宏标识符作用
WIFEXITED(status)判断子进程是否正常结束
WEXITSTATUS(status)获得子进程的返回值
WIFSIGNALED(status)判断子进程是否被信号结束
WTERMSIG(status)获取结束子进程的信号类型
  • 父进程用宏标识符,通过status判断子进程的状态

示例

#include
#include
#include

int main(int argc, const char *argv[])
{
	int status;
	pid_t pid, pr;

	pid = fork();
	if (pid < 0){
		perror("fork");
		exit(-1);
	}else if (pid == 0){
		printf("the PID of son process is %dn", getpid());
		sleep(1);
		while(1);  //让父进程一直等待信号,此时子进程处于僵尸态
// 		exit(2);
	}else{

		
#if 0
		printf("the PID of father process is %dn", getpid());
		while(1);
#endif
		pr = wait(&status);

		if (pr == -1){
			perror("con't use wait functionn");
			exit(1);
		}
		if (WIFEXITED(status)){ //子进程是否正常结束
			printf("%d is finished normally, status = %dn", pr, WEXITSTATUS(status));
		}
		else if(WIFSIGNALED(status)){ //子进程是否被信号结束
			printf("%d is killed by signal %dn", pr, WTERMSIG(status));	
		}
		exit(0);
	}

	return 0;
}

总结

exit 函数主要就是把进程结束后(释放资源后),向父进程发送一个 SIGCHLD 信号
这时父进程里正在阻塞的wait 会获取到这个信号,把僵尸进程(释放资源后的进程) 的PCB释放,使得进程彻底消失
status 是父进程从子进程捕捉到的子进程的状态----子进程是如何结束的,一般我们只需让进程消失就行了,不用管它的状态

waitpid函数原型

#include  

#include 

pid_t waitpid(pid_t pid,int *status,int options)
  • 从本质上讲,系统调用waitpid和wait的作用是完全相同的,但waitpid多出了两个可由用户控制的参数pid和options,从而为我们编程提供了另一种更灵活的方式。

参数

  • pid:从参数的名字pid和类型pid_t中就可以看出,这里需要的是一个进程ID。但当pid取不同的值时,在这里有不同的意义。
  • pid>0时,只等待进程ID等于pid的子进程,不管其它已经有多少子进程运行结束退出了,只要指定的子进程还没有结束,waitpid就会一直等下去。
  • pid=-1时,等待任何一个子进程退出,没有任何限制,此时waitpid和wait的作用一模一样。
  • pid=0时,等待同一个进程组中的任何子进程,如果子进程已经加入了别的进程组,waitpid不会对它做任何理睬。
  • pid<-1时,等待一个指定进程组中的任何子进程,这个进程组的ID等于pid的绝对值。
  • options:options提供了一些额外的选项来控制waitpid,目前在Linux中只支持WNOHANG和WUNTRACED两个选项,这是两个常数,可以用"|"运算符把它们连接起来使用

ret=waitpid(-1,NULL,WNOHANG | WUNTRACED);
如果我们不想使用它们,也可以把options设为0,如:
ret=waitpid(-1,NULL,0);

  • WNOHANG参数调用waitpid,即使没有子进程退出,它也会立即返回,不会像wait那样永远等下去。
  • WUNTRACED参数,由于涉及到一些跟踪调试方面的知识,加之极少用到,这里就不多费笔墨了,有兴趣的读者可以自行查阅相关材料。

返回值

  • 当正常返回的时候,waitpid返回收集到的子进程的进程ID;
  • 如果设置了选项WNOHANG,而调用中waitpid发现没有已退出的子进程可收集,则返回0;
  • 如果调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误所在

示例

#include
#include
#include
#include

int main(){
	pid_t pc, pr;

	pc = fork();
	if (pc < 0){
		error("fork");
		exit(-1);
	}else if (pc == 0){ 
		sleep(3);
		exit(0);
	}
	
	
    do{
		pr = waitpid(pc, NULL, WNOHANG); //没等到信号过来,返回0
		if (pr == 0){
			printf("Not receive a signal form a childn");
			sleep(1);
		}
	}while(pr == 0); //如果信号一直没收到,就一直循环

	if (pr == pc){
		printf("successfully get a child's signaln");
	}else{
		printf("Errorn");
	}

	return 0;
}
守护进程 概念

守护进程(Daemon进程)是Linux中的后台服务进程。

特点

  • 生存期长
  • 独立于控制终端
  • 周期性执行某种任务或等待处理某些发生的事件
  • 系统启动时开始执行,关闭时终止

作用

当你希望某个进程不因为用户、终端或者其他的变化而受影响,你要把它变成守护进程

终端是什么

终端是每个系统与用户进行交流的界面

作用

  • 进程从一个终端开始运行,它就会依附于这个终端。
  • 这个终端又称为这些进程的“控制终端”
  • 控制终端关闭时,相应的进程都会自动结束
守护进程的创建
  1. 创建子进程,父进程退出
if(fork()>0){
	exit(0);
}
  • 子进程变成孤儿进程,被init进程收养
  • 子进程在后台运行,但是依旧和终端相关联
  1. 子进程创建新会话
if(setsid()<0)//通过setsid创建一个新的会话
{
	exit(-1);
}
  • 子进程成为新的会话组长
  • 子进程脱离原先的终端
  1. 更改当前工作目录
chdir("/");
chdir("/tmp");//所有用户可读可写可执行
  • 守护进程一直在后台运行,其工作目录不能被卸载
  • 重新设定当前工作目录cwd
  1. 重设文件权限掩码
if(umask(0)<0){
	exit(-1);
}
  • 文件权限掩码设置为0
  • 只影响当前进程
  1. 关闭打开的文件描述符
int i;
for(i=0;i 
  • 关闭所有从父进程继承的打开文件
  • 已脱离终端,stdin/stdout/stderr无法再使用

示例:

#include
#include
#include
#include
#include
#include
#include


int main(int argc, const char *argv[]){
	pid_t pid;
	int i,fd;
	char *buf = "This is a Daemonn";

	
	pid = fork();
	if (pid < 0){
		printf("Error forkn");
		exit(1);
	}else if (pid > 0){
		exit(0); // parent out 
	}

	
	setsid();

	
	chdir("/tmp");

	
	umask(0);

	
	for (i=0; i < getdtablesize(); i++){
		close(i);
	}
	
	
	if ((fd = open("daemon.log", O_CREAT|O_WRONLY|O_TRUNC, 0600)) < 0){
		printf("Open file errorn");
		exit(1);
	}

	
	while(1){
		write(fd, buf, strlen(buf));
		sleep(2);
	}
	exit(0);
}
进程创建的写时拷贝技术 Copy-On-Write

#include 
#include 
#include 
#include 

int data = 10;

int child_process()
{
	printf("Child process %d, data %dn",getpid(),data);
	data = 20;
	printf("Child process %d, data %dn",getpid(),data);
	_exit(0);
}

int main(int argc, char* argv[])
{
	int pid;
	pid = fork();

	if(pid==0) {
		child_process();
	}
	else{
		sleep(1);
		printf("Parent process %d, data %dn",getpid(), data);
		exit(0);
	}
}

linux@linux:~/Lesson/level5/day3$ gcc cow.c
linux@linux:~/Lesson/level5/day3$ ./a.out
Child process 4701, data 10
Child process 4701, data 20
Parent process 4700, data 10

vfork
#include 
#include 
#include 

int data = 10;

int child_process()
{
	printf("Child process %d, data %dn",getpid(),data);
	data = 20;
	printf("Child process %d, data %dn",getpid(),data);
	_exit(0);
}

int main(int argc, char* argv[])
{
	if(vfork()==0) {
		child_process();	
	}
	else{
		sleep(1);
		printf("Parent process %d, data %dn",getpid(), data);
	}
}

linux@linux:~/Lesson/level5/day3$ gcc vfork.c
linux@linux:~/Lesson/level5/day3$ ./a.out
Child process 4858, data 10
Child process 4858, data 20
Parent process 4857, data 20

线程


PID和TGID

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/323378.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号