进程与线程

目   录

一、进程的定义

(1)Tip:进程和程序的区别:

(2)进程是程序执行和资源(内存)管理的最小单位

二、进程的类型

三、进程运行的状态

四、进程的执行模式

五、进程相关的操作(系统调用)

(1)创建进程

​编辑案例1:

案例2:

案例3:

(2)两种异常进程

1、孤儿进程

2、僵尸进程

(3)进程退出

(4)进程阻塞(回收子进程,可以避免僵尸进程)

六、进程的分类之-守护进程

守护进程创建步骤:

七、进程间通信

通信方式

通信的三种模式

(一)无名管道

1.无名管道的相关信息

2.案例:

(二)有名管道

1.有名管道的相关概念

2.有名管道的创建

案例1:

(三)信号

(四)IPC对象

(五)共享内存

(六)消息队列

(七)信号灯

二、线程


* 同一时刻有且只能有一个进程在运行!(针对于单核处理机环境下)

* 双核环境下可以同时有两个进程处于运行态!

一、进程的定义

进程是程序的一次 动态 执行过程

(1)Tip:进程和程序的区别:

进程是 动态的, 它是一些保存在磁盘上的指令的有序集合,没有任何执行的概念
程序是 静态的, 它是程序执行的过程,包括 创建、调度和消亡

(2)进程是程序执行和资源(内存)管理的最小单位

原因:因为每一个进程都有一个0~4G的虚拟内存

二、进程的类型

三、进程运行的状态

进程的运行状态
 
进程的状态之间的转化
 
关于taskstruct结构体:(即进程的相关信息的结构体)

四、进程的执行模式

五、进程相关的操作(系统调用)

同一时刻有且只能有一个进程在运行!(针对于单核处理机环境下)

双核环境下可以同时有两个进程处于运行态!

(1)创建进程

案例1:

#include<stdio.h>
#include<unistd.h>
int main(int argc, const char *argv[])
{
	pid_t pid =fork();
	//报错
	if(pid<0)
	{
		perror("fork error");
	}
	//父进程
	else if(pid>0)
	{
		while(1)
		{
			printf("i am parent\n");
			sleep(1);
		}
	}
	//子进程
	else
	{
		while(1)
		{
			printf("i am child\n");
			sleep(0.5);
		}
	}
	return 0;
}

运行结果 :

* fork创建子进程后,会出现子进程和父进程争夺资源的情况,谁先运行是不确定的

案例2:

#include<sys/types.h>
#include<unistd.h>
#include<stdio.h>

int main(int argc, const char *argv[])
{
	//创建子进程
	printf("111111");
	pid_t pid =fork();
	if(pid<0)
	{
		perror("fork error");
	}
	else if(pid>0)
	{
		//父进程
		while(1)
		{
			printf("i am parent\n");
			sleep(1);
		}
	}
	else 
	{
		while(1)
		{
			printf("i am child\n");
			sleep(1);
		}
	}
	return 0;
}

运行结果:

linux@ubuntu:~/jincheng$ ./a.out
111111i am parent
111111i am child
i am parent
i am child
i am parent
i am child
i am parent
i am child
i am parent
i am child
i am parent

解释:出现两次111111的原因:

  • printf(“1111111”)没有换行符,首先存储在缓冲区
  • 父进程和子进程中的printf有换行符,缓冲区遇到换行符会打印出来;fork之后,进程会连同父进程的缓冲区一块拷贝过来,所以出现两次打印

案例3:

#include<sys/types.h>
#include<unistd.h>
#include<stdio.h>

int main(int argc, const char *argv[])
{
	//创建子进程
	pid_t pid =fork();
	if(pid<0)
	{
		perror("fork error");
	}
	else if(pid>0)
	{
		//父进程
		
		{
			printf("i am parent\n");
		}
	}
	else 
	{
		sleep(3);
		{
			printf("i am child\n");
			sleep(1);
		}
	}
	printf("222222222\n");
	while(1);	
	return 0;
}

运行结果:

linux@ubuntu:~/jincheng$ ./a.out
i am parent
222222222
i am child
222222222

结论:同一时刻只能有一个进程。所以parent打印完到打印22222;接着child打印完到22222

(2)两种异常进程

1、孤儿进程

概念:

父进程先于子进程退出,子进程会被systemd收养(ubuntu16.04之前init收养),变为后台进程

案例:

#include<sys/types.h>
#include<unistd.h>
#include<stdio.h>

