linux系统编程试卷(答案)
凌阳教育
嵌入式培训系统编程部分测试试题
注:考试为闭卷,程序题需上机操作运行出结果,考试时间为120分钟
一:选择题(本题共4小题,每题3分共12分)
1)下列不是Linux系统进程类型的是( D )
A 交互进程
B 批处理进程
C 守护进程
D 就绪进程(进程状态)
2)以下对信号的理解不正确的是( B )
A 信号是一种异步通信方式
B 信号只用在用户空间进程通信,不能和内核空间交互
C 信号是可以被屏蔽的
D 信号是通过软中断实现的
3)进程有三种状态( C )
A 准备态、执行态和退出态
B 精确态、模糊态和随机态
C 运行态、就绪态和等待态
D 手工态、自动态和自由态
4)不是进程和程序的区别( B)
A 程序是一组有序的静态指令,进程是一次程序的执行过程
B 程序只能在前台运行,而进程可以在前台或后台运行
C 程序可以长期保存,进程是暂时的
D 程序没有状态,而进程是有状态的
二:填空题(本题共6小题,2)、3)两题每空四分,其余每空一分。共23分)
1) 列举八种常见的进程间通信方式无名管道、有名管道、消息队列、信号量、共享内存、信号、套接字
网络上两个主机的进程间通信方式为套接字
2) 命名管道比无名管道的优势提供了一个可以访问的路径名,实现没亲缘关系的进程
间通信
3) 消息队列比命名管道和无名管道的优势可以按类型实现消息的随机查询,没必要先
进先出
4) 按照逻辑结构不同进行数据库划分,Sqlite 数据库属于哪一类关系型数据库
5) 在C语言中操作sqlite数据库,常用的2中方式是sqlite_exec(回调)、
sqlite_gettable(非回调)
6) 列举四种进程调度算法先来先调度(FCFS)、短进程优先调度(SPF)、高优先级调度
(HPF)、时间片轮转调度
三:问答题(本题共7题,每题5分,共35分)
1) 什么是系统调用?系统调用是通过什么方式陷入内核态的?请写出你对系统调用的理解。什么是文件I/O和标准I/O库?文件I/O和标准I/O库的区别?
系统调用是指操作系统提供给用户程序调用的一组特殊接口,用户程序可以通过这组接口获得操作系统内核提供的服务。
系统调用是通过软件中断方式陷入内核的
linux的文件I/O是由操作系统提供的基本IO服务, 标准I/O库通过封装系统调用,提供了一个到底层I/O的接口。
标准I/O默认采用了缓冲机制,还创建了一个包含文件和缓冲区相关数据的数据结构;文件I/O一般没有采用缓冲模式,需要自己创建缓冲区。一种是标准库封装系统调用而成,更高级,一种是系统提供的,比较低级;标准I/O可移植性高、文件I/O可移植性低。
2) 什么是进程?用fork()创建一个子进程时,系统会做什么工作
进程是具有独立功能的程序关于某个数据集合上的一次运行活动,是系统进行资源分配的单位,不仅是系统内部独立运行的实体也是独立竞争资源的实体。
用fork()时系统会分配子进程一个ID号然后继承父进程的地址空间,包括进程上下文进程堆栈打开的文件描述符等等,他就是父进程的一个复制品。
3) 进程和线程有什么区别?
每个独立的进程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能够独立执行,必须依存在进程程中,由进程提供多个线程执行的控制。
进程是系统进行资源分配和调度的一个独立单位. 线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源.
一个线程可以创建和撤销另一个线程;同一个进程中的多个线程之间可以并发执行。
4) 什么是线程的互斥和同步,程序应怎样写才能达到互斥或同步?
互斥:是指某一资源同时只允许一个访问者对其进行访问,具有唯一性和排它性。但互斥无法限制访问者对资源的访问顺序,即访问是无序的。
同步:是指在互斥的基础上(大多数情况),通过其它机制实现访问者对资源的有序访问。在大多数情况下,同步已经实现了互斥,特别是所有写入资源的情况必定是互斥的。少数情况是指可以允许多个访问者同时访问资源。
在写程序时可以用互斥锁和信号量实现线程同步,一个线程访问共享资源时给这个资源上锁其他线程就不能访问了直到上锁的进程释放互斥锁为止。
5) 什么是僵尸进程?孤儿进程?守护进程?
僵尸进程:僵尸进程是指它的父进程已经退出(父进程没有等待(调用wait/waitpid)它),而该进程dead之后没有进程接受,就成为僵尸进程,也就是(zombie)进程。
孤儿进程:一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成
为孤儿进程。孤儿进程将被init进程(进程号为1)所收养,并由init进程对它们完成状态收集工作。
守护进程:是指在UNIX或其他多任务操作系统中在后台执行的电脑程序,并不会接受电脑用户的直接操控。此类程序会被以进程的形式初始化。守护进程程序的名称通常以字母“d”结尾:例如,syslogd就是指管理系统日志的守护进程。通常,守护进程没有任何存在的父进程(即PPID=1),且在UNIX系统进程层级中直接位于init之下。守护进程程序通常通过如下方法使自己成为守护进程:对一个子进程调用fork,然后使其父进程立即终止,使得这个子进程能在init下运行。这种方法通常被称为“脱壳”
6) 有3个线程:A、B、C,现要求A先执行,然后B再执行,最后C再执行,之后
A->B->C循环执行,请说出大体的思路?
创建3个信号量sem_A、sem_B、sem_C,分别用来给A、B、C 3个线程,启动sem_A信号量的初始值为1,其余2个信号量初始值为0,当主线程创建3个子线程后,因为sem_A 的值大于0,所以只有A线程执行相关的代码,B、C线程堵塞在对sem_B、sem_C信号量的p操作;
当A线程执行完相应的代码后,给sem_B信号量加1,之后B线程开始执行相关操作,在此时A线程堵塞在对sem_A信号量的p操作,C线程继续堵塞;当B线程执行完毕相关操作后,给sem_C进行V操作,B线程堵塞,C线程执行相关操作;
以上是从A->B->C线程执行的流程.
7) exit()、_exit()和return()的区别?
exit():结束当前的进程,并且会刷新缓存区,关闭没有关闭的文件等
_exit():结束当前的进程,不对缓存区刷新
return:
1:在main函数中会结束当前进程
2:在子函数中,会返回调用当前函数的调用位置,进程从下个C语句开始执行
四:程序题(本小题共3题,每题10分共30分)
1)用多线程、信号量实现生产者和消费者的模拟,仓库容量为10,仓库中开始有3
件产品,消费者每3秒消费一件产品,生产者每两秒生产一个产品,生产者和消费者不能同时进入仓库(需要互斥)
//信号量---线程间通信
//“生产者消费者”问题
#include
#include
#include
#include
#include
#define msleep(x)usleep(x*1000)
#define PRODUCT_SPEED 3 //生产速度
#define CONSUM_SPEED 1 //消费速度
#define INIT_NUM 3 //仓库原有产品数#define TOTAL_NUM 10 //仓库容量
sem_t p_sem, c_sem, sh_sem;
int num=INIT_NUM;
void product(void) //生产产品
{
sleep(PRODUCT_SPEED);
}
int add_to_lib() //添加产品到仓库{
num++;//仓库中的产品增加一个
msleep(500);
return num;
}
void consum() //消费
{
sleep(CONSUM_SPEED);
}
int sub_from_lib() //从仓库中取出产品
{
num--; //仓库中的产品数量减一
msleep(500);
return num;
}
void *productor(void *arg) //生产者线程
{
while(1)
{
sem_wait(&p_sem);//生产信号量减一
product();// 生产延时
sem_wait(&sh_sem);//这个信号量是用来互斥的
printf("push into! tatol_num=%d\n",add_to_lib());
sem_post(&sh_sem);
sem_post(&c_sem); //消费信号量加一
}
}
void *consumer(void *arg) //消费者线程
{
while(1)
{
sem_wait(&c_sem); //消费者信号量减一
sem_wait(&sh_sem);
printf("pop out! tatol_num=%d\n",sub_from_lib());
sem_post(&sh_sem);
sem_post(&p_sem);//生产者信号量加一
consum();//消费延时
}
}
int main()
{
pthread_t tid1,tid2;
sem_init(&p_sem,0,TOTAL_NUM-INIT_NUM);
sem_init(&c_sem,0,INIT_NUM);
sem_init(&sh_sem,0,1);
pthread_create(&tid1,NULL,productor,NULL);
pthread_create(&tid2,NULL,consumer,NULL);
pthread_join(tid1,NULL);
pthread_join(tid2,NULL);
return 0;
}
2)编写一个多线程的程序:
要求:
1、创建2个子线程,主进程要传递3个参数给线程1,传递另一个参数给线程2;
2、线程1、线程2中对每个传给线程的参数加1,等线程都执行完毕后,进程打印
出四个参数的值。
3、传给线程1参数的值必需要能够改变,传给线程2参数的值必需不能改变#include
#include
#include
typedef struct{
int A;
int B;
int C;
}DATA;
/*传参方法1*/
void *thread_1(void *arg)
{
DATA *rec;
sleep(1);
rec = (DATA *)arg;
rec->A += 1;
rec->B += 1;
rec->C += 1;
return NULL;
}
/*传参方法2*/
void *thread_2(void * arg)
{
int rec = 0;
sleep(1);
rec = (int )(arg);
rec ++;
return NULL;
}
int main(int argc, char *argv[])
{
pthread_t tid1,tid2;
DATA test;
int D = 100;
test.A = 100;
test.B = 100;
test.C = 100;
/*创建两个线程*/
pthread_create(&tid1, NULL, (void *)thread_1, (void *)(&test));
pthread_create(&tid2, NULL, (void *)thread_2, (void *)D);
pthread_join(tid1, NULL);
pthread_join(tid2, NULL);
printf("A=%d\n", test.A);
printf("B=%d\n", test.B);
printf("C=%d\n", test.C);
printf("D=%d\n", D);
return 0;
}
3) 编制一段程序,实现进程的管道通信:
使用系统调用pipe()建立一条管道线,2个子进程分别向管道各写一句话:Child process 1 is sending a message!
Child process 2 is sending a message!
而父进程则从管道中读出来自于2个子进程的信息,显示在屏幕上。
要求:父进程先接收子进程P1发来的消息,然后再接收子进程P2发来的消息。
#include
#include
#include
#include
int main(int argc, char *argv[])
{
int fd[2];
pid_t pid;
pid_t pid_sec;
char buf[7];
pipe(fd);
if((pid = fork())<0)
{
printf("fork error!");
exit(1);
}
else if(pid == 0)
{
close(fd[0]);
write(fd[1],"first1",7);
close(fd[1]);
exit(0);
}
else
{
if((pid_sec = fork())<0)
{
printf("fork second error!");
exit(1);
}
else if(pid_sec==0)
{
close(fd[0]);
write(fd[1],"Child1",7);
close(fd[1]);
exit(0);
}
else
{
wait(0);
close(fd[1]);
read(fd[0],buf,7);
printf("%s\n",buf);
wait(0);
close(fd[1]);
read(fd[0],buf,7);
close(fd[0]);
printf("%s\n",buf);
exit(0);
}
}
return 0;
}