操作系统 复习--实训题

一. 简答题(共8题,100分)

1. (简答题)

编程使用fork()函数创建子进程,要求父进程中打印当前进程的 PID 和子进程的 PID,而在子进程中只打印当前进程的 PID。

参考代码:

int main() { 

	pid_t child_pid;
    child_pid = fork();   

    if (child_pid == -1) {
        perror("fork");      

       return 1;
    }    // 子进程返回 0
    if (child_pid == 0) {        

     printf("This is the child process, pid = %d\n", getpid());
    } else {  // 父进程返回子进程的 PID
        printf("This is the parent process, pid = %d, child_pid = %d\n", getpid(), child_pid);
    }    return 0;
}

参考资料
【Linux】——进程创建fork()详解

2. (简答题)

在多线程编程中,为了避免多个线程同时访问共享资源而引起的问题,需要使用信号量(Semaphore)实现线程同步。信号量本质上是一个计数器,它记录某个共享资源可被访问的数量。当一个线程想要获取该共享资源时,需要先尝试获取信号量的锁,若获取成功则可以访问该资源,否则需要等待。而当一个线程释放该共享资源时,需要将信号量的计数器加一,以便其他线程可以进行访问。
阅读代码填空:

#define NUM_THREADS 5

int shared_resource = 0;  // 共享资源sem_t mutex;  // 互斥锁信号量// 线程函数,每个线程对共享资源加 1

void* thread_function(void* arg) 
{   
	int thread_id = *((int*)arg);
    sem_wait(&mutex);  // 获取互斥锁信号量
    printf("Thread %d is accessing the shared resource...\n", thread_id);
    shared_resource++;  // 对共享资源加 1
    printf("Thread %d updated the shared resource to %d\n", thread_id, shared_resource);
    sem_post(&mutex);  // 释放互斥锁信号量
    pthread_exit(NULL);
}

int main() 
{ 
	pthread_t threads[NUM_THREADS];   
	int thread_ids[NUM_THREADS];    int i;
    sem_init(&mutex, 0, 1);  // 初始化互斥锁信号量
    for (i = 0; i < NUM_THREADS; i++) {
        thread_ids[i] = i + 1;
        pthread_create(&threads[i], NULL, thread_function, &thread_ids[i]);
    }    
   for (i = 0; i < NUM_THREADS; i++) {
        pthread_join(threads[i], NULL);
    }
    sem_destroy(&mutex);  // 销毁互斥锁信号量
    return 0;
}

画横线的地方就是要考的地方

操作系统 复习--实训题_第1张图片

参考答案:

试获取信号量的锁,若获取成功则可以访问该资源,否则需要等待。而当一个线程释放该共享资源时,需要将信号量的计数器加一,以便其他线程可以进行访问。

阅读代码填空:
#define NUM_THREADS 5int shared_resource = 0;  // 共享资源sem_t mutex;  // 互斥锁信号量// 线程函数,每个线程对共享资源加 1

void* thread_function(void* arg) 
{   
	int thread_id = *((int*)arg);
    sem_wait(&mutex);  // 获取互斥锁信号量
    printf("Thread %d is accessing the shared resource...\n", thread_id);
    shared_resource++;  // 对共享资源加 1
    printf("Thread %d updated the shared resource to %d\n", thread_id, shared_resource);
    sem_post(&mutex);  // 释放互斥锁信号量
    pthread_exit(NULL);
}

int main() 
{ 
	pthread_t threads[NUM_THREADS];   
	int thread_ids[NUM_THREADS];    int i;

    sem_init(&mutex, 0, 1);  // 初始化互斥锁信号量

    for (i = 0; i < NUM_THREADS; i++) {
        thread_ids[i] = i + 1;
        pthread_create(&threads[i], NULL, thread_function, &thread_ids[i]);
    }    
	for (i = 0; i < NUM_THREADS; i++) {
        pthread_join(threads[i], NULL);
	}
    sem_destroy(&mutex);  // 销毁互斥锁信号量

    return 0;
}

3. (简答题)

使用 pipe() 函数创建了一个匿名管道,然后通过 fork() 函数创建了一个子进程。父进程向管道中写入字符串 “Hello, world!”,而子进程从管道中读取数据并输出。

参考代码:

#define BUFFER_SIZE 25
int main()
{  
	int fd[2];  
	char write_msg[BUFFER_SIZE] = "Hello, world!";  
	char read_msg[BUFFER_SIZE];  
	pid_t pid;  
	if (pipe(fd) == -1) {  // 创建匿名管道
		fprintf(stderr, "Pipe failed");  
		return 1;
	}
	pid = fork();  // 创建子进程
	if (pid < 0) {  // 创建子进程失败
		fprintf(stderr, "Fork failed");
		return 1;
	} else if (pid == 0) {  // 子进程     
		close(fd[1]);  // 关闭写端口     
		read(fd[0], read_msg, BUFFER_SIZE);  // 从管道中读取数据      
		printf("Child process received message: %s\n", read_msg);
	} else {  // 父进程        
		close(fd[0]);  // 关闭读端口    
    	write(fd[1], write_msg, BUFFER_SIZE);  // 向管道中写入数据   
		printf("Parent process sent message: %s\n", write_msg);
	}    
	return 0;
}

4. (简答题)

生产者消费者问题描述 [1] 生产者消费者问题,也称为有限缓冲问题,是一个经典的多进程同步问题。该问题描述了两个进程之间如何在实际运行时共享固定大小的缓冲区。一个进程是生产者,它生成一些数据并将其放入缓冲区;另一个进程是消费者,它从缓冲区中取出数据并将其处理。由于缓冲区具有固定的大小,因此生产者和消费者必须在缓冲区不满和不空的情况下进行同步。
给出同步条件和算法描述:

同步条件 为了遵守同步条件,生产者和消费者必须遵循以下规则:

生产者只有在缓冲区不满时才能添加数据到缓冲区;
消费者只有在缓冲区不空时才能从缓冲区中取出数据;
生产者在向缓冲区中添加数据后应该将其唤醒消费者进程,以便消费者可以及时处理数据;
消费者在取出数据后应该将其唤醒生产者进程,以便生产者可以及时生成数据。

算法描述

可以用一个数组表示缓冲区。在本算法中,将包含生产者和消费者的程序称为主程序。

主程序:

定义缓冲区数组,初始化缓冲区为空
定义互斥量mutex和信号量full、empty分别表示缓冲区是否满或空
创建消费者线程和生产者线程,启动线程
等待子线程完成后,销毁互斥量和信号量

生产者进程:

生成数据项
若缓冲区满,等待信号量empty
获取互斥锁mutex
将数据项放入缓冲区
释放互斥锁mutex
发送信号量full

消费者进程:

若缓冲区空,等待信号量full
获取互斥锁mutex
取出缓冲区中的数据项
释放互斥锁mutex
发送信号量empty
处理数据项

在这个算法描述中,互斥量和信号量是用于确保线程安全和同步的关键元素。互斥量用于控制对共享资源(缓冲区)的访问,以确保同时只有一个线程能够访问它。信号量用于控制进程之间的同步,并向另一个进程发出信号以指示某些事件已经发生。

参考答案:

while(1){  
    produce an item in nextp; //生产数据  
    P(empty); //(要用什么P一下) //获取空缓冲区单元  
    P(mutex);                 //互斥,进入临界区  
    add nextp to buffer;      //将数据放入缓冲区  
    V(mutex);                 //离开临界区,释放互斥信号量  
    V(full);                  //满缓冲区数加1,相当于放入缓冲区了,让缓冲区的数加1  
}

consumer(){  
	while(1){  
		P(full);//获取满缓冲区单元,如果没有数据就等待  
		P(mutex);//进入缓冲区  
		remove an item from buffer;//从缓冲区取出数据  
		V(mutex);//离开临界区,释放互斥信号量  
		V(empty);//空缓冲区数加1  
    	consume the item;//消费数据  
	}
}

5. (简答题)

实训链接:创建进程选择题

1、画出下列程序的进程树:

#include 
#include 

int main()
{
	fork();
	fork();
	fork();
	fork();
	printf("ok\n");
	return 0;
}

参考:
两个图的答案都正确,推荐手写的这种方法

写法一:
操作系统 复习--实训题_第2张图片
写法二:
操作系统 复习--实训题_第3张图片
参考链接:
三个连续的fork形成的进程数树怎么画

2、下面程序执行后输出几个“!”,并说明原因。

#include 
#include 

int main()
{
	int i;
	for(i = 0; i < 2; i++){
        fork();
		printf("!\n");
	}
	return 0;
}

参考:
一共输出 6 个。
 
首先程序一开始,bash产生一个进程 P0 执行此程序,P0 进入程序。
 
当 i=0 时:
fork() 产生一个子进程P1,同时它自己输出一个’!'。P1 继承 P0 的诸如环境变量,P1 首先会输出一个 ‘!’。
 