int main(int argc, const char *argv[])
{
	//创建子进程
	pid_t pid =fork();
	if(pid<0)
	{
		perror("fork error");
	}
	else if(pid>0)
	{
		//父进程
		
		{
			printf("i am parent\n");
		}
	}
	else 
	{
		sleep(3);
		printf("i am child\n");
		while(1);
		
	}
	return 0;
}

运行结果:

./a.out

linux@ubuntu:~/jincheng$ ./a.out
i am parent

ps -axj

2、僵尸进程

概念子进程先退出,父进程没有回收子进程的资源(task_struct结构体),子进程就变成僵尸进程

注意:父进程没有退出,子进程就会一直保持僵死状态,直到父进程退出,子进程会被systemd回收

案例:

#include<sys/types.h>
#include<unistd.h>
#include<stdio.h>

int main(int argc, const char *argv[])
{
	//创建子进程
	pid_t pid =fork();
	if(pid<0)
	{
		perror("fork error");
	}
	else if(pid>0)
	{
		//父进程
		
		{
			sleep(1);
			printf("i am parent\n");
			while(1);
		}
	}
	else 
	{
		printf("i am child\n");
	}
	return 0;
}

运行结果:

linux@ubuntu:~/jincheng$ ./a.out
i am child
i am parent

tip:

图1-孤儿进程

图2-僵尸进程

(3)进程退出

案例:

#include<sys/types.h>
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
int main(int argc, const char *argv[])
{
	//创建子进程
	pid_t pid =fork();
	if(pid<0)
	{
		perror("fork error");
	}
		//父进程
	else if(pid>0)
	{
		int i=0;
		while(1)
		{
			if(i>3)
			{
				printf("111111");
				exit(0);//刷新缓冲区
			}
			i++;
			printf("i am parent\n");
			sleep(1);
		}
	}
	else 
	{
		while(1)
		{
		
			printf("i am child\n");
			sleep(1);
		}
	}
	return 0;
}

运行结果:

linux@ubuntu:~/jincheng$ i am child
i am child
i am child
i am child
i am child
i am child
i am child
i am child
i am child
i am child
i am child
Li am child
i am child
i am child
i am child
i am child
……

该进程为后台进程,kill  -9 PID可将其杀死

(4)进程阻塞(回收子进程,可以避免僵尸进程)

参数中的*status是一个整形指针,用来表示子进程退出时的状态

  • status为空,表示忽略子进程的退出状态
  • status为其他值,表示保存子进程的退出状态

六、进程的分类之-守护进程

守护进程创建步骤:

案例:

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(void)
{
//(1)创建子进程,让父进程退出
pid_t pid = fork();
if(pid < 0)
{
perror("fork error");
return -1;
}
else if(pid > 0)
{
//退出
exit(0);
}
//(2)创建新的会话
int ret = setsid();
if(ret < 0)
{
perror("setsid error");
return -1;
}
//修改当前目录为根目录
chdir("/");
//修改文件权限掩码
umask(0);
//关闭文件描述符
int i;
for(i = 0; i < getdtablesize(); i++)
{
close(i);
}
3、通信相关概念
3.1 通信的模式
//写日志
//打开文件
int fw = open("daemon.log",O_WRONLY | O_CREAT,0777);
if(fw < 0)
{
perror("open error");
return -1;
}
//操作文件
while(1)
{
ret = write(fw,"hello",5);
if(ret > 0)
{
printf("write ok!\n");
}
sleep(1);
}
return 0;
}

运行:

sudo ./a.out

结果:TTY变成?控制终端变成守护进程

TTY:终端的次要装置号码,示例中的TTY列都是“?”,是表示这些进程不属于任何TTY,因为它们是由系统启动的,tty1-tty6 是本机上面的登入者程序,若为 pts/0 等等的,则表示为由网络连接进主机的程序。

七、进程间通信

  • 通信方式

通信的方式
  • 通信的三种模式

通信的三种模式

(一)无名管道

1.无名管道的相关信息

2.案例:

(1)关闭无名管道的 写端,读管道不会阻塞,返回0

#include <stdio.h>
#include <sys/types.h>
#include<unistd.h>
int main()
{
	int fd[2];
	//创建无名管道
	int ret =pipe(fd);
	if(ret<0)
	{
		perror("pipe error");
		return -1;
	}
	//管道的写端
	close(fd[1]);
	//读管道
	char buf[128]={0};
	ret=read(fd[0],buf,sizeof(buf));
	if(ret<0)
	{
		perror("read error");
		return -1;
	}
	else if(ret ==0)
	{
		printf("read return 0\n");
		close(fd[0]);
		return 0;
	}
	else
	{
		printf("ret=%d,buf=%s\n",ret,buf);
	}
	close(fd[0]);
	return 0;

}

