嵌入式学习DAY25 --- 进程线程开始学习,进程的基本概念以及创建退出

嵌入式入门学习笔记,遇到的问题以及心得体会!
DAY25

笔记:

并发编程:

同一时刻实现多个任务同时执行,这样的编程称之为并发编程。

1.并发编程的核心:
	进程
	线程
	进程间通信
	线程的同步和互斥
2.多任务的实现:
多任务是通过操作系统的任务调度策略实现并发的。
多进程并发是通过时间片的快速轮转,给用户一个真并发的错觉。
多线程并发在多核cpu机器上,则是以真并发形式存在。
3.任务,进程,线程之间的关系:

任务:
是一个逻辑概念
通常是一个逻辑操作(一个程序的执行)
可以由多个进程/线程组成
线程存在于进程内,不能摆脱进程独立存在
任务可以由一个或多个进程组成

进程:

进程的基本概念

进程:一个具有独立功能的程序在某个数据集上的一次动态执行过程,是操作系统进行资源分配和调度的基本单元。一次任务的运行可以并发激活多个进程,这些进程相互合作来完成该任务的一个最终目标。
简略地说:进程是程序地一次动态执行过程,(仅限于单进程程序。要是多进程的程序就不能这么说了。)

进程是系统分配资源的最小单位(这里说的资源是内存(准确的来说是进程的地址空间))

进程的特点:

1.并发性:
多任务操作系统支持多进程并发执行

2.交互性
可以和用户进行交互

3.异步性
用户可以在不关心进程如何运行时,发送指令后,就能得到想要的结果

4.动态性
进程有生死,有睡眠(阻塞),是动态存在的

5.独立性
多进程之间有自己独立的地址空间
隔离性好

程序和进程的区别:

程序:
程序是静态的一段代码
是一些保存在非易性存储器的指令的有序集合
没有任何执行的概念

进程:
是一个动态的概念
是程序执行的过程,包括动态创建,调度和消亡的整个过程。
它是资源分配的最小单元。

并发编程的核心:

1.程序执行的过程
CPU、总线、内存
三者如何执行指令见计算机组成原理

2.并发编程中的核心问题
>多任务如何共享CPU
单核CPU
多核CPU
>多任务如何共享内存(有操作系统)
默认有一份虚拟地址空间

多进程主要问题是如何共享一份虚拟地址空间
进程间切换:
主要是同一份虚拟地址空间的上下文切换
->保存上文
保存上一个进程的运行状态
->加载下文
加载下一个程序到内存中
进程上下文切换发生在以下几个情况中:
a进程执行完,b进程执行
a进程时间片到了,b进程执行
a进程被中断,优先执行b进程

CPU调度算法:

公平调度策略:
是按照时间片轮转的方法来实现多任务调度

抢占式任务调度:
有优先级,优先级即高的可以抢占优先级低的进程的执行资源。

Linux下进程的分类:

1.交互式进程
2.批处理进程
3.实时进程

进程的地址空间:

Linux操作系统采用的是虚拟内存管理技术,使得每个进程都有独立的地址空间。该地址空间是大小为4G的线性虚拟空间,用户所看到和接触到的都是该虚拟地址。无法看到实际的物理内存地址。利用这种虚拟地址不但更安全(用户不能直接访问物理内存),而且用户程序可以使用比实际物理内存更大的地址空间。
4GB的进程地址空间会被分成两个部分——用户空间与内核空间。用户地址空间是0~3G(0xC0000000),内核地址空间占据3-4GB。用户进程通常情况下只能访问用户空间的虚拟地址,不能访问内核空间虚拟地址。只有用户进程使用系统调用(代表用户进程在内核态执行)时,才可以访问到内核空间。每当进程切换,用户空间就跟着变化;而内核空间是由内核负责用设,它并不会随着进程改变,是固定的。内核空间地址有自己对应的页表,用户进程各自有不同的页表。每个进程的用户空间都是完全独立、互不相干的。

Linux,进程开辟个数的上线:
为了与老版本兼容,short int pid,32768
可以修改上线:
修改这个文件:/proc/sys/kernel/pid_max

