提交 2e82abc8 编写于 作者: H hypox64

Merge branch 'linux_tmp'

## Linux修改时区的正确方法
ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
## centOS 7 gcc install
yum install gcc -y
## 编译和运行HelloWorld
gcc HelloWorld.c -o HelloWorld-getchar
HelloWorld-getchar
#include <stdio.h>
int main()
{
printf("HelloWorld!\n");
getchar();
return 0;
}
\ No newline at end of file
[toc]
## 实验目的
* 通过编程实践、观察,学习内存管理的方式。
## 实验环境
* 硬件
1.Intel(R) Xeon(R) Bronze 3104 CPU @ 1.70GHz
2.DDR4 16G
* 软件
1.CentOS 7
2.ubuntu 18.04
3.VMware Workstation Pro 15
## 实验记录
### 2-1 察看HelloWorld-gechar 的三个进程<br>
```bash
ps -aux | grep HelloWorld
```
3个进程的PID分别是26608 26649 26689<br>
![image](./record/pic/2-1.jpg)
### 2-2 屏显 HelloWorld 可执行文件信息<br>
```bash
file HelloWorld
```
![image](./record/pic/2-2.png)
x86-64 平台上的ELF 格式可执行文件。
一种用于二进制文件、可执行文件、目标代码、共享库和核心转储格式文件。
ELF文件由4部分组成,分别是ELF头(ELF header)、程序头表(Program header table)、节(Section)和节头表(Section header table)。
由于ELF 文件内部包含了足够的信息,操作系统可以根据ELF 可执行文件创建出进程映像,进程映像以及各种管理数据结构构成了进程实体,另外进程还可能向系统申请各种资源——共同组成了进程实体的全部内涵。进程实体被调度运行就构成了进程的活动,如果系统中有多个活动的进程,那么它们各自有独立的进程空间(各自独立地使用自己的0~0xFFFFFFFF 虚存空间)。<br>
在Linux 中进程PCB 是task_struct 结构体,<br>
### 进程间组织关系<br>
Linux 系统中所有进程通过进程控制块PCB(struct task_struct)形成多种组织关系。根据
进程创建过程可以有亲缘关系,通过进程间的父子关系组织成一个进程树<br>
一个进程通过创建一个子进程则形成父子关系,如果创建多个子进程,那么这些新创建的
进程间属于兄弟关系<br>
### 2-3 pstree 输出
![image](./record/pic/2-3.png)
### 2-4 查看会话、进程组和控制终端的例子
![image](./record/pic/2-4.png)
### PPID
PPID(PPID,Parent PID) PID(进程ID) PGID(Process Group ID 进程组 ID号) SID(Session ID 会话ID) TTY TPGID(tty process group ID for the process group leader) STAT() UID TIME COMMAND<br>
STAT:D 无法中断的休眠状态(通常 IO 的进程);R 正在运行可中在队列中可过行的;S 处于休眠状态;T 停止或被追踪;W 进入内存交换(从内核2.6开始无效);X 死掉的进程(从来没见过);Z 僵尸进程;+ 位于后台的进程组<br>
“会话/进程组/线程组”几个概念呈现层级关系,Linux 系统中可以有多个会话(session),
每个会话可以包含多个进程组(process group),每个进程组可以包含多个进程,每个进程构
成一个线程组——一个线程组由一个进程内的一个或多个线程组成,这些对象都有各自的ID。会话是从用户登录系统到退出前的全部活动,不同帐户的登录属于不同会话,而同一帐户的多次登录也构成不同的会话。而进程组主要出于作业控制的目的,有些shell 没有作用控制能力,就将整个会话的所有进程构成一个进程组。
### 2-5 创建进程的命令
```bash
chmod a+x shell-script
shell-script
```
![image](./record/pic/2-5.png)
第一行用于指出该脚本需要用/usr/bin/bash 来解
释执行,第二行shell 内部命令echo 用来显示,第三行HelloWorld 是外部命令(磁盘上的可执
行文件)。
### 2-6 通过ps 查找进程的PID
1.查找进程的PID<br>
2.kill PID <br>
```bash
ps -ef | grep HelloWorld-getchar
```
![image](./record/pic/2-6.png)
### 2-7 fork-demo 的输出
![image](./record/pic/2-7.png)
fork 函数被成功调用后将按照父进程的样子复制出一个完全相同的子进程。父进程fork()
函数结束时的返回值是子进程的PID 编号。新创建的子进程则也是从fork()返回处开始执行,
但不同的是它获得的返回值是0。也就是说父子进程执行上几乎相同,<br>
* 唯一区别是子进程中返回0 值而父进程中返回子进程PID。<br>
* 如果fork()出错则只有父进程获得返回值-1 而子进程未能创建。<br>
### 2-8 fork-demo 的输出
![image](./record/pic/2-8.png)
### 2-9 fork-twice-demo 执行后的 输出
![image](./record/pic/2-9.png)
### 孤儿进程和僵尸进程
(1) 孤儿进程:一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。孤儿进程将被init 进程(进程号为1)所收养,并由init 进程对它们完成状态收集工作。
(2) 僵尸进程:一个子进程在其父进程还没有调用wait()或waitpid()的情况下退出。这个子进程就是僵尸进程。
子进程结束后大部分资源都将直接回收,但是还将在PCB(struct task_struct 和内核堆栈)中记录一些退出时的状态等信息,这些信息供父进程检查后才能销毁回收。因此,僵尸进程因相应的数据没有父进程处理并回收,将会导致资源浪费,而孤儿则不会有这样的问题。
* 孤儿进程
![image](./record/pic/孤儿进程.png)
* 僵尸进程
![image](./record/pic/僵尸进程.png)
### 2-10 fork-exec-demo 的执行结果
![image](./record/pic/2-10.png)
* 这里可以看到子进程通过execve()将自己变身为“/usr/bin/ls”,因此不再执行与父进程里的代——即后面的“printf("this printf()will not be executed, because …”是没有机会得到执行的。
* fork()和exec 函数族构成了创建新进程的完整操作,如果父子进程功能相同则只用fork(),如果父子进程不同则利用exec 函数族的来实现。
### 2-11 用ps –ajx 查看daemon-demo 守护进程
![image](./record/pic/2-11.png)
关闭daemon-demo运行终端后仍能找到daemon-demo进程,且PID为1
### 2-12 /etc/rsyslog.conf 中的内容
![image](./record/pic/2-12.png)
日志信息写入了/var/log/messages 文件中。
### 2-13 查看/var/log/messages
![image](./record/pic/2-13.png)
由于前后运行了多次,所以出现了3个。
### 2-14&2-15 pthread-demo 的输出及ps –aLf 的输出
![image](./record/pic/2-14&2-15.png)
两个线程公用一个PID号19392,但是LWP却不一样。
### 2-16&2-17 PCB 开销
```bash
cat /proc/slabinfo | grep task_struct
```
比较两者在进程控制块上的开销(包括 struct task_struct 和内核栈)
2-16 fork-100-demo 运行前后的/proc/slabinfo 中关于 task_struct 数量
![image](./record/pic/2-16.png)
2-17 pthread-100-demo 运行前后的/proc/slabinfo 中关于 task_struct 数量
![image](./record/pic/2-17.png)
两者在运行后的task_struct数量均增加100,说明线程作为调度执行单位,进程控制块PCB(task_struct)还是需要的,这是最小资源的一部分。因此这方面资源开销对进程和线程都是一样的。
### 2-18&2-19&2-20&2-21内存描述符开销
* 查看mm_struct数量
```bash
cat /proc/slabinfo | grep mm_struct
```
2-18 fork-100-demo 运行前后的/proc/slabinfo 中关于 mm_struct 数量
![image](./record/pic/2-18.png)
2-19 pthread-100-demo 运行前后的/proc/slabinfo 中关于 mm_struct 数量
![image](./record/pic/2-19.png)
前者运行时mm_struct数量增加了100,而后者没有变化,说明,pthread-100-demo 虽然创建了 100 个线程,但是用于共享进程空间,并没有增加一个 mm_struct
* 查看vm_area_struct数量
```bash
cat /proc/slabinfo | grep vm_area_struct
```
2-20 fork-100-demo 运行前后的/proc/slabinfo 中关于 vm_area_struct 数量
![image](./record/pic/2-20.png)
2-21 pthread-100-demo 运行前后的/proc/slabinfo 中关于 vm_area_struct 数量
![image](./record/pic/2-21.png)
可以发现pthread-100-demo需要新增大约 150 左右的使用量,而fork-100-demo需要约 1300 左右的使用量,故线程的开销确实比进程要小。
## 课后作业
### 1.编写 C 语言程序,利用 fork()创建子进程,形成以下父子关系:并通过 ps 命令检查进程的进程号 PID 和父进程号 PPID 证明你成功创建相应的父子关系,同时也用 pstree 输出进程树结构来验证其关系。
![image](./homework/pic/problem_1.png)
* 解答:
* (1).[code](./homework/code/1_topology_1.c)
![image](./homework/pic/1_topology_1.png)
可以看到父进程12232拥有10个子进程且满足题目的要求
* (2).[code](./homework/code/1_topology_2.c)
![image](./homework/pic/1_topology_2.png)
可以看到父进程13738拥有10个子进程且满足题目的要求
* (3).[code](./homework/code/1_topology_3.c)
![image](./homework/pic/1_topology_3.png)
可以看子进程结构满足题目要求。
### 3.创建多个线程,在各个线程中打印出堆栈变量的地址,大致标记出其位置,注意区分主线程的堆栈和其他线程堆栈位置的差异。
* 解答:[code](./homework/code/3_pthread.c)
注意:编译时需要链接文件-lpthread -rdynamic
```bash
gcc ./3_pthread.c -lpthread -rdynamic -o 3_pthread
```
![image](./homework/pic/3_pthread.png)
程序中有一个父线程以及3个子线程,可以发现子线程的偏移地址以及返回地址都是一样的,且斗与父线程不一样。
### 4.尝试自行设计一个 C 语言小程序,完成最基本的 shell 角色:给出命令行提示符、能够接受命令;对于命令分成三种,内部命令(例如 help 命令、exit 命令等)、外部命令(常见的 ls、cp 等,以及其他磁盘上的可执行程序 HelloWrold 等)以及无效命令(不是上述两种命令)。
* 解答:[code](./homework/code/shell.c)
![image](./homework/pic/4_shell.png)
捕获键盘输入->判断是那种命令->
* 1.内部命令:打印help文件或exit(0);退出<br>
* 2.外部命令,fork出新的进程并使用execvp函数执行
* 3.可执行程序,与外部命令方法相同
* 4.无效命令:打印无效警告
#include <stdio.h>
#include <unistd.h>
//1_topology_1───10*[1_topology_1]
int main(int argc, char ** argv )
{
int i = 0;
for (i = 0; i < 10; ++i)
{
int pid = fork();
if(pid == -1)
{
printf("error!\n");
}
else if(pid ==0)
{
printf("This is the child process %d!\n",i);
getchar();
}
}
{
printf("This is the parent process!");
getchar();
wait(NULL);
}
return 0;
}
\ No newline at end of file
#include <stdio.h>
#include <unistd.h>
//1_topology_2───1_topology_2───1_topology_2───1_topology_2───1_topology_2───1_topology_2───1_topology_2───1_topology_2───1_topology_2───1_topology_2───1_topology_2
void recursive_fork(num)
{
num--;
if (num != -1)
{
int pid = fork();
if(pid == -1)
{
printf("error!\n");
}
else if(pid ==0)
{
printf("This is the child process %d!\n",num);
recursive_fork(num);
getchar();
}
}
}
int main(int argc, char ** argv )
{
int i = 0;
recursive_fork(10);
{
printf("This is the parent process!\n");
getchar();
wait(NULL);
}
return 0;
}
\ No newline at end of file
#include <stdio.h>
#include <unistd.h>
/*
| | `-1_topology_3
| | |-1_topology_3
| | | |-1_topology_3
| | | | |-1_topology_3
| | | | `-1_topology_3
| | | `-1_topology_3
| | | |-1_topology_3
| | | `-1_topology_3
| | `-1_topology_3
| | |-1_topology_3
| | | |-1_topology_3
| | | `-1_topology_3
| | `-1_topology_3
| | |-1_topology_3
| | `-1_topology_3
| | |-grep --color=auto 1_topology_3
*/
void btree_fork(num)
{
num--;
if (num != -1)
{
int j = 0;
for (j = 0; j < 2; ++j)
{
int pid = fork();
if(pid == -1)
{
printf("error!\n");
}
else if(pid ==0)
{
printf("This is the child process %d_%d\n",j);
btree_fork(num);
getchar();
}
}
}
}
int main(int argc, char ** argv )
{
int i = 0;
btree_fork(3);
{
printf("This is the parent process!\n");
getchar();
wait(NULL);
}
return 0;
}
\ No newline at end of file
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <execinfo.h>
#include <unistd.h>
void my_backtrace()
{
void *buffer[100] = { NULL };
char **trace = NULL;
int size = backtrace(buffer, 100);
trace = backtrace_symbols(buffer, size);
if (NULL == trace) {
return;
}
for (int i = 0; i < size; ++i) {
printf("%s\n", trace[i]);
}
free(trace);
printf("----------done----------\n");
}
void thread(void)
{
// int i = 0;
my_backtrace();
printf("This is a pthread");
sleep(10);
}
int main(void)
{
pthread_t id[3];
int i,ret;
for(i=0;i<3;i++)
{
ret=pthread_create(&id[i],NULL,(void *) thread,NULL);
if(ret!=0)
{
// printf ("Create pthread error!\n");
exit (1);
}
}
// printf("This is the main process.");
my_backtrace();
for(i=0;i<10;i++)
{
pthread_join(id[i],NULL);
}
return (0);
}
\ No newline at end of file
#define SH_TOK_BUFSIZE 64
#define SH_TOK_DELIM " \t\r\n\a"
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
char *shell_read_line();
char **shell_split_line(char * line);
int shell_execute(char **args);
void shell_loop(void){
char *line;
char **args;
int status;
do {
printf(">>");
line = shell_read_line();
args = shell_split_line(line);
status = shell_execute(args);
free(line);
free(args);
}while(status);
}
char *shell_read_line(){
char *line = NULL;
ssize_t bufsize = 0; // getline()function will help us allocate a buffer
getline(&line,&bufsize,stdin);
return line;
}
char **shell_split_line(char *line){
int bufsize = SH_TOK_BUFSIZE;
int position = 0;
char **tokens = malloc(bufsize * sizeof(char*));
char **token;
if(!tokens){
fprintf(stderr,"Mycshell:allocation error!\n");
exit(EXIT_FAILURE);
}
token = strtok(line,SH_TOK_DELIM);
while(token != NULL){
tokens[position] = token;
position++;
if(position >= bufsize){
bufsize += SH_TOK_BUFSIZE;
tokens = realloc(tokens,bufsize * sizeof(char*));
if(!tokens){
fprintf(stderr,"Mycshell:allocation error!\n");
exit(EXIT_FAILURE);
}
}
token = strtok(NULL,SH_TOK_DELIM);
}
tokens[position] = NULL;
return tokens;
}
int shell_launch(char **args){
pid_t pid,wpid;
int status;
pid = fork();
if(pid == 0){
if(execvp(args[0],args) == -1){
perror("mycshell");
}
exit(EXIT_FAILURE);
} else if (pid < 0) {
//Error fork()
perror("mycshell");
} else {
do {
wpid = waitpid(pid,&status,WUNTRACED);
}while (!WIFEXITED(status) && !WIFSIGNALED(status));
}
return 1;
}
/* Builtin Function part*/
int shell_help(char **args);
int shell_exit();
char *builtin_str[] = {"help","exit"};
int (*builtin_func[])(char **) = {&shell_help,&shell_exit};
int shell_num_builtins(){
return sizeof(builtin_str) / sizeof(char *);
}
int shell_help(char **args){
int i;
printf("This is J's cshell,\n");
printf("Type program names and arguments,then enter.\n");
printf("The following are built in:\n");
for(i = 0;i < shell_num_builtins();i++){
printf(" %s\n",builtin_str[i]);
}
printf("Please use the man command for information on other programs.\n");
return 1;
}
int shell_exit(char **args){
return 0;
}
int shell_execute(char **args){
int i;
if(args[0] == NULL){
//if input an empty command
return 1;
}
for(i = 0;i<shell_num_builtins();i++){
if(strcmp(args[0],builtin_str[i]) == 0){
return (*builtin_func[i])(args);
}
}
return shell_launch(args);
}
int main(int argc,char **agrv){
//Load config files;
shell_loop();
//Shutdown or Clean up;
}
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define SH_TOK_BUFSIZE 64
#define SH_TOK_DELIM " \t\r\n\a"
char *shell_read_line();
char **shell_split_line(char * line);
int shell_execute(char **args);
void shell_loop(void){
char *line;
char **args;
int size;
while(1){
printf(">>");
line = shell_read_line();
args = shell_split_line(line);
shell_execute(args);
free(line);
free(args);
}
}
char *shell_read_line(){
char *line = NULL;
ssize_t bufsize = 0; // getline()function will help us allocate a buffer
getline(&line,&bufsize,stdin);
return line;
}
char **shell_split_line(char *line){
int bufsize = SH_TOK_BUFSIZE;
int position = 0;
char **tokens = malloc(bufsize * sizeof(char*));
char **token;
if(!tokens){
fprintf(stderr,"allocation error!\n");
exit(EXIT_FAILURE);
}
token = strtok(line,SH_TOK_DELIM);
while(token != NULL){
tokens[position] = token;
position++;
if(position >= bufsize){
bufsize += SH_TOK_BUFSIZE;
tokens = realloc(tokens,bufsize * sizeof(char*));
if(!tokens){
fprintf(stderr,"allocation error!\n");
exit(EXIT_FAILURE);
}
}
token = strtok(NULL,SH_TOK_DELIM);
}
tokens[position] = NULL;
return tokens;
}
// char *shell_read_line(){
// static char buff[1024];
// //scanf("%s", buff);
// fgets(buff, sizeof(buff), stdin);
// // printf("stren buff[%ld]\n", strlen(buff));
// buff[strlen(buff)-1] = '\0';
// return buff;
// }
int shell(char **args){
pid_t pid,wpid;
int status;
pid = fork();
if(pid == -1 )
{
printf("error!\n");
}
else if( pid ==0 )
{
if(execvp(args[0],args) == -1){
perror("shell");
}
exit(EXIT_FAILURE);
// execvp(args[0],args);
}
do {
wpid = waitpid(pid,&status,WUNTRACED);
}while (!WIFEXITED(status) && !WIFSIGNALED(status));
return 1;
}
int shell_execute(char **args)
{
// printf("%s\n",args[0] );
if (strcmp(args[0], "help") == 0){
printf("%s\n","This is a smaple shell build by C @Hypo");
}
else if(strcmp(args[0], "exit") == 0){
exit(0);
}
else if(strcmp(args[0], "cd") == 0){
printf("%s\n","No such command!" );
}
else{
shell(args);
}
}
int main(int argc, char **argv[])
{
shell_loop();
}
\ No newline at end of file
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
// int **addOne(int a[M][N]){
// int **b =(int **)malloc(M*sizeof(int *));
char **split(char **args, schar *str, char key){
// char **args = malloc(10 * sizeof(char*));
args[0] = "sef";
return args;
// static char args[10][64];
// int part = 0;
// int flag = 0;
// int i = 0;
// while(str[i] != '\0'){
// if (str[i] == key){
// int j;
// for (j = 0; j < (i-flag); ++j){
// args[part][j] = str[i];
// }
// args[part][i-flag]='\0';
// part++;
// flag = i;
// }
// // printf("%d\n",i );
// // printf("%c\n",str[i]);
// i++;
// }
// printf("%c\n",args[0][0] );
// return args;
}
int main(void)
{
char *str = "abc a";
char args[10][64];
// printf("%s\n",str );
char a[10][64] = split(args,str,'a');
// char **args = malloc(64 * sizeof(char*));
// args[0] = str;
// printf("%c\n",args[0][0] );
// int i = 0;
// while(str[i] != '\0'){
// printf("%c\n",str[i]);
// i++;
// }
// char* str2 = "abc";
// char *args[10];
// args[0] = str1;
// printf("%s\n", args[0]);
// if (args[3]=="/0")
// {
// printf("%s\n","true" );
// /* code */
// }
return 0;
}
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
//返回一个 char *arr[], size为返回数组的长度
char **split(char sep, const char *str, int *size)
{
int count = 0, i;
for(i = 0; i < strlen(str); i++)
{
if (str[i] == sep)
{
count ++;
}
}
char **ret = calloc(++count, sizeof(char *));
if (count == 0) {
ret[0] = calloc(strlen(str), sizeof(char)); //分配子串长度+1的内存空间
memcpy(ret[0], str, strlen(str) - 1);
return ret;
}
int lastindex = -1;
int j = 0;
for(i = 0; i < strlen(str); i++)
{
if (str[i] == sep)
{
ret[j] = calloc(i - lastindex, sizeof(char)); //分配子串长度+1的内存空间
memcpy(ret[j], str + lastindex + 1, i - lastindex - 1);
j++;
lastindex = i;
}
}
//处理最后一个子串
if (lastindex <= strlen(str) - 1)
{
ret[j] = calloc(strlen(str) - lastindex, sizeof(char));
memcpy(ret[j], str + lastindex + 1, strlen(str) - 1 - lastindex);
j++;
}
*size = j;
return ret;
}
void test(){
int size;
char **args;
char *str = "aaabbb";
args = split(',',str, &size);
printf("%s\n",args[0] );
}
int main()
{ int i =0;
for (i = 0; i < 10; ++i)
{
test();
}
// int i;
// for(i = 0; i < size; i++)
// {
// printf("%s\n", ret[i]);
// free(ret[i]);
// }
// free(ret);
return 0;
}
#include <unistd.h>
#include <signal.h>
#include <fcntl.h>
#include <sys/syslog.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int init_daemon(void)
{
int pid;
int i;
// 1)忽略一些控制终端发来的信号,信号的内容请参考其他文献
signal(SIGTTOU,SIG_IGN);
signal(SIGTTIN,SIG_IGN);
signal(SIGTSTP,SIG_IGN);
signal(SIGHUP ,SIG_IGN);
// 2)在后台运行
if( pid=fork() )
{ // 父进程
exit(0); //结束父进程,子进程继续
}
else if(pid< 0)
{ // 出错
perror("fork");
exit(EXIT_FAILURE);
}
// 3)脱离控制终端、登录会话和进程组
setsid();
// 4)禁止进程重新打开控制终端
if( pid=fork() )
{ // 父进程
exit(0); // 结束第1 代子进程,第2 代子进程继续(第2 代子进程不再是会话组长)
}
else if(pid< 0)
{ // 出错
perror("fork");
exit(EXIT_FAILURE);
}
// 5)关闭打开的文件描述符
// NOFILE 为文件描述符最大个数,不同系统有不同限制,<sys/param.h> 的宏定义
for(i=0; i< NOFILE; ++i)
{
close(i);
}
// 6)改变当前工作目录
chdir("/tmp");
// 7)重设文件创建掩模
umask(0);
// 8)处理 SIGCHLD 信号
signal(SIGCHLD,SIG_IGN);
// 9) 输出日志
openlog("daemon-demo.log",LOG_PID,0);
syslog(LOG_INFO,"daemon-demo is running ...\n");
return 0;
}
int main(int argc, char *argv[])
{
init_daemon();
while(1);
return 0;
}
\ No newline at end of file
#include <stdio.h>
#include <unistd.h>
int main(int argc, char ** argv )
{
int i;
for(i=0;i<100;i++)
{
int pid = fork();
if(pid == -1 )
{
printf("error!\n");
}
else if( pid ==0 )
{
printf("This is the child process!\n");
sleep(10);
return 0;
}
else
{
printf("This is the parent process! child process id = %d\n", pid);
}
}
sleep(10);
return 0;
}
\ No newline at end of file
#include <stdio.h>
#include <unistd.h>
int main(int argc, char ** argv )
{
int pid = fork();
if(pid == -1)
{
printf("error!\n");
}
else if(pid ==0)
{
printf("This is the child process!\n");
getchar();
}
else
{
printf("This is the parent process! child process id = %d\n", pid);
getchar();
wait(NULL);
}
return 0;
}
\ No newline at end of file
#include <stdio.h>
#include <unistd.h>
int main(int argc, char ** argv )
{
int pid = fork();
if(pid == -1 )
{
printf("error!\n");
}
else if( pid ==0 )
{
printf("This is the child process!\n");
char *argv[ ]={"ls", "-al", "/etc/passwd", NULL};
char *envp[ ]={"PATH=/usr/bin", NULL};
execve("/usr/bin/ls", argv, envp);
printf("this printf() will not be executed,because it will be replaced by /usr/bin/ls code!\n");
}
else
{
printf("This is the parent process! child process id = %d\n", pid);
getchar();
}
return 0;
}
\ No newline at end of file
#include <stdio.h>
#include <unistd.h>
int main(int argc, char ** argv )
{
int pid = fork();
if(pid == -1 )
{
printf("error!\n");
}
else if( pid ==0 )
{
printf("This is the child process!\n");
fork();
getchar();
}
else
{
printf("This is the parent process! child process id = %d\n", pid);
fork();
getchar();
}
return 0;
}
\ No newline at end of file
#include <stdio.h>
#include <unistd.h>
int main(int argc, char ** argv )
{
int pid = fork();
if(pid == -1)
{
printf("error!\n");
}
else if(pid ==0)
{
printf("This is the child process!\n");
getchar();
}
else
{
printf("This is the parent process! child process id = %d\n", pid);
getchar();
// wait(NULL);
}
return 0;
}
\ No newline at end of file
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
void thread(void)
{
printf("This is a pthread.\n");
sleep(10);
}
int main(void)
{
pthread_t id[100];
int i,ret;
for(i=0;i<100;i++)
{
ret=pthread_create(&id[i],NULL,(void *) thread,NULL);
if(ret!=0)
{
printf ("Create pthread error!\n");
exit (1);
}
}
printf("This is the main process.\n");
for(i=0;i<100;i++)
{
pthread_join(id[i],NULL);
}
return (0);
}
\ No newline at end of file
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
void thread(void)
{
printf("This is a pthread.\n");
sleep(10);
}
int main(void)
{
pthread_t id;
int i,ret;
ret=pthread_create(&id,NULL,(void *) thread,NULL);
if(ret!=0)
{
printf ("Create pthread error!\n");
exit (1);
}
printf("This is the main process.\n");
pthread_join(id,NULL);
return (0);
}
\ No newline at end of file
#!/usr/bin/bash
echo Running a shell script!
HelloWorld
\ No newline at end of file
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
main()
{
pid_t pid;
pid = fork();
if(pid < 0)
printf("error occurred!\n");
else if(pid == 0)
{
printf("Hi father! I'm a ZOMBIE\n");
exit(0); //no one waits for this process.
}
else
{
sleep(10);
wait(NULL); //the zombie process will be reaped now.
}
}
\ No newline at end of file
[toc]
## 实验目的
* 通过编程实践、观察,学习内存管理的方式。
## 实验环境
* 硬件
1.Intel(R) Xeon(R) Bronze 3104 CPU @ 1.70GHz
2.DDR4 16G
* 软件
1.CentOS 7
2.ubuntu 18.04
3.VMware Workstation Pro 15
## 实验记录
### 3-1 /proc/cpuinfo 信息
```bash
cat /proc/cpuinfo
```
![image](./record/pic/3-1.png)
CPU型号为Intel(R) Core(TM) i7-4710MQ CPU @ 2.50GHz,拥有四核心八线程
### 3-2 top 命令的输出
```bash
top
```
![image](./record/pic/3-2.png)
其中 7.7 us 表示 7.7%的时间用于运行用户空间的代码,相应地有 sy 对应运行内核代码占用 CPU 时间的百分比、ni 表示低优先级用户态代码占用 CPU时间的百分比、id 表示空闲(运行 idle 任务进程)CPU 时间的百分比、wa 表示 IO 等待占用CPU 时间的百分比、hi 表示硬件中断(Hardware IRQ)占用 CPU 时间的百分比、si 表示软中断(Software Interrupts)占用 CPU 时间的百分比以及和虚拟化有关的 st(steal time)占用 CPU时间的百分比。
### 3-3 top 命令的输出(展开 CPU 利用率)
![image](./record/pic/3-3.png)
### 3-4&3-5 全系统的调度统计
```bash
cat /proc/sched_debug
cat /proc/schedstat
```
![image](./record/pic/3-4.png)
![image](./record/pic/3-5.png)
### 3-6 ps –aux 查看进程状态
```bash
ps -aux
```
![image](./record/pic/3-6.png)
* R:TASK_RUNNING 可执行状态,包括就绪和正在 CPU 上执行
* S:TASK_INTERRUPTIBLE 可中断的睡眠状态,也是操作系统课程中所谓的阻塞状态
* D:TASK_UNINTERRUPTIBLE 不可中断的睡眠状态
* T:TASK_STOPPED 或 TASK_TRACED 暂停状态或跟踪状态
* Z:TASK_DEAD(-EXIT_ZOMBIE) 退出状态,且成为僵尸进程
* X:TASK_DEAD(-EXIT_DEAD) 退出状态,且进程即将被销毁
### 3-7&3-8 就绪与阻塞
3-7 HelloWorld-loop-getchar 的运行输出
![image](./record/pic/3-7.png)<br>
3-8 用 ps 观察 HelloWorld-loop-getchar 进程调度状态变化
```bash
ps aux|grep HelloWorld-loop-getchar
```
![image](./record/pic/3-8.png)
### 3-9&3-10 进程的调度统计
```bash
cat /proc/15366/status
```
![image](./record/pic/3-9.png)
进程主动切换的次数8,进程主动切换的次数213<br>
```bash
cat /proc/15363/stat
```
![image](./record/pic/3-10.png)
### 3-11&3-12&3-13&3-14普通进程的 CFS 调度
* nice 命令可以用-xx 给出调整的数值(降低优先级 xx),也可以用“-n xx”方式直接设定 NICE 值(-
20~19)
* taskset -c 0 在命令前加上这条语句可以绑定进程运行在制定CPU<br>
3-11 执行 Run-NICE.sh 脚本并用 ps –a 查看
![image](./record/pic/3-11.png)<br>
3-12 用 top 观察优先权不同的两个进程
![image](./record/pic/3-12.png)
16460 号进程占用了 CPU 的 90.4%的时间,16461 号进程仅占用了 CPU 的 9.6%的时间。<br>
3-13 /proc/PID/sched
![image](./record/pic/3-13_1.png)
![image](./record/pic/3-13_2.png)<br>
3-14 /proc/PID/schedstat
![image](./record/pic/3-14.png)
### 3-15&3-16创建实时进程
* 以 root 身份运行 RT-process-demo 进程,如果以普通用户运行 RT-process-demo 则会报告不允许创建实时进程。<br>
* 实时任务的均衡原则很简单,对于 N 个处理器的系统,保证优先级最高的 N 个实时任务各自在一个处理器上运行。如果实时任务少于 N 个,则多出来的处理器可以运行普通 CFS 任务。
3-15 用 ps –al 观察 RT-process-demo 的四个观测点
![image](./record/pic/3-15a.png)
![image](./record/pic/3-15b.png)
![image](./record/pic/3-15c.png)
![image](./record/pic/3-15d.png)<br>
3-16 用/proc/PID/sched 观察 RT-process-demo 的四个观测点
![image](./record/pic/3-16a.png)<br>
![image](./record/pic/3-16b.png)<br>
![image](./record/pic/3-16c.png)<br>
![image](./record/pic/3-16d.png)<br>
### 3-17 创建实时进程
```bash
sudo taskset -c 0 ./RR-FIFO.sh
```
经过多次测试发现实验结果无法在ubuntu18.04 多核处理(非虚拟机)器下复现,故改用centOS7进行实验<br>
top 命令查看高优先级实时进程10047 抢占 CPU
![image](./record/pic/3-17a.png)
![image](./record/pic/3-17b.png)
![image](./record/pic/3-17c.png)
3-17 运行脚本并查看三个实时进程的调度信息
```bash
# cat ./sched-10044; cat ./sched-10045; cat ./sched-10047
RR-FIFO-sched (10044, #threads: 1)
-------------------------------------------------------------------
nr_involuntary_switches : 198
se.load.weight : 1024
policy : 2实时任务的均衡原则很简单,对于 N 个处理器的系统,保证优先级最高的 N 个实时任务
各自在一个处理器上运行。如果实时任务少于 N 个,则多出来的处理器可以运行普通 CFS 任
务。
prio : 9
-------------------------------------------------------------------
nr_involuntary_switches : 199
se.load.weight : 1024
policy : 2
prio : 9
-------------------------------------------------------------------
nr_involuntary_switches : 25
se.load.weight : 1024
policy : 1
prio : 4
```
### 3-20 ~ 3-26 CFS 进程的负载均衡
执行 HelloWorld-loop-nice+5/+15 并查看它们的负载权重.
![image](./record/pic/3-18.png)
可以发现负载权重相差了3*3=9倍
3-20 显示进程所在处理器编号的 top 输出
top命令后按 f 进入管理界面根据提示操作打开Last Used Cpu
![image](./record/pic/3-20.png)
3-21 空闲系统上的即时负载情况
![image](./record/pic/3-21.png)
3-22 用 top 查看四个 NICE=-5 的进程所在 CPU
![image](./record/pic/3-22.png)
可以发现4个进程被分配在4个不同的CPU核心上运行
FIFO 任务执
行时不被打断一直到运行结束(除非更高优先级的实时任务来抢占),而后者的 RR 任务在执
行一个时间片之后将让出 CPU 给其他相同优先级的 RR 任务实现轮转执行。3-23 察看运行-5/0/+5/+10 四个进程后各处理器负载
![image](./record/pic/3-23.png)
3-24 从/proc/PID/sched 中查看进程的迁移次数
处理的进程数小于等于处理器核心数,基本不会进行调度
![image](./record/pic/3-24.png)
3-25 用 top 查看五个进程的处理器编号
在这个情况下处理的进程数大于处理器核心数,故会不断进行调度
![image](./record/pic/3-25a.png)
![image](./record/pic/3-25b.png)
![image](./record/pic/3-25c.png)
3-26 用/proc/PID/sched 查看进程迁移次数
![image](./record/pic/3-26.png)
### 3-27&3-28实时进程负载均衡
进程在八个处理器上运行
![image](./record/pic/3-27.png)
高优先级实时进程抢占低优先级实时进程的示例,开启一个优先级更高的进程
![image](./record/pic/3-28.png)
## 课后作业
### 1.在一个空闲的单核 Linux 系统上用 nice 命令调整两个进程的优先级,使得它们各自使用 1/5 和 4/5 的 CPU 资源。
```bash
cat Run-NICE.sh
taskset -c 0 ./HelloWorld-loop &
taskset -c 0 nice -6 ./HelloWorld-loop &
```
运行结果如下:
![image](./homework/1/1.png)
### 2.P1/P3 在第一个处理器上运行各占 50%的 CPU 资源,P2/P4 在另一个处理器上运行,各自 30%和 70%的 CPU 资源。
```bash
cat Run-NICE.sh
taskset -c 0 ./HelloWorld-loop &
taskset -c 0 nice -4 ./HelloWorld-loop &
taskset -c 1 ./HelloWorld-loop &
taskset -c 1 ./HelloWorld-loop &
```
运行结果如下:
![image](./homework/2/2.png)
### 3.请预测 Linux 对于相同优先级的 FIFO 实时进程的调度情况,并通过实验给出实测数据以证明。
教材中提到:
* FIFO 任务执行时不被打断一直到运行结束(除非更高优先级的实时任务来抢占),而后者的 RR 任务在执行一个时间片之后将让出 CPU 给其他相同优先级的 RR 任务实现轮转执行。
* FIFO 由于避免了调度切换的时间开销,因此更有利于本任务的快速完成。但是同时由于FIFO 也会造成同级 FIFO 任务的明显延迟.
故可以猜测 Linux 对于相同优先级的 FIFO 实时进程会安装先进先出的原则执行完一个进程后再执行另外一个。
实验结果如下:
1.启动两个相同优先级的FIFO 实时进程
![image](./homework/3/3_homework_3_1.png)
2.观察top结合1的打印结果,发现只有一个成功执行
![image](./homework/3/3_homework_3_2.png)
3.一段时间后第一个进程执行完毕,第二进程PID号才打印了出来,说明两者运行互斥
![image](./homework/3/3_homework_3_3.png)
![image](./homework/3/3_homework_3_4.png)
### 4.在一个空载的系统上,如果将代码 3-8 的最后一行替换成两个“RR-FIFO-sched 2 80”,请预测其负载均衡后进程的运行情况,并说明是否只有一种处理器分配方案?
```bash
cat RT-Balance.sh
./RR-FIFO-sched 1 5&
./RR-FIFO-sched 1 10&
./RR-FIFO-sched 1 15&
./RR-FIFO-sched 1 20&
./RR-FIFO-sched 1 25&
./RR-FIFO-sched 1 30&
./RR-FIFO-sched 1 35&
./RR-FIFO-sched 2 80&
./RR-FIFO-sched 2 80
```
知识点:
* 以 O(1)调度器方式调度——本处理器上的实时进程按照优先级不同挂入不同的子队列中,高优先级的队列先执行完再执行低优先级的队列上的进程。
* 实时进程可以进一步分为两种调度模式:SCHED_FIFO 和 SCHED_RR。前者的 FIFO 任务执行时不被打断一直到运行结束(除非更高优先级的实时任务来抢占),而后者的 RR 任务在执行一个时间片之后将让出 CPU 给其他相同优先级的 RR 任务实现轮转执行。
预测:后面运行的RR-FIFO-sched 2 80拥有高的优先级故会挂载进队列并开始运行,最低优先级的./RR-FIFO-sched 1 5 被挤出队列,待其他进程完成后再运行
实际结果(一开始运行就卡炸了):
首先,8个优先级较高的开始运行,最高占用不超过95%
此时可以观察到8个进程所占用的CPU核心会发生跳动
![image](./homework/4/4-1.png)
待8个优先级较高的进程执行完成后,优先级较低的进程开始执行
![image](./homework/4/4-2.png)
\ No newline at end of file
#include <stdio.h>
int main()
{
long i,j,temp;
printf("HelloWorld!\n");
while(1)
{
for (i=0;i<1024*1024*1024;i++)
{
temp+=1;
}
printf("one iteration!\n");
}
return 0;
}
\ No newline at end of file
taskset -c 0 ./HelloWorld-loop &
taskset -c 0 nice -6 ./HelloWorld-loop &
\ No newline at end of file
#include <stdio.h>
int main()
{
long i,j,temp;
printf("HelloWorld!\n");
while(1)
{
for (i=0;i<1024*1024*1024;i++)
{
temp+=1;
}
printf("one iteration!\n");
}
return 0;
}
\ No newline at end of file
taskset -c 0 ./HelloWorld-loop &
taskset -c 0 nice -4 ./HelloWorld-loop &
taskset -c 1 ./HelloWorld-loop &
taskset -c 1 ./HelloWorld-loop &
\ No newline at end of file
#include <sched.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
int main(int argc,char ** argv)
{
long i,j,temp;
char cmd_str[100];
int rc,current_scheduler_policy;
struct sched_param my_params;
//将要设置的调度参数
if(argc!=3)
{
printf("usage:RR-FIFO-sched sched_class priority \nsched_class: 0 for CFS; 1 forFIFO; 2 for RR\n");
exit(1);
}
my_params.sched_priority=atoi(argv[2]);
rc=sched_setscheduler(0,atoi(argv[1]),&my_params);
if(rc<0)
{
perror("sched_setscheduler error\n");
exit(0);
}
current_scheduler_policy=sched_getscheduler(0);
printf("the PID:%d current scheduler = %d \n", getpid(),current_scheduler_policy);
for(i=0;i<1024*1024*1024;i++)
for(j=0;j<10;j++)
temp++;
sprintf(cmd_str,"cat /proc/%d/sched > ./sched-%d ; date >> ./sched-%d", getpid(),
getpid(), getpid());
system(cmd_str);
//记录各个进程的/proc/PID/sched 以及时间信息
return 0;
}
\ No newline at end of file
taskset -c 0 ./RR-FIFO-sched 1 95&
taskset -c 0 ./RR-FIFO-sched 1 95
\ No newline at end of file
./RR-FIFO-sched 1 5&
./RR-FIFO-sched 1 10&
./RR-FIFO-sched 1 15&
./RR-FIFO-sched 1 20&
./RR-FIFO-sched 1 25&
./RR-FIFO-sched 1 30&
./RR-FIFO-sched 1 35&
./RR-FIFO-sched 2 80&
./RR-FIFO-sched 2 80
\ No newline at end of file
#include <stdio.h>
int main()
{
long i,j,temp;
printf("HelloWorld!\n");
while(1)
{
for (i=0;i<1024*1024*1024;i++)
{
temp+=1;
}
printf("one iteration!\n");
getchar();
}
return 0;
}
\ No newline at end of file
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册