[LINUX-04]linux进程、调度、线程、进程上下文等几点理解
1、信号来自进程或内核
2、线程共享进程的代码空间和数据空间(全局变量或静态变量),文件描述符,信号,以及malloc分配的内存,每个线程拥有独立的栈空间和程序计数器,在创建线程时,调用pthread_create函数的线程和新建线程的执行顺序随机
3、在linux中,使用轻量级进程来模拟线程,线程操作的相关函数通过第三方线程库 (Linuxthreads 或 Native POSIX Thread Library(NPTL))来实现
4、down()/down_interruptible()用于内核空间执行单元的同步,其中down(struct semaphore *sem)用于获取信号量sem,如果调用者获取不到信号量,将会导致睡眠,进入睡眠状态的进程不能被信号打断,而因down_interruptible进入睡眠状态的进程能被信号打断,导致该函数返回。
5、时间片:进程处于运行状态时所剩余的时钟滴答数 ,每次时钟中断到来时,这个 值就减1。当这个域的值变得越来越小,直至为0 时,就把need_resched 域置1(在调度时机到来时,检测这个域的值,如果为1,则调用schedule())
6、OS时钟(嘀嗒时钟)作为linux系统调度的基准时钟。
7、linux多线程编程中,可使用sleep函数来释放线程的cpu控制权,测试代码如下
#include <stdio.h> #include <unistd.h> #include <string.h> #include <pthread.h> void *SleepTest1(void *arg) { while(1) { printf("the sleeptest1 is ok!\n"); sleep(3); } } void *SleepTest2(void *arg) { while(1) { printf("the sleeptest2 is ok!\n"); sleep(10); } } int main(int argc, char *argv[]) { pthread_t tid[2]; int ret; ret = pthread_create(&tid[0], NULL, SleepTest1, NULL); if(ret < 0) { printf("pthread_create"); return -1; } ret = pthread_create(&tid[1], NULL, SleepTest2, NULL); if(ret < 0) { perror("pthread_create"); return -1; } printf("the thread1 and thread2 create ok!\n"); pthread_join(tid[0],NULL); pthread_join(tid[1],NULL); printf("the thread exit!\n"); return 0; }
8、僵死进程:在每个进程退出的时候,内核释放该进程所有的资源(包括打开的文件、占用的内存等),但是仍然为其保留一定的信息(包括进程号,退出状态,运行时间等),直到父进程通过外wait/waitpid来取时才释放。此时该进程处于僵死状态。
9、进程退出
进程在退出时,必须释放它所拥有的资源,并通过某种方式(发送SIGCHLD信号)告诉父进程。进程的退出一般是显示或隐式地调用了eixt(),或者接受了某种信号。不过什么原因退出,最终都调用了do_exit。
用于进程退出的系统调用有两个exit和exit_group,exit只是终止某个进程,而exit_group整个线程中的进程。它们在内核中的服务函数分别为sys_exit和sys_exit_group,它们又分别调用了do_exit和do_group_exit。而do_group最终又调用了do_exit(do_exit定义在kernel/exit.c中)。
10、查看某进程打开的文件描述符
[root@localhost proc]# cd 1328/
[root@localhost 1328]# ls
attr clear_refs cpuset exe io loginuid mountinfo net pagemap sched smaps statm task
auxv cmdline cwd fd latency maps mounts oom_adj personality schedstat stack status wchan
cgroup coredump_filter environ fdinfo limits mem mountstats oom_score root sessionid stat syscall
[root@localhost 1328]# cd fdinfo/
[root@localhost fdinfo]# ls
0 1 10 11 12 13 14 15 16 17 18 19 2 20 21 22 23 3 4 5 6 7 8 9
[root@localhost fdinfo]#
fdinfo文件夹中列出的数字,即该进程打开的文件描述符
11、fork函数
fork在复制的时候,parent顺序执行过的代码在chiled中应该是不会再执行!
12、进程上下文和中断上下文详解
用户空间的应用程序,通过系统调用,进入内核空间。这个时候用户空间的进程要传递 很多变量、参数的值给内核,内核态运行的时候也要保存用户进程的一些寄存 器值、变量等。所谓的“进程上下文”,可以看作是用户进程传递给内核的这些参数以及内核要保存的那一整套的变量和寄存器值和当时的环境等。
硬件通过触发信号,导致内核调用中断处理程序,进入内核空间。这个过程中,硬件的 一些变量和参数也要传递给内核,内核通过这些参数进行中断处理。所谓的“ 中断上下文”,其实也可以看作就是硬件传递过来的这些参数和内核需要保存的一些其他环境(主要是当前被打断执行的进程环境)。
[简单理解:设备运行过程中,有两部分软件将可能被运行,即用户程序和系统内核,某个应用程序在执行时,可能通过ioctl等函数访问内核,内核收到此访问后,cpu将运行内核对应函数代码段,这时要保存当前环境信息,以保证内核代码执行后回到对应的应用程序代码段,这就是进程上下文;对于中断上下文仅内核存在中断处理,与单片机中断性质一样]
LINUX完全注释中的一段话:
当一个进程在执行时,CPU的所有寄存器中的值、进程的状态以及堆栈中的内容被称 为该进程的上下文。当内核需要切换到另一个进程时,它需要保存当前进程的 所有状态,即保存当前进程的上下文,以便在再次执行该进程时,能够必得到切换时的状态执行下去。在LINUX中,当前进程上下文均保存在进程的任务数据结 构中。在发生中断时,内核就在被中断进程的上下文中,在内核态下执行中断服务例程。但同时会保留所有需要用到的资源,以便中继服务结束时能恢复被中断进程 的执行。
进程上下文是一种内核所处的操作模式,此时内核代表进程执行--例如执行系统调用或运行内核线程。
上下文context: 上下文简单说来就是一个环境,相对于进程而言,就是进程执行时的环境。具体来说就是各个变量和数据,包括所有的寄存器变量、进程打开的文件、内存信息等。
一个进程的上下文可以分为三个部分:用户级上下文、寄存器上下文以及系统级上下文。
用户级上下文: 正文、数据、用户堆栈以及共享存储区;
寄存器上下文: 通用寄存器、程序寄存器(IP)、处理器状态寄存器(EFLAGS)、栈指针(ESP);
系统级上下文: 进程控制块task_struct、内存管理信息(mm_struct、vm_area_struct、pgd、pte)、内核栈。
当发生进程调度时,进行进程切换就是上下文切换(context switch).操作系统必须对上面提到的全部信息进行切换,新调度的进程才能运行。而系统调用进行的模式切换(mode switch)。模式切换与进程切换比较起来,容易很多,而且节省时间,因为模式切换最主要的任务只是切换进程寄存器上下文的切换。
13、进程终止方式
在Linux中进程退出分为了正常退出和异常退出两种。
1>正常退出
a. 在main()函数中执行return 。
b.调用exit()函数
c.调用_exit()函数
2>异常退出
a.调用abort函数
b.进程收到某个信号,而该信号使程序终止。
不管 是那种退出方式,系统最终都会执行内核中的同一代码。这段代码用来关闭进程所用已打开的文件描述符,释放它所占用的内存和其他资源。
3>_exit()和 exit()区别:
a._exit()执行后立即返回给内核,而exit()要先执行一些清除操作,然后将控制权交给内核。
b. exit() 和 _exit()区别之一: 前者支持调用用户自定义的清除程序(atexit函数注册)
c. 调用_exit函数时,其会关闭进程所有的文件描述符,清理内存以及其他一些内核清理函数,但不会刷
新流(stdin, stdout, stderr ...). exit函数是在_exit函数之上的一个封装,其会调用_exit,并在
调用之前先刷新流。
14、copy-on-write技术
fork会将调用进程的所有内容原封不动的拷贝到新产生的子进程中去,这些拷贝的动作很消耗时间,而如果fork完之后我们马上就调用exec,这些辛辛苦苦拷贝来的东西又会被立刻抹掉,这看起来非常不划算,于是人们设计了一种"写时拷贝(copy-on-write)"技术,使得fork结束后并不立刻复制父进程的内容,而是到了真正使用的时候才复制,这样如果下一条语句是exec,它就不会白白作无用功了,也就提高了效率。
15、后台程序运行需注意几点:
在后台运行作业时要当心:需要用户交互的命令不要放在后台执行,因为这样你的机器就会在那里傻等。不过,作业在后台运行一样会将结果输出到屏幕上,干扰你的工作。如果放在后台运行的作业会产生大量的输出,最好使用下面的方法把它的输出重定向到某个文件中:
command >out.file 2>&1 &
16、fflush函数
标准C的fflush()支持刷新输出缓冲区,对于输入缓冲区无定义