linux 进程调度卡死,Linux的进程、线程以及调度(一)

66b52468c121889b900d4956032f1009.png

8种机械键盘轴体对比

本人程序员,要买一个写代码的键盘,请问红轴和茶轴怎么选?

Linux的进程、线程以及调度(一)

1.1 task_struct

进程是一个资源分配单位。

linux下进程是用什么样的数据结构来描述它?

进程控制块 PCB(process control block),linux下的进程控制块是task_struct结构体。

struct task_struct {

pid_t pid;

...

struct mm_struct *mm;/* 描述内存资源 */

/* filesystem information */

struct fs_struct *fs;/* 描述文件系统资源 */

/* open file information */

struct files_struct *files;/* 描述文件资源 */

struct signal_struct *signal;

...

}

6a98db2fd816c6389f48e8aa4d62b02d.png

pid

pid的数量是有限的,32位系统查看pid最大值,cat /proc/sys/kernel/pid_max, 值为32768。

fork 炸弹

:(){:|:&};:

/* 函数名为冒号:,{}函数体里面调用自己,再创建一个管道创建一个新的进程调用自己并且后台执行,

分号;为函数结束,再调用自己,这个程序不断的创建新的进程导致pid最终用完。 */

task_struct 被管理的数据结构

task_struct通过形成链表,树,哈希来被管理,这里task_strcuct 只是一个但是用多个数据结构从多个不同的侧面来描述它,自然在各种场景下访问都比较快,如遍历所有的进程就用链表,想知道进程的父就用树,想通过pid快速的检索一个进程就用哈希表,这是典型的以空间换时间的思路。

4fc11cb0810f3e9a55f40c0f93cb7ebb.png

1.2 Linux 进程生命周期(就绪、运行、睡眠、停止、僵死)就绪态 运行态

进程一旦被fork出来就处于就绪态,一旦拿到的cpu就处于运行态,实际上在内核里面就绪和运行的状态标志是一样的。但是进程不能老是一直占有cpu,也可能让给别人,所以运行态也可能切回就绪态。有两种可能性,(1)时间片用完(分时调度),(2)时间片没用完但是被更紧急的事件抢占。运行态 —–> 睡眠态

进程处于运行态经常需要等待资源,如串口,网络发包,等资源不可能一直占有cpu死等,必须把进程切换睡眠态。

睡眠分为深度睡眠和浅度睡眠,深度睡眠指必须等到资源才会醒,浅度睡眠指资源来了也会醒,信号(singal)来了也会醒。睡眠态 —-> 就绪态

一旦等到了资源,切换就绪态运行态 —-> 僵尸

进程刚死时会变成僵尸,在linux下任何一个进程死时,不是人间蒸发,都会变成僵尸。运行态 —-> 停止态

停止态指进程在运行的过程中不去睡眠人为的让它停止,一般是发stop信号(ctrl +z ),或者是gdb attach debug调试。停止态 —-> 就绪态

给停止态发送continue信号即可到就绪态。

fb7cac8af42a352050f536f704b4723b.png

1.3 僵尸是什么?

僵尸是什么?指的是用于描述它的数据结构task_struct还没有消失,但是进程所依附的资源都已经消失了,留下来的目的是让它的父进程wait4的api去等待它死,父进程通过wait4来等它时,僵尸才会消失,僵尸是非常短的临界状态,一个进程死了但父进程还没有来到及调它的wait4得情况,它就是僵尸,一旦父进程wait4他就直接人间蒸发掉了。

资源已经释放:无内存泄露等,task_struct还在:父进程可以查到子进程的死因(父进程wait 它的时候可以得到子进程的退出码)。

437976b2c9356b75d2faf575019ee689.png

例子:

#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 1/* define 1 to make child process always a zomie */

printf("ppid:%dn", getpid());

while(1);

#endifdo {

/* 子进程死 waitpid就返回,status保存子进程退出原因 */

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 1 改为if 0

窗口1:

$ ./a.out

$ child process id: 113494

$ child process is killed by signal 2

窗口2:

$ kill -2 113494

将if 0 改为if 1 父进程一直不能wait子进程

窗口1:

$ ./a.out

$ ppid:130064

$ child process id: 130065

窗口2:

lisongze@svr04:~$ ps -aux | grep a.out

lisongze 130064 100 0.0 4200 788 pts/8 R+ 14:08 0:09 ./a.out 父进程

lisongze 130065 0.0 0.0 4200 84 pts/8 S+ 14:08 0:00 ./a.out 子进程

lisongze 130240 0.0 0.0 15944 2508 pts/11 S+ 14:08 0:00 grep --color=auto a.out

lisongze@svr04:~$ kill -2 130065 杀掉子进程

lisongze@svr04:~$ ps -aux | grep a.out

lisongze 130064 101 0.0 4200 788 pts/8 R+ 14:08 0:22 ./a.out 父进程

lisongze 130065 0.0 0.0 0 0 pts/8 Z+ 14:08 0:00 [a.out] 子进程变成僵尸态

lisongze 131485 0.0 0.0 15944 2548 pts/11 S+ 14:08 0:00 grep --color=auto a.out

lisongze@svr04:~$ kill -9 130065

lisongze@svr04:~$ ps -aux | grep a.out

lisongze 130064 101 0.0 4200 788 pts/8 R+ 14:08 0:30 ./a.out

lisongze 130065 0.0 0.0 0 0 pts/8 Z+ 14:08 0:00 [a.out] 任何信号是干不掉僵尸,但僵尸不会耗费资源

lisongze 132025 0.0 0.0 15944 2488 pts/11 S+ 14:09 0:00 grep --color=auto a.out

lisongze@svr04:~$ kill -2 130064 唯一可以清理子进程僵尸方法是杀掉父进程

lisongze@svr04:~$ ps -aux | grep a.out

lisongze 132987 0.0 0.0 15944 2552 pts/11 S+ 14:09 0:00 grep --color=auto a.out

1.4 内存泄漏的真实含义

不是进程死了,内存没释放,而是进程活着,运行越久,耗费内存越多

d6927b89480df26883db048080f173ea.png

停止状态与作业控制,cpulimit

ctrl + z,fg/bg 例子:

int main()

{

int i = 0;

while(1) {

volatile int j,k;

for(i = 0; i < 1000000; i++);

printf("hello %dn",j++);

printf("world %dn",j++);

}

return 0;

}

调试:

通过ctrl+z可让进程暂停,fg可让进程继续执行。

cpulimit 控制一个进程的cpu消耗。

cpulimit -l 20 -p 10111

限制pid为10111程序的cpu使用率不超过20

cpulimit的原理是不断进程在停止和运行态之间来回切换,以此来降低cpu利用率。

c04d522e6dacf7c4dc3a0c6d629b2523.png

1.5 fork

打印几个hello?

main()

{

fork();

printf("hellon");

fork();

printf("hellon");

while(1);

}

一个fork执行如下图:

1f8f7a49b04ac8107d0e8ebe384d11c8.png 上面程序fork之后产生4个进程,打印6个hello

参考学习宋宝华《打通linux脉络:Linux的进程、线程以及调度》后总结。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值