当 i=1 ,会继续执行 for 循环— P1 先 fork() 出一个子进程 P2,同时再输出一个 ‘!’。
P2 进程为 P1 的子进程,它会复制其父进程P2的指令,变量值,程序调用栈,环境变量,缓冲区等,它会输出一个 ‘!’。
 
此时 P0 进入程序后,当 i=1 时,fork() 产生另一个它的子进程P3,同时输出一个 ‘!’。P3 同样会输出一个 ‘!’。
 
下面是图示
操作系统 复习--实训题_第4张图片
 
参考链接
fork()请问下面的程序一共输出多少个“-”?

6. (简答题)

程序员编程过程中,经常需要进程通信,操作系统提供的进程间通信机制主要包括哪些?

参考答案:
1.管道(Pipe):一种单向通信机制,只能在父子进程或兄弟进程之间使用。
2.命名管道(Named Pipe):类似于管道,但可以在不同进程之间使用。
3.消息队列(Message Queue):可以在不同进程之间传递消息,支持多对多通信。
4.共享内存(Shared Memory):可以在不同进程之间共享同一块物理内存,速度很快。
5.信号量(Semaphore):可以用来控制对共享资源的访问,避免竞争条件的发生。
6.套接字(Socket):可以在不同主机之间进行网络通信。
7.信号(Signal):可以用来通知进程某个事件已经发生。

7. (简答题)

实训链接:进程基础知识

  1. 临界区是指并发进程中涉及共享变量的()。
    A、程序段
    B、管理信息区
    C、公共数据区
    D、信息存储区

  2. 下列有关fork()函数返回值说法错误的是()
    A、函数成功返回时,一次返回两个值,错误返回时为-1
    B、返回值等于0表示子进程
    C、返回值大于0表示父进程
    D、大于0返回值为父进程的PID号

  3. 下面程序的输出是什么()
    A、helloworld
    B、wordhello
    C、hello
    D、不确定

    #include 
    #include 
    
    int main(int argc, char** argv){
       if(fork() == 0){
           printf("hello");
       }else{
           printf("world");
       }
       return 0;
    }
    
  4. 下面说法不正确的是( )
    A、管道和命名管道是最早进程间通信机制之一
    B、消息队列是将消息按队列的方式组织成的链表,每个消息都是其中的一个节点
    C、进程创建一般由create函数完成
    D、共享内存和消息都是由Linux内核来管理和分配资源

  5. 关于SIGCHLD信号说法错误的是()
    A、在子进程退出时,会向父进程发送该信号
    B、需要及时处理SIGCHLD防止僵尸进程
    C、SIGCHLD信号的默认处理方式是忽略
    D、由于SIGCHLD信号默认方式是忽略,所以在代码中不需要手动设置SIGCHLD信息的处理方式,也不会产生僵尸进程

  6. 下列哪种通信方式只能用于具有亲缘关系进程之间的通信()
    A、匿名管道
    B、消息队列
    C、共享内存
    D、命名管道

8. (简答题)

实训链接:文件系统基础

  1. 比较文件的差异要用到的命令是以下哪一种?(单选)
    A、diff
    B、cat
    C、wc
    D、head

  2. 存放设备文件的相关文件目录是?(单选)
    A、/dev
    B、/etc
    C、/lib
    D、/bin

  3. rm命令表示什么?(单选)
    A、文件复制命令
    B、移动文件命令
    C、文件内容统计命令
    D、文件删除命令

  4. 在openEuler系统中,用户文件描述符0表示?(单选)
    A、标准输出设备文件描述符
    B、标准输入设备文件描述符
    C、管道文件描述符
    D、标准错误输出设备文件描述符

    注释:
    0是标准输入,1是标准输出,2是标准错误
     
    参考:
    彻底弄懂 Linux 下的文件描述符(fd)

  5. 在使用 mkdir命令创建新的目录时,在其父目录不存在时先创建父目录的选项是?(单选)
    A、-d
    B、-m
    C、-p
    D、-f

  6. 执行命令“chmod o+rw myfile”后,myfile文件的权限变化为?(单选)
    A、所有用户都可读写myfile文件
    B、其他用户可读写myfile文件
    C、同组用户可读写myfile文件
    D、文件所有者读写myfile文件
    操作系统 复习--实训题_第5张图片
    操作系统 复习--实训题_第6张图片

    参考链接
    Linux chmod命令

你可能感兴趣的:(操作系统实训,linux)