Linux下进程的状态:
如何查看Linux 进程状态:
ps:静态查看进程信息
ps -aux
ps -ajx (SID)
top:动态查看活跃的进程信息
pstree:显示进程的父子关系/打印进程树
kill:给指定进程发送信号,查看Linux提供的信号

ps命令的输出内容:
	PPID:父进程ID       				*
	PID:进程ID 							*
	PGID:进程组ID 						*
	SID:会话组ID(会话期ID) 				*
	TTY:终端							*
	STAT:进程状态						*
			进程状态,用下面的代码中的一个给出:
			D    不可中断     Uninterruptible sleep (usually IO)
			R    正在运行,或在队列中的进程
			S    处于休眠状态
			T    停止或被追踪
			Z    僵尸进程
			W    进入内存交换(从内核2.6开始无效)
			X    死掉的进程
			<    高优先级
			N    低优先级
			N    低优先级
			L    有些页被锁进内存
			s    包含子进程
			+    位于后台的进程组
			l    多线程,克隆线程

	UID:进程的用户ID
	TIME:进程使用的总CPU时间
	COMMAND:执行进程时的命令
	USER:进程所有者
	%CPU:进程自最近一次刷新以来所占用的CPU时间和总时间的百分比
	%MEM:进程使用内存的百分比
	VSZ:进程使用的虚拟内存大小,以K为单位
	RSS:驻留空间的大小。显示当前常驻内存的程序的K字节数
	START:进程的起始时间
	NI	进程的优先级值,较小的数字意味着占用较少的CPU时间
	PRI	进程优先级。
	WCHAN	进程等待的内核事件名
Linux进程状态:
	运行态
	停止态
	僵尸态
	死亡态
	阻塞态
		可中断的阻塞态
		不可中断的阻塞态

进程信息存在:/proc目录下

进程的创建
进程创建技术:
1.完美拷贝
几乎拷贝父进程的所有内容,除了父进程的PID,PCB信息
2.写时拷贝
开辟好进程基本管理信息后,共享父进程的数据和代码,只有
需要更改时,才拷贝
较为节省内存开销,所以用的较多
Linux 2.6版本之后,大多使用写时拷贝

fork:
	/*需要包含的头文件*/
	#include 
	/*
	 *函数名:fork
	 *函数功能:创建子进程
	 *函数参数:void
	 *函数返回值:pid_t
	 *		>0: 父进程,代表子进程的pid
	 *      ==0: 子进程
	 *      -1: fork失败
	 */
	pid_t fork(void);


vfork
clone
system:<在正式工作时,不建议使用,容易产生死锁>
	/*需要包含的头文件*/
	#include 
	/*
	 *函数名:system
	 *函数功能:fork一个子进程,执行一条shell指令
	 *函数参数:const char *command:指令内容
	 *函数返回值:int:见manual
	 */
	int system(const char * command);
sleep():
	/*需要包含的头文件*/
	#include 
	/*
	 *函数名:sleep
	 *函数功能:让进程/线程睡眠指定的秒数
	 *函数参数:unsigned int seconds:指定睡眠的秒数
	 *函数返回值:unsigned int:成功返回sleep的时间,失败返回0			
	 */
	 unsigned int sleep(unsigned int seconds);
fork():
	1.创建一个子进程
	2.2.6以后用写时拷贝,之前用完美拷贝
	3.子进程和父进程执行顺序不确定
	4.有两个返回值,
		>0:为父进程,返回值代表子进程的pid
		==0:为子进程,返回值为0
vfork():
	1.创建子进程,阻塞父进程
	2.子进程先执行
	3.使用的是写时拷贝
	4.有两个返回值,
		>0:为父进程,返回值代表子进程的pid
		==0:为子进程,返回值为0

clone():
	不管是fork()还是vfork(),底层实现都是clone()
	该函数返回子进程的进程号,可以有选择性的拷贝
	父进程的内容。

system():
	fork() + execl()
	创建一个进程,并执行一个shell命令
	不建议执行耗时命令,容易产生死锁的情况
进程的执行