(2)向无名管道写入数据,读取数据

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
int main()
{
	int fd[2];
	//创建无名管道
	int ret =pipe(fd);
	if(ret<0)
	{
		perror("pipe error");
		return -1;
	}
	//向管道写入数据
	char buf[100]="hello world";
	write(fd[1],buf,strlen(buf));
	close(fd[1]);
	//读管道
//	char buf1[100]={0};
//	ret=read(fd[0],buf1,strlen(buf));
	ret=read(fd[0],buf,strlen(buf));
	if(ret<0)
	{
		perror("read error");
		close(fd[0]);
		return -1;
	}
	else if(ret ==0)
	{
		printf("read return 0\n");
		close(fd[0]);
		return 0;
	}
	else
	{
		printf("ret=%d,buf=%s\n",ret,buf);
	}
	close(fd[0]);
	return 0;

(3)测试管道破裂

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>
#include <stdlib.h>

int main()
{
//创建无名管道
	int fd[2]={-1,-1};
	int ret =  pipe(fd);
	if(ret <0)
	{
		perror("pipe error");
		return -1;
	}
	//关闭读段
	close(fd[0]);
	//创建子进程
	pid_t pid =fork();
	if(pid < 0)
	{
		perror("fork error");
		return -1;
	}	
	//父进程
	else if(pid > 0)
	{
		int status = 0;
		ret = wait(&status);
		if(ret > 0)
		{
			if(WIFEXITED(status))
			{
				printf("terminated normally %d\n",WEXITSTATUS(status));
			}
			else if(WIFSIGNALED(status))
			{
				printf("terminated by signal%d\n",WTERMSIG(status));
			}

		}
	}
	else
	{
		//子进程
		char buf[100]={0};
		printf("w:");
		fgets(buf,sizeof(buf),stdin);
		write(fd[1],buf,sizeof(buf));
		exit(3);
	}

	return 0;
}
*为什么无名管道只能用于具有亲缘关系间的通信?
子进程会继承父进程的文件描述符, 得到同一个文件描述符 之后就可以通信了

(二)有名管道

1.有名管道的相关概念

2.有名管道的创建

案例1:
#include<unistd.h>
#include<fcntl.h>
#include<sys/types.h>
#include<stdio.h>
#include<errno.h>

int main()
{
	char buf[100]={0};
	//创建有名管道
	int ret = mkfifo("test.fifo",0644);
	if(ret < 0 && EEXIST != errno)
	{
		perror("mkfifo error");
		return -1;
	}
	//打开文件
	int fw = open("test.fifo",O_WRONLY);
	if(fw < 0)
	{
		perror("open error");
		return -1;
	}
	//操作文件
	printf("w:");
	fgets(buf,sizeof(buf),stdin);
	write(fw,buf,sizeof(buf));
	//关闭文件
	close(fw);
	return 0;
}

只有读端或者写端存在时,系统会阻塞,直到下一个程序的到来会打破这种阻塞

(三)信号

(四)IPC对象

(五)共享内存

       查看共享内存命令:ipcs

(六)消息队列

(七)信号灯

二、线程

相关推荐

  1. 进程线

    2024-03-15 01:10:02       21 阅读
  2. 线进程

    2024-03-15 01:10:02       12 阅读
  3. 进程线(Thread)

    2024-03-15 01:10:02       19 阅读
  4. 线进程学习笔记

    2024-03-15 01:10:02       29 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-03-15 01:10:02       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-03-15 01:10:02       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-03-15 01:10:02       19 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-03-15 01:10:02       20 阅读

热门阅读

  1. 模块化(理解)

    2024-03-15 01:10:02       23 阅读
  2. 深入理解 MySQL 中的 CASE 语句:从基础到实战

    2024-03-15 01:10:02       21 阅读
  3. 配置 conda为国内源

    2024-03-15 01:10:02       19 阅读
  4. git 批量clone,pull 项目

    2024-03-15 01:10:02       18 阅读
  5. python--运算符和字符串

    2024-03-15 01:10:02       22 阅读
  6. SpringBoot如何修改pom依赖的默认版本号

    2024-03-15 01:10:02       19 阅读
  7. vue3中子级页面绑定调用父级页面得自定义事件

    2024-03-15 01:10:02       18 阅读
  8. 力扣爆刷第95天之hot100五连刷61-65

    2024-03-15 01:10:02       16 阅读
  9. sql语句

    2024-03-15 01:10:02       16 阅读