【操作系统实验】---shell、多线程、信号量实验报告

操作系统实验报告

job6/sh3.c

题目要求

实现shell程序,要求支持基本命令、重定向命令、管道命令、后台命令。

解决思路

  1. type类型有TREE_BASICTREE_ASYNC,TREE_PIPE,TREE_REDIRECT,TREE_BASIC,TREE_TOKEN五种,分别是后台类型,管道类型,重定向类型,基本类型,叶子节点。tree_t结构体的type存放类型,token存放叶子节点的单词,child_count孩子数量(比如类型为TREE_PIPE的节点有两个孩子子树),child_vector指向子树;
  2. 根据节点类型不同,选择不同的子节点存放方式
    • 每个type为TREE_BASIC的子节点中存放单词;
    • 每个type为TREE_REDIRECT的第一个子节点中存放“>”或">>“或”<",其他节点中存放basic节点;
    • 每个type为TREE_PIPE的两个子节点中存放对应节点(可以是basic\redirect\pipe);
    • 如果type为TREE_ASYNC,一定是最后一个节点。
  3. 从根节点开始,每个节点tree_t执行exec
  4. 在exec中通过type进行判断:
    • 如果是TREE_PIPE,执行exec_pipe,exec_pipe中将 "|"左边的输出重定向至管道的写端,将“|”右边的输入重定向至管道的读端;
    • 如果是TREE_REDIRECT,执行exec_redirect,exec_redirect判断这个节点的第一个子节点,可能为“>”或“>>”或“<”,分别执行写文件重定向至标准输出、追加文件重定向至标准输出、读文件重定向至标准输入。
    • 如果是TREE_BASIC,执行exec_basic,除了“cd”、"pwd"操作,其他使用execvp()函数执行操作。
  5. 在main中,如果某节点类型为TREE_ASYNC,则不等待子进程结束,就可以执行下一个循环,以此实现后台命令。

关键代码

基本操作的执行,如果是cd操作就直接退出,pwd操作使用getcwd获取路径,其他的命令使用execvp装入程序

void exec_basic(tree_t *command_tree)
{
	if(strcmp(command_tree->child_vector[0]->token, "cd") == 0)
	{
		return;
	}
	else if(strcmp(command_tree->child_vector[0]->token, "pwd") == 0)
	{
		char *path = getcwd(NULL, 0);
		printf("%s\n", path);
		free(path);
		// exit(0);
	}
	else
	{
		int argc = command_tree->child_count;
		char *argv[argc+1];
		for(int i=0; i<argc; i++){
		argv[i] = command_tree->child_vector[i]->token;
		}
		argv[argc] = NULL;
		int error = execvp(argv[0], argv);
		if(error < 0)
		{
			perror(argv[0]);
		}
	}

}

管道操作,左子树的输出重定向到管道的写端,右子树的输入重定向到管道的读端。

void exec_pipe(tree_t *command_tree)
{
	int fd[2];
	int pid;
	pipe(fd);
	pid = fork();
	if(pid == 0){
		dup2(fd[1], 1);
		close(fd[0]);
		close(fd[1]);
		exec(command_tree->child_vector[0]);
		exit(0);
	}
	wait(NULL);
	dup2(fd[0], 0);
	close(fd[0]);
	close(fd[1]);
	exec(command_tree->child_vector[1]);
}

重定向操作,第一个孩子的token为“<”,打开文件读;为“>”,打开文件写;为“>>”,打开文件追加。

void exec_redirect(tree_t *command_tree)
{
	int fd;
	if(strcmp(command_tree->child_vector[1]->token, "<") == 0)
	{
		fd = open(command_tree->child_vector[2]->token, O_RDONLY, 0666);
		dup2(fd, 0);
		close(fd);
	}
	else if(strcmp(command_tree->child_vector[1]->token, ">") == 0)
	{
		fd = open(command_tree->child_vector[2]->token, O_CREAT|O_RDWR, 0666);
		dup2(fd, 1);
		close(fd);
	}
	else
	{
		fd = open(command_tree->child_vector[2]->token, O_APPEND|O_CREAT|O_RDWR, 0777);
		dup2(fd, 1);
		close(fd);
	}
	exec(command_tree->child_vector[0]);

}

执行代码