exec函数族:
一般用来执行程序,而不是程序员自己灵活编写的代码
要求执行的是可执行文件。
当判断是否为子进程时,直接写相应的代码
这种方式比较灵活
system
同execl,但容易发生死锁
exec x x:
l e
v p
第五位的选项:
l:命令行参数以枚举形式输入,包含命令本身,以NULL结尾
v:命令行参数以指针数组形式传入,即把list内容写入指针数组
第六位的选项:
e:在指定的位置中执行查找可执行文件
p:在系统环境变量中执行查找可执行文件

exec函数族只能执行指定的可执行文件,是一种不够灵活的执行特定任务
的方法,掌握 execve execlp就可以了

进程的销毁
exit:
	调用退出处理函数
	清理I/O缓冲区
_exit:
	没有上述操作	

获取进程表识:
#include
#include

获取当前进程的pid
pid_t getpid(void);
获取父进程的pid
pid_t getppid(void);

标准IO缓冲区:
缓冲区的分类:
全缓冲:普通文件
行换成:stdin,stdout
无缓冲:stderr
ANSI C(C89)要求缓冲具有以下特点:
1.当且仅当标准输入和标准输出并不涉及交互设备时,它们才可以使用全缓冲。

这里并没有交代如果标准输入和标准输出涉及交互作用设备时,它们使用无缓冲还是行缓冲。

大多数系统默认使用下列类型的缓冲区:
1.标准错误是不带缓冲区的
2.如果涉及终端设备的流,则它们是行缓冲的
3.否则是全缓冲

注意:终端牵扯标准输入/输出流(stdin/stdout)

在我们使用的linux/ubuntu中:
全缓冲的大小为:4096 Byte
行缓冲的大小为:1024 Byte

如何验证全缓冲的大小:
原理:缓冲区存不下数据的时候,会刷新缓冲区(正常情况)
注意:为了避免进程退出刷新缓冲区,需要_exit()函数来做这件事情
或者使用while(1)还阻塞进程,防止进程退出(不建议使用,比较难理解)

例子:
#include 
		#include 

		int main()
		{
		        FILE *fp = NULL;
		        fp = fopen("a.txt","r+");
		        if(NULL == fp)
		        {
		                puts("open file error.");
		                return -1;
		        }

		        int i = 0;
		        //当缓冲区中的数据<=4096,在没有其他强制刷新的情况下,不会
		        //清空缓冲区,只有当缓冲区中的数据>4096个字符时,才会刷新
		        for(i = 0; i < 4096; i++)
		        {
		                fputc('a',fp);
		        }
		        _exit(0);
		}
	输出结果:执行之后,a.txt没有写入数据
		-rw-rw-r-- 1 farsight farsight    0 May  6 19:37 a.txt

	上面的实验结果说明:缓冲区没有满,数据没有写入,也证明了大小为4096
#include 
		#include 

		int main()
		{
		        FILE *fp = NULL;
		        fp = fopen("a.txt","r+");
		        if(NULL == fp)
		        {
		                puts("open file error.");
		                return -1;
		        }

		        int i = 0;
		        //当缓冲区中的数据<=4096,在没有其他强制刷新的情况下,不会
		        //清空缓冲区,只有当缓冲区中的数据>4096个字符时,才会刷新
		        for(i = 0; i < 4200; i++)
		        {
		                fputc('a',fp);
		        }
		        _exit(0);
		}
	输出结果:执行之后,a.txt写入数据,并且只写了缓冲大小个数据
    	-rw-rw-r-- 1 farsight farsight 4096 May  6 19:39 a.txt

如何验证行缓冲的大小:
原理:同全缓冲的原理

例子:
#include 
		#include 

		int main()
		{
				//当缓冲区中的数据<=1024,在没有其他强制刷新的情况下,不会
		        //清空缓冲区,只有当缓冲区中的数据>1024个字符时,才会刷新
		        int i = 0;
		        for(i = 0; i < 1024; i++)
		        {
		                fputc('a',stdout);
		        }
		        _exit(0);
		}
	输出结果:
		没有数据输出

	#include 
	#include 
int main()
	{
			//当缓冲区中的数据<=1024,在没有其他强制刷新的情况下,不会
	        //清空缓冲区,只有当缓冲区中的数据>1024个字符时,才会刷新
	        int i = 0;
	        for(i = 0; i < 1030; i++)
	        {
	                fputc('a',stdout);
	        }
	        _exit(0);
	}
	输出结果:写入1024个数据
		aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa		

无缓冲:
stderr
例子:

#include 
	#include 

	int main()
	{
	        int i = 0;
	        fputs("error 011",stderr);
	        _exit(0);
	}
输出:
	farsight@ubuntu:~/ioclass$ ./a.out 
	error 011farsight@ubuntu:~/ioclass$ 

如果想用其他方法来强制刷新缓冲区,有以下几种方法:
1.使用函数fflush
2.行缓冲中遇到’\n’
3.exit(),进程退出时
4.程序自动退出时(即,原因是3,进程自动退出)
5.fclose()调用时
6.正常return 0

如果想程序退出,且不刷新缓冲区:
1.使用_exit()函数

============================================================================================
如果想要设置缓冲区的大小:
使用setvbuf()函数来实现:
/*
*函数名:setvbuf
*函数功能:设置缓冲区大小
*函数参数:
* FILE *stream:被设置缓冲区的文件流指针
* char *buf:缓冲区的首地址
* int mode:
* _IONBF unbuffered(无缓冲)
* _IOLBF line buffered(行缓冲)
* _IOFBF fully buffered(全缓冲)
*
* size_t size:缓冲区的大小(stdio.h有一个BUFSIZ宏,是8192)
*函数返回值:int,成功返回0,失败返回错误码
*/
int setvbuf(FILE *stream, char *buf, int mode, size_t size);

如何获取缓冲:
1.当文件上发生第一个IO操作时,才会调用malloc

例子:
#include 
		#include 
		int main()
		{

		        char bufr[8192] = {0};
		        printf("BUFSIZ:%d\n",BUFSIZ);
		        FILE *fp = NULL;
		        fp = fopen("a.txt", "r+");
		        if(NULL == fp)
		        {
		                puts("open stdout error.");
		                return -1;
		        }
		        char buf[20];
		        memset(buf, 0, sizeof(buf));
		        //读操作之前打印
		        printf("bufsize:%d\n",fp->_IO_buf_end - fp->_IO_buf_base);
		        printf("readbufsize:%d\n",fp->_IO_read_end - fp->_IO_read_base);
		        printf("writebufsize:%d\n",fp->_IO_write_end - fp->_IO_write_base);
		        fread(buf, 1, sizeof(buf), fp);
		        puts(buf);
		        puts("=================");
		        printf("readbufsize:%d\n",fp->_IO_read_end - fp->_IO_read_base);
		        char str[] = "hellodlfjsdklffsdjflkdksfjlkdsfjlklkdksjfkl";
		        fwrite(buf, strlen(buf),1,fp);
		        //读写操作之后打印
		        printf("writebufsize:%d\n",fp->_IO_write_end - fp->_IO_write_base);
		        printf("bufsize:%d\n",fp->_IO_buf_end - fp->_IO_buf_base);
		        puts("&&&&&&&&&&&&&&&&&&&&&&&");
		        char *p = NULL;
		        //通过setvbuf来重新设置缓冲区大小
		        setvbuf(fp,bufr,_IOFBF,BUFSIZ);
		        fseek(fp, 0L, SEEK_SET);
		        memset(buf, 0, sizeof(buf));
		        printf("bufsize:%d\n",fp->_IO_buf_end - fp->_IO_buf_base);
		        printf("readbufsize:%d\n",fp->_IO_read_end - fp->_IO_read_base);
		        printf("writebufsize:%d\n",fp->_IO_write_end - fp->_IO_write_base);
		        fclose(fp);
		        return 0;
		}
	代码输出:
		farsight@ubuntu:~/ioclass$ ./a.out 
		BUFSIZ:8192
		bufsize:0
		readbufsize:0
		writebufsize:0
		aaaaaaaaaaaaaaaaaaaa
		=================
		readbufsize:4096
		writebufsize:4076
		bufsize:4096
		&&&&&&&&&&&&&&&&&&&&&&&
		bufsize:8192
		readbufsize:0
		writebufsize:0

你可能感兴趣的:(多进程,多线程,c语言,嵌入式,ubuntu)