void exec(tree_t *command_tree)
{
	switch(command_tree->type)
	{
		case TREE_BASIC:
			exec_basic(command_tree);
			break;
		case TREE_PIPE:
			exec_pipe(command_tree);
			break;
		case TREE_REDIRECT:
			exec_redirect(command_tree);
			break;
		default:
			break;

	}

mian函数,循环接收输出的指令,根据指令构建语法树,exit指令就结束程序,cd指令就调用chdir,其他指令使用子进程调用exec,后台命令就不用等待子进程结束。

int main()
{
	char line[SIZE];
	int count;
	while(1){
		write(1, ">", sizeof(">"));
		count = read(0, line, sizeof(line)) -1;
		line[count] = 0;

		char *word_array[MAX_ARGC];
		int word_count = split(line, " ", word_array);


		tree_t *root = func(word_array, word_count);
		// tree_print(root,0);
		int async = 0;
		if(root->type == TREE_ASYNC){
			async = 1;
			root = root->child_vector[0];
		}

		if(root->child_vector[0]->token != NULL && strcmp(root->child_vector[0]->token, "exit") == 0){
			exit(0);
		}
		if(root->child_vector[0]->token != NULL && strcmp(root->child_vector[0]->token, "cd") == 0){
//			printf("cd\n");
			int error = chdir(root->child_vector[1]->token);
			if(error < 0){
				perror("cd");
			}
		}
		else{
			pid_t pid = fork();
			if(pid == 0){
				exec(root);
			}
		}
		if(!async)
			wait(NULL);
	}

	return 0;
}

运行结果

实现了shell程序,支持基本命令、重定向命令、管道命令、后台命令。

//基本输入
echo abc ddd
//输出
abc ddd

//重定向输入
echo abc >log
//输出
log文件中内容:abc

//管道输入
cat </etc/passwd | wc -l >log
//输出
log文件中内容:29

//后台输入
cat /etc/passwd >log &
//输出
log文件中内容是/etc/passwd中的内容

job7/pi2.c

题目要求

使用N个线程根据莱布尼兹级数计算PI

  • 能适应N个核心
  • 主线程创建N个辅助线程
  • 每个辅助线程计算一部分任务,并将结果返回
  • 主线程等待N个辅助线程运行结束,将所有辅助线程的结果累加
  • 本题要求 1: 使用线程参数,消除程序中的代码重复
  • 本题要求 2: 不能使用全局变量存储线程返回值

解决思路

  1. 在main中创建多个子线程,每个子线程执行compute ,子线程参数为param结构体,param中有compute累加需要的start与end参数;
  2. 在compute函数中,初始化sum=0,i从param->start开始增加,判断i,若i是奇数且i+1是4的倍数,sum减去i的倒数,若i是奇数且i+1不是4的倍数,sum加上i的倒数。循环直到i等于param->end,返回sum;
  3. main中等待所有子线程结束,获得所有子线程的返回值,将所有返回值累加再乘以4,则得到PI;
  4. 每个子线程的start与end由核心数CORE_N与END决定。

关键代码

参数结构体,返回值结构体

struct param{
	int start;
	int end;
};
struct result{
	double sum;
};

计算函数
i从param->start开始增加,判断i,若i是奇数且i+1是4的倍数,sum减去i的倒数,若i是奇数且i+1不是4的倍数,sum加上i的倒数。循环直到i等于param->end,返回sum。

void *compute(void *arg)
{
	struct param *param;
	struct result *result;
	double sum = 0;
	param = (struct param *)arg;
	for(int i=param->start;i<param->end;i++)
	{
		double num = i;
		if(i%2==1)
		{
			if((i+1)%4==0)
			{
			//	printf("-1/%d",i);
				sum-=1.0/num;
			}
			else
			{
			//	printf("+1/%d",i);
				sum+=1.0/num;
			}
		}
	}
	result = malloc(sizeof(struct result));
	result->sum = sum;
	return result;
}

主函数循环创建多个子线程,并等待子线程结束获得返回值,计算得PI。

int main()
{
	pthread_t worker_tids[CORE_N];
	struct param params[CORE_N];
	double total=0;
	for(int i=0;i<CORE_N;i++)
	{
		struct param *param;
		param = &params[i];
		param -> start = i*END/CORE_N;
		param -> end = (i+1)*END/CORE_N;
		pthread_create(&worker_tids[i],NULL,compute,param);
	}
	for(int i=0;i<CORE_N;i++)
	{
		struct result *result;
		pthread_join(worker_tids[i],(void**)&result);
	//	printf("CPU%d sum = %f\n",i,result->sum);
		total += result->sum;
		free(result);
	}
	total*=4;
	printf("pi = %f\n",total);
	return 0;
}

运行结果

当END取9999,CORE_N 取 10时,

结果为:PI=3.141793

job8/pc.c

题目要求

使用条件变量解决生产者、计算者、消费者问题

  • 系统中有3个线程:生产者、计算者、消费者
  • 系统中有2个容量为4的缓冲区:buffer1、buffer2
  • 生产者
    • 生产’a’、‘b’、‘c’、‘d’、‘e’、‘f’、‘g’、'h’八个字符
    • 放入到buffer1
    • 打印生产的字符
  • 计算者
    • 从buffer1取出字符
    • 将小写字符转换为大写字符,按照 input:OUTPUT 的格式打印
    • 放入到buffer2
  • 消费者
    • 从buffer2取出字符
    • 打印取出的字符
  • 程序输出结果(实际输出结果是交织的)
    a
    b
    c
    ...
        a:A
        b:B
        c:C
        ...
                A
                B
                C
                ...
    

解决思路

  1. 创建两个缓冲区,buffer1中存放小写字母,buffer2中存放大写字母
  2. get_item1()与put_item1()对buffer1进行操作,get_item2()与put_item2()对buffer2进行操作,
    buffer1_is_empty()与buffer1_is_full()对buffer1进行判断,buffer2_is_empty()与buffer2_is_full()对buffer2进行判断;
  3. main函数中创建子线程compute_tid与consumer_tid,入口函数分别是compute()与consume();
  4. 主线程调用produce(),如果buffer1是满的,则等待条件变量wait_empty_buffer1,向buffer1中放入产生的字母,并将发出buffer1满的信号;
  5. 子线程computer_tid调用compute(),buffer1是空的,则等待条件变量wait_full_buffer1,从buffer1中取出字母,并将发出buffer1空的信号。buffer2是满的则,等待条件变量wait_empty_buffer2,将小写字母转换为大写字母,向buffer2中放入产生的大写字母,并将发出buffer2满的信号;
  6. 子线程consumer_tid调用consume(),buffer2是空的,则等待条件变量wait_full_buffer2,从buffer2中取出字母,并将发出buffer2空的信号;
  7. main函数等待所有子线程结束再结束。

关键代码

生产者
互斥访问buffer1,等待条件变量wait_empty_buffer1,产生小写字母。

void *produce(void *arg)
{
	int i;
	int item;

	for(i=0;i < ITEM_COUNT;i++)
	{
		pthread_mutex_lock(&mutex_pc);
		while(buffer1_is_full())
		{
			pthread_cond_wait(&wait_empty_buffer1,&mutex_pc);
		}
		item = 'a' + i;
		put_item1(item);
		printf("%c\n",item);

		pthread_cond_signal(&wait_full_buffer1);
		pthread_mutex_unlock(&mutex_pc);

	}
	return NULL;

}

计算者
互斥访问buffer1,等待条件变量wait_full_buffer1,获取小写字母。
互斥访问buffer2,等待条件变量wait_empty_buffer2,产生大写字母。

//a:A
void *commpute(void *arg)
{
	int i;
	int item,ITEM;

	for(i=0;i<ITEM_COUNT;i++)
	{
		pthread_mutex_lock(&mutex_pc);
		while(buffer1_is_empty())
		{
			pthread_cond_wait(&wait_full_buffer1,&mutex_pc);
		}
		item = get_item1();
		pthread_cond_signal(&wait_empty_buffer1);
		pthread_mutex_unlock(&mutex_pc);

		pthread_mutex_lock(&mutex_cc);
		while(buffer2_is_full())
		{
			pthread_cond_wait(&wait_empty_buffer2,&mutex_cc);
		}
		ITEM = item+'A'-'a';
		put_item2(ITEM);
		printf("\t%c:%c\n",item,ITEM);
		pthread_cond_signal(&wait_full_buffer2);
		pthread_mutex_unlock(&mutex_cc);
	}
	return NULL;
}

消费者
互斥访问buffer2,等待条件变量wait_full_buffer2,获取大写字母。

void *consume(void *arg)
{
	int i;
	int item;
	for(i = 0;i<ITEM_COUNT;i++)
	{
		pthread_mutex_lock(&mutex_cc);
		while(buffer2_is_empty())
		{
			pthread_cond_wait(&wait_full_buffer2,&mutex_cc);
		}
		item = get_item2();
		printf("\t\t%c\n",item);
		pthread_cond_signal(&wait_empty_buffer2);
		pthread_mutex_unlock(&mutex_cc);
	}
	return NULL;
}

主函数
初始化互斥量、条件变量,创建子线程执行计算与消费,调用生产者,等待所有线程结束。

int main()
{

	pthread_t computer_tid;
	pthread_t consumer_tid;

	pthread_mutex_init(&mutex_pc,NULL);
	pthread_mutex_init(&mutex_cc,NULL);

	pthread_cond_init(&wait_empty_buffer1,NULL);
	pthread_cond_init(&wait_full_buffer1,NULL);
	pthread_cond_init(&wait_empty_buffer2,NULL);
	pthread_cond_init(&wait_full_buffer2,NULL);


	pthread_create(&computer_tid,NULL,commpute,NULL);
	pthread_create(&consumer_tid,NULL,consume,NULL);

	produce(NULL);
	pthread_join(computer_tid,NULL);
	pthread_join(consumer_tid,NULL);

	return 0;
}

运行结果

a
b
c
d
	a:A
		A
e
	b:B
		B
f
	c:C
		C
g
	d:D
		D
h
	e:E
	f:F
	g:G
		E
		F
		G
	h:H
		H

job9/pc.c

题目要求

使用信号量解决生产者、计算者、消费者问题

  • 系统中有3个线程:生产者、计算者、消费者
  • 系统中有2个容量为4的缓冲区:buffer1、buffer2
  • 生产者
    • 生产’a’、‘b’、‘c’、‘d’、‘e’、‘f’、‘g’、'h’八个字符
    • 放入到buffer1
    • 打印生产的字符
  • 计算者
    • 从buffer1取出字符
    • 将小写字符转换为大写字符,按照 input:OUTPUT 的格式打印
    • 放入到buffer2
  • 消费者
    • 从buffer2取出字符
    • 打印取出的字符
  • 程序输出结果(实际输出结果是交织的)
    a
    b
    c
    ...
        a:A
        b:B
        c:C
        ...
                A
                B
                C
                ...
    

解决思路

  1. 创建两个buffer,buffer1中存放小写字母,buffer2中存放大写字母;
  2. get_item1()与put_item1()对buffer1进行操作,get_item2()与put_item2()对buffer2进行操作,
    buffer1_is_empty()与buffer1_is_full()对buffer1进行判断,buffer2_is_empty()与buffer2_is_full()对buffer2进行判断;
  3. 建立信号量sema_t结构体,包含信号量的值、互斥量、条件变量。
    • sema_init():信号量初始化,
    • sema_wait():如果信号量的值小于等于0,则等待条件变量,将信号量的值减一
    • sema_signal():将信号量的值加一,唤醒等待条件变量的线程
  4. mutex1_sema 、 mutex2_sema用于互斥访问共享缓冲区变量in1、out1、in2、out2
  5. empty_buffer1_sema、full_buffer1_sema、empty_buffer2_sema、full_buffer3_sema用于线程同步;
  6. 主线程调用produce(),生产者需要一个空的buffer1,申请信号量empty_buffer1_sema,mutex1_sema用于对共享变量进行互斥访问,生产者线程生产一个数据后产生一个新的满buffer1,释放信号量full_buffer1_sema
  7. 子线程computer_tid调用compute():
    • full_buffer1_sema的数值表示空buffer的数量,消费者需要一个满的buffer1,申请请信号量full_buffer1_sema,mutex1_sema用于对共享变量进行互斥访问,消费者线程取走一个数据后,产生一个新的空buffer1,释放信号量empty_buffer1_sema
    • 将小写字母转换为大写字母
    • 还需要一个空的buffer2,申请信号量empty_buffer2_sema,mutex2_sema用于对共享变量进行互斥访问,生产者线程生产一个数据后产生一个新的满buffer2,释放信号量full_buffer2_sema
  8. 子线程consumer_tid调用consume(),full_buffer2_sema的数值表示空buffer的数量,消费者需要一个满的buffer2,申请请信号量full_buffer2_sema,mutex2_sema用于对共享变量进行互斥访问,消费者线程取走一个数据后,产生一个新的空buffer2,释放信号量empty_buffer2_sema
  9. 初始化信号量,创建消费者线程,主线程作为生产者,执行produce函数,子线程compute_tid与consumer_tid,入口函数分别是compute()与consume()。main函数等待所有子线程结束再结束。

关键代码

生产者
等待信号量empty_buffer1_sema,等待信号量mutex1_sem,产生小写字母。

void *produce(void *arg)
{
	int i;
	int item;
	for(i=0;i<ITEM_COUNT;i++)
	{
		sema_wait(&empty_buffer1_sema);
		sema_wait(&mutex1_sema);

		item = i+'a';
		put_item1(item);
		printf("%c\n",item);
		sema_signal(&mutex1_sema);
		sema_signal(&full_buffer1_sema);
	}
	return NULL;
}

计算者
等待信号量full_buffer1_sema,等待信号量mutex1_sem,获取小写字母。
等待信号量empty_buffer2_sema,等待信号量mutex2_sem,产生大写字母。

void *commpute(void *arg)
{
	int i;
	int item;
	int ITEM;

	for(i=0;i<ITEM_COUNT;i++)
	{
		sema_wait(&full_buffer1_sema);
		sema_wait(&mutex1_sema);

		item = get_item1();
		ITEM = item+'A'-'a';
//		printf("commpute:%c\n",item);
		printf("\t %c:%c\n",item,ITEM);

		sema_signal(&mutex1_sema);
		sema_signal(&empty_buffer1_sema);


		sema_wait(&empty_buffer2_sema);
		sema_wait(&mutex2_sema);

		put_item2(ITEM);

		sema_signal(&mutex2_sema);
		sema_signal(&full_buffer2_sema);
	}
	return NULL;
}

消费者
等待信号量full_buffer2_sema,等待信号量mutex2_sem,获取大写字母。

void *consume(void *arg)
{
	int i;
	int item;

	for(i=0;i<ITEM_COUNT;i++)
	{
		sema_wait(&full_buffer2_sema);
		sema_wait(&mutex2_sema);

		item = get_item2();
		printf("\t\t%c\n",item);
		sema_signal(&mutex2_sema);
		sema_signal(&empty_buffer2_sema);
	}
	return NULL;
}

主函数
初始化信号量,创建子线程执行计算与消费,调用生产者,等待所有线程结束。

int main()
{
	pthread_t commpute_tid;
	pthread_t consume_tid;
	sema_init(&mutex1_sema,1);
	sema_init(&mutex2_sema,1);
	sema_init(&empty_buffer1_sema,CAPACITY-1);
	sema_init(&full_buffer1_sema,0);
	sema_init(&empty_buffer2_sema,CAPACITY-1);
	sema_init(&full_buffer2_sema,0);
	pthread_create(&commpute_tid,NULL,commpute,NULL);
	pthread_create(&consume_tid,NULL,consume,NULL);
	produce(NULL);
	pthread_join(commpute_tid,NULL);
	pthread_join(consume_tid,NULL);
	return 0;
}

运行结果

a
b
c
d
	a:A
		A
e
	b:B
		B
f
	c:C
		C
g
	d:D
		D
h
	e:E
	f:F
	g:G
		E
		F
		G
	h:H
		H

job10/pfind.c

题目要求

  1. 功能
  • 功能与 sfind 相同
  • 要求使用多线程完成
    • 主线程创建若干个子线程
      • 主线程负责遍历目录中的文件
      • 遍历到目录中的叶子节点时
      • 将叶子节点发送给子线程进行处理
    • 两者之间使用生产者消费者模型通信
      • 主线程生成数据
      • 子线程读取数据
  1. 图示
  • 主线程创建 2 个子线程
    • 主线程遍历目录 test 下的所有文件
    • 把遍历的叶子节点 path 和目标字符串 string,作为任务,发送到任务队列
  • 子线程
    • 不断的从任务队列中读取任务 path 和 string
    • 在 path 中查找字符串 string

在这里插入图片描述

解决思路

  1. buffer格式为task结构体,task存储is_end判断是否是最后一个文件,path找到的文件的路径,string含有要查找单词的行;
  2. buffer_is_empty()与buffer_is_full()对buffer进行判断,mutex对buffer进行互斥访问, wait_empty_buffer、wait_full_buffer两个条件变量用于存buffer、取buffer。
  3. 在get_item与put_item中互斥访问buffer:
    • get_item中,如果buffer_is_empty,要等条件变量wait_full_buffer再取文件,并设置wait_empty_buffer
    • put_item中,如果buffer_is_full,要等条件变量wait_empty_buffer再存文件,并设置wait_full__buffer。
  4. 在文件中查找含有单词的行,逐行读取文件,再使用strstr()即可;
  5. 子线程入口函数worker_entry中有get_item,获取要处理的文件,如果是最后一个文件,在结束子进程之前,再向buffer中放入一个is_end=1的task,让其他的子进程也可以结束。
  6. 主线程要找出某目录下所有的文件,使用find_dir函数,opendir()进入目录,判断每个元素,是文件就执行put_item放入buffer,是目录就加入目录名字递归查找
  7. 主函数 先判断目标路径是不是文件,是文件就直接find_file,不是就find_dir。添加一个is_end=1的task作为结束的元素,再创建多个子进程,每个子进程通过worker_entry处理文件。等待子进程结束

关键代码

读写buffer

struct task get_item()
{
	struct task item;
	pthread_mutex_lock(&mutex);
	while(buffer_is_empty())
	{
		printf("wait_full\n");
		pthread_cond_wait(&wait_full_buffer,&mutex);
	}
	item = buffer[out];
	out = (out+1)% CAPACITY;
	pthread_cond_signal(&wait_empty_buffer);
	pthread_mutex_unlock(&mutex);
	return item;
}

void put_item(struct task item)
{
	pthread_mutex_lock(&mutex);
	while(buffer_is_full())
	{
		pthread_cond_wait(&wait_empty_buffer,&mutex);
	}
	buffer[in]=item;
	in = (in+1)%CAPACITY;
	pthread_cond_signal(&wait_full_buffer);
	pthread_mutex_unlock(&mutex);
}

找文件,找到文件放入buffer

void find_dir(char *path,char *target)
{
	DIR *dir = opendir(path);
	struct dirent *entry;
	struct task buffer;
	while(entry = readdir(dir)){
		if(strcmp(entry->d_name,".") == 0)
			continue;
		if(strcmp(entry->d_name,"..") == 0)
			continue;
		if(entry->d_type == DT_DIR)
		{
			char base[80];
			memset(base,'\0',sizeof(base));
			strcpy(base,path);
			strcat(base,"/");
			strcat(base,entry->d_name);
//			printf("base:%s\n",base);
			find_dir(base,target);

		}
		if(entry->d_type == DT_REG)
		{
			char base[80];
			memset(base,'\0',sizeof(base));
			strcpy(base,path);
			strcat(base,"/");
			strcat(base,entry->d_name);
			//join array
			buffer.is_end=0;
			strcpy(buffer.path,base);
			strcpy(buffer.string,target);
			put_item(buffer);
//			printf("put_item\n");
			break;
		}
	}
	closedir(dir);

}

主函数,如果输入的是路径,找出所有文件,给buffer,创建子线程,查找含单词的行。

int main(int argc,char *argv[])
{
	char *path = argv[1];
	char *string = argv[2];

	if(isfile(path))
	{
		find_file(path,string);
		return 0;
	}
	pthread_t consumer_tid[WORKER_NUMBER];
	find_dir(path,string);
	struct task buf;
	buf.is_end=1;
	put_item(buf);

	//create
	for(int i=0;i<WORKER_NUMBER;i++)
	{
		pthread_create(&consumer_tid[i],NULL,worker_entry,NULL);

	}
	//is_end

	//exit
	for(int i=0;i<WORKER_NUMBER;i++)
	{

		pthread_join(consumer_tid[i],NULL);
	}

	return 0;
}

运行结果

指令:
./a.out test/world/world.c int
输出:

test/world/world.c: 			printf("%s: %s",path,line);
test/world/world.c: 			printf("dir %s\n",entry->d_name);
test/world/world.c: 			printf("file %s\n",entry->d_name);
test/world/world.c: int main(int argc,char *argv[])

指令:
./a.out test main
输出:

test/world/world.c: int main(int argc,char *argv[])
test/hello/hello.c: int main(int argc,char *argv[])

总结

在本学期的操作系统实验这门课学习当中,为了更好的了解操作系统相关知识,我们在Linux系统做了10个实验。在实验的过程中,我对课堂上学到的操作系统的一些知识有了新的认识,通过实验动手写shell让我深入了解了命令执行原理,通过代码实现了生产者-消费者加深了我对条件变量、信号量的理解,收获颇丰。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值