2023年8月2日发(作者:)

选择

Linux操作系统的应用领域极其广泛,在下列选项中,哪些可能用到了Linux操作系统?(以上全部)

为了提高嵌入式软件的可移植性,应该注意提高软件的(D)。

A. 使用的方便性 B. 安全性 C. 可靠性 D. 硬件无关性

在Linux系统中用于在两个文件之间创建链接的命令是(A)

A.ln B. mkdir C. chmod D. adduser

Linux系统中,文件test的访问权限744,若增加同组用户的写权限,取消文件所有者的执行权限,要在终端输入下列哪个命令(B)

A. chmod u+x, g+w test B. chmod a-x, g+w test

C. chmod 646 test D. chmod g+w test

嵌入式的存储空间常常分为四个分区,其中,存放在第三个分区的是(B)

A. U-boot B. Linux内核 C. 启动参数 D. 文件系统

6. U-boot的常用命令中个,setenv命令的作用是(C)

A. 拷贝文件 B. 打印环境变量 C. 设置环境变量 D. 修改内存

7. 进程可以忽略大部分信号,但下列信号中( C )是不能忽略的。

B. SIGINT C. SIGSTOP D. SIGQUIT

8.Linux操作系统中,fork()系统调用用于创建进程。仔细阅读、分析程序,假设程序正确运行并创建子进程成功,那么,输出到屏幕的正确结果是( C )

main()

{

pid_t pid;

pid = fork();

if (pid = = 0)

printf ("Hello Worldn");

else

if (pid > 0)

printf ("Hello Worldn");

else

printf ("Hello Worldn");

}

A. 什么都没有 B. 1行Hello World

C. 2行Hello World D. 3行Hello World

9. 使用文件前要先打开文件。在成功执行打开文件系统调用后,系统会返回给用户一个( C )。

A.文件长度 B.内存地址 C.文件描述符 D. 文件打开方式

10. 关于虚拟文件系统(VFS)的说法,不正确的是( B )

屏蔽了用户与底层文件系统实现

不支持此不同文件系统之间的相互访问

C. 它位于用户程序和具体的文件系统之间,对用户程序提供了标准的文件系统调用接口

D. 对具体的文件系统,它通过一系列对不同文件系统公用的函数指针来调用具体的文件系统函数,完成实际的操作

11. 下列文件操作函数中,具有缓存式特性的是( D )

A. open B. read C. lseek D. fwrite

12. 下列不属于标准I/O库中预定义的3个流是( D )

A. stdin B. stdout C. stderr D. write

13. 下列不是Linux系统进程类型的是( D )

A.交互进程 B. 批处理进程 C. 守护进程 D.就绪进程

14. 可以使用( D )命令得到IPC机制中所有对象的状态。

A。ls B. cd C. kill D. ipcs

15. 进程可以忽略大部分信号,但下列信号中( C )是不能忽略的。

B. SIGINT C. SIGSTOP D. SIGQUIT

16. 结束后台进程的命令是( A )

B. Ctrl+C C. tar D.

17. 在kill(pid, signum)函数中,pid参数表示kill函数发送信号对象的进程号或进程组号,如果pid<-1,表示( C )

A. 向进程号为PID值的进程发送信号

B. 向与发送信号的进程有相同进程组号的进程发送信号

C. 向组号为pid绝对值的进程组发送信号

D. 未定义

18. 在Linux系统中,涉及到权限且能够彻底终止进程的kill命令中,需要输入的参数是( BD )

A. 2 B. 5 C. 8 D. 9

19. 与并行通信相比,串行通信适用于( A )的情况。

A. 传输距离远 B. 传送速度快 C. 传送信号好 D. 传送费用高

20. Linux系统中,内核以( C )区分设备。

A. 设备节点名 B. 设备名称 C. 设备号 D. 设备节点号

21. 可以使用下面的( C )函数将一个线程挂起。

A. pthread_self B. pthread_pause

C. pthread_join D. pthread_exit

22. 与并行通信相比,串行通信适用于( A )的情况。

A. 传输距离远 B. 传送速度快 C. 传送信号好 D. 传送费用高

23. 下列哪个选项不是使用互斥锁的代码段具有的特性。 ( D )

A. 非繁忙等待 B. 原子性

C. 唯一性 D. 条件满足方可生效

24. Linux驱动程序运行在( A )

A. 内核空间 B. 用户空间 C. 用户空间和内核空间

25. 以下有关嵌入式系统中驱动的说法,错误的是( B )

A. 驱动程序崩溃可能会导致内核崩溃

B. 驱动程序的漏洞不会影响内核的安全性

C. 驱动的效率会影响内核的整体效率

D. 内核会以函数调用的方式调用驱动代码

26. 下面临界区概念论述正确的是( A )。

A. 临界区是指进程中用于实现进程互斥的那段程序代码

B. 临界区是指进程中用于实现进程同步的那段程序代码

C. 临界区是指进程中用于实现进程通信的那段程序代码

D. 临界区是指并发进程中与共享变量有关的程序段

二、填空题

1. 对于Linux而言,所有的设备和文件的操作都用文件描述符来进行,几乎所有基本操作函数都需要它作为参数。

2. 在Linux系统中,基本指令tar命令对文件目录进行压缩和解压缩。

3. 编译器gcc编译过程分为4个步骤:预处理、编译、汇编和链接。

4. 文件的访问权限中,r表示可读,w表示可写,x表示可执行。

5. Linux系统中文件类型用“d”表示文件夹,“-”表示普通文件。

6. Makefile文件主要包含_注释_、编辑目标定义和_适配段_。

7. Linux系统中的vi编辑器有三种基本状态,分别是一般模式_、_编辑模式_和_命令行模式。

8. 在32位Ubuntu系统中默认四字节对齐,给定一个结构体,

struct A

{

char a;

char b;

char c;

short d;

int e;

};

求sizeof(struct A) = ___12____。

9. 由于嵌入式设备端内存较小,不具备调试开发能力,因此需要在PC端开发可以在设备端运行的文件后将文件下载到设备端,这种编译方式叫做___交叉编译。

10. 32位Ubuntu系统,char str[] = “hello”;,sizeof(str) = _6_,sizeof(str[0]) = __1__,strlen(str) = __5__。

11. U-boot的启动过程分为两个阶段,第一阶段是芯片级初始化,由_汇编_语言编写,第二阶段是板级初始化,由_C语言_编写。

12. U-boot中支持的文件系统代码存放在_fs_目录。

13. 在Ubuntu 32位系统中,编写以下程序,请写出打印结果为b=_0xffffff8f_。

#include

#define YYY(x, n) (x & ~(7U<<(n-1)))

int main(void)

{

unsigned int a = 0xFFFFFFFF;

unsigned int b = 0;

b = YYY(a, 5);

printf("b = 0x%x.n", b);

return 0;

}

14. 假设在Linux操作系统环境下执行以下程序:

main()

{

printf("Hello Worldn");

fork();

printf("Hello Worldn");

}

若程序正常运行,子进程创建成功,则终端输出__3__个“Hello World”

15.进程的三种状态有____运行_____、____就绪____、____阻塞____。

16. 在系统API中,___read__函数从文件中读取指定长度的数据到内存中,_____函数是将内存中的数据写入文件中。

17. fork函数在父进程中的返回值是__子进程标识号__,在子进程中的返回值是___0___。

18. 一个运行着的进程打开了一个新的文件,则指向该文件数据结构的关键指针存放在_进程控制块(PCB)_

19. 在Ubuntu系统某目录下存在文件,内容为“I love Linux”,利用以下API进行操作:利用open函数以读写方式打开该文件,利用write写入“abc”三个字符,在利用read函数读取内容,则读出字符的个数是_9_。

20. 使用快捷键组合Ctrl+会发送信号SIGQUIT到前台进程。

21. 解决常规信号不可靠的机制称为信号屏蔽。

22. Linux系统中的大多数信号都可以被忽略,但9号信号SIGKILL和19号信号SIGSTOP是超级用户杀死进程的可靠方法,不能被忽略。

23. 信号递达进程后才可能被处理,信号的处理方式有三种,分别为:忽略、捕捉和默认执行。

24. 在Linux系统中,__线程__是系统调度分派的最小单位,__进程__是资源分配的最小单位。

25. 线程和进程的最大区别在于,__线程__PCB中指向内存资源的三级页表相同,而__进程__PCB中指向内存资源的三级页表不同。

26. Linux系统中那些种类的设备有设备节点__字符设备和网络设备__。

27. 若第一次调用alarm()函数时参数为5,3秒后再次调用alarm()函数,并传入参数1,则第二次调用alarm()函数时,函数的返回值为_2_。

28. Linux2.6版本内核中,设备的主设备号用_12_位来表示,次设备号用_20_位来表示。

三、判断题

1. vi编辑器的底行模式下,输入q和q!表达的意思完全相同。(错)

2. Makefile的假目标可以采用.PHONY关键字定义。(对)

3. vi编辑器从编辑模式回到一般模式按ESC键。(对)

4. cp命令可以复制文件和目录,且命令参数相同。(错)

5. 在Linux系统的安装过程中可以进行网络配置。(对)

6. Linux系统的主要的系统的配置文件主要存放在/var/目录中。(错)

7. 在任何环境下编译C语言程序都不需要引导启动代码。(错)

8. U-boot程序不具备检测更新功能。(错)

9. 在C语言程序中,申请内存后不归还,会导致内存泄漏。(对)

10. C语言中的静态变量的生存期和应用程序相同,可以在所有函数中访问。(错)

11. 进程的属性保存在一个被称为进程控制块的结构体中。(对)

12. 在利用Qt平台开发时,Q_OBJECT是一个宏定义,如果类里面用到了signal或者slots,就必须要声明这个宏。( 对 )

13. 并行和串行通信都要求有固定的数据格式。 ( 错 )

14. PC微机的RS-232C的串行通信接口线上是TTL电平。 ( 错 )

15. 线程是最小的资源分配单位。( 错 )

16. 当waitpid()函数的参数options设置为WNOHANG,该函数不会阻塞等待子进程终止。(对)

17. signal()函数的功能为捕获信号,修改信号向量表中该信号的信号处理函数指针,它不能实现信号屏蔽。(错)

18. 信号SIGINT、SIGQUIT、SIGKILL的默认动作都是终止进程。(对)

19. 在程序中调用kill()函数,若参数pid > 0,则发送信号sig给进程号为pid的进程。(对)

20. 只有指向管道读端的文件描述符打开时,向管道中写入数据才有意义,否则写端的进程会收到内核传来的信号SIGPIPE,默认情况下该信号会导致进程终止。(对)

21. signal()函数和sigaction()函数分别用于发送信号和捕获信号。(错)

四、简单题

1. 在U-boot和Linux内核源码的大部分程序是由C语言编写的,请说明在static、volatile、register、auto关键字在C语言中的作用。

2. Linux系统中的文件类型有哪几种(写五种即可)。

3. 简述Linux系统中kill函数的功能,并对其参数pid进行详细说明。

4. 简述利用Qt平台设计一个对话框主要包括哪些步骤?

(1)创建窗体并在窗体中防止各种控件;

(2)对窗体进行布局管理;

(3)设置各个控件的标签顺序;

(4)创建信号与槽

(5)连接信号与槽

5. 写出嵌入式系统驱动部分,打印信息函数的名称,并与C语言中的printf函数比较异同。

(1)printk在内核源码中用来打印信息的函数

(2)printf和printk最大的差别:printf是C库函数,是在应用层编程中使用的,不能在Linux内核源代码中使用;printk是Linux内核源代码中自己封装出来的一个打印函数,是内核源码中的一个普通函数,只能在内核源码范围中使用,不能再应用编程中使用。

(3)printk相比printf来说还多了打印级别的设置

6. 简述在驱动程序开发和驱动框架中,以下命令和宏定义insmod、rmmod、lsmod、MODULE_DESCRIPTION、MODULE_LICENSE的意义或功能。

insmod:加载驱动模块;

rmmod:卸载驱动模块;

lsmod:用于显示已载入系统的驱动模块;

MODULE_DESCRIPTION:模块用途的简单描述;

MODULE_AUTHOR:声明谁编写了模块,声明编写该模块的作者;

MODULE_LICENSE:宏声明此模块的许可证。

7. kill()函数的参数有两个,分别为pid和sig,其中pid表示接收信号的进程的pid。简单说明该参数的取值,与每种取值所代表的含义。

参数PID的取值可分为4种情况,每种取值代表的含义如下

① PID > 0,则发送信号SIG给进程号为PID的进程;

② PID = 0,则发送信号SIG给当前进程所属组中的所有进程;

③ PID = -1,则发送信号SIG给除1号进程与当前进程的所有进程;

④ PID < -1,则发送信号SIG给属于进程组-PID的所有进程。

8. 简单说明Linux系统中信号的处理方式。

信号递达进程后才可能被处理,信号的处理方式有三种:忽略、捕捉和执行默认动作。大多数信号都可以被忽略,但9号信号SIGKILL和19号信号SIGSTOP是超级用户杀死进程的可靠方法,不能被忽略,也不能被捕获。

9. 简单说明fork函数为何会有两个返回值,以及两个返回值所代表的含义。

第一个返回值是父进程中fork()的返回值,返回的是子进程的PID;第二个返回值是子进程中fork()的返回值,返回的是0。

10. 简述在嵌入式软件程序的的编写过程中,’0’、’0’、0和NULL各自的作用和区别。

'0'是一个转义字符,对应的ASCII编码值是0,本质就是0;

'0'是一个字符,他对应的ASCII编码值是48,本质是48;

0是一个数字,没有ASCII编码,他就是0,本质就是0;

NULL是一个表达式,是强制类型转换为void *类型的0,本质是0;

’0’用法是C语言字符串的结尾标志,一般用来比较字符串中的字符以判断字符串有没有到头;’0’是字符0,对应0这个字符的ASCII编码,一般用来获取0一般用来比较的ASCII码值;0是数字,一般用来比较一个int类型的数字是否等于0;NULL是一个表达式,一般用来表指针是否是一个野指针。

11. C语言程序编写时,头文件包含格式通常有两种,#include和#include“filename.h”,请问#include 和#include“filename.h”的区别是什么?

<>是用来包含系统提供的头文件(就是系统自带的,不是程序员自己写的,譬如C库),””用来包含自己写的头文件;更深层次来说:<>的话C语言编译器只会到系统指定目录(编译器中配置的或者操作系统配置的寻找目录,譬如在Ubuntu中的是usr/include目录,编译器还允许用-I来附加指定其他的包含路径)寻找这个头文件(意思就是不会找当前目录下),如果找不到就会提示头文件不存在。””包含的头文件,编译器会默认先在当前目录下寻找相应的头文件,如果没找到然后再到系统指定的目录去寻找,如果还没找到则提示文件不存在。

12. BootLoader的结构分为两部分,简述各部分的功能,各个阶段都做了什么工作。

第一阶段:SOC内部初始化,即芯片级初始化;第二阶段,SOC外部开发板内部,板级初始化。

第一阶段:

(1)构建异常向量表;

(2)设置CPU为SVC模式;

(3)关看门狗;

(4)开发板供电置锁;

(5)时钟初始化;

(6)DDR初始化;

(7)串口初始化并打印“OK”;

(8)重定位; (9)建立映射表并开启MMU;

(10)跳转到第二阶段。

第二阶段:

初始化CPU、板卡、中断、控制台等;初始化icache和dcache;初始化flash;打印logo;使能中断;获取环境变量;网卡初始化;main_loop循环

13. 简述U-boot启动内核的步骤。

第一步:将内核搬移到DDR中(将内核镜像从它部署的地方(SD卡、nand、远端服务器通过tftp)弄到DDR中的链接地址);

第二步;校验内核格式(zImage、uImage还有其他)、CRC等;

第三步:准备传参(以tag方式,在tag_start和tag_end之间)(地址30000100);

第四步:跳转执行内核(远跳转,把内核转为函数指针,传参)。

14. 描述ifconfig命令和ping命令的功能和用法。

ifconfig命令用于查看系统中的网卡,也可与选项-A一起使用,表示查看所有的网卡。ping命令用于测试网络连通状态,其常用格式为“ ping IP地址&rdquo。

15. 简述Linux系统的gcc编译器的工作流程,并说明每步执行的内容。

GCC的编译过程分为四个步骤,分别是预处理、编译、汇编和链接。预处理阶段主要处理源代码中以“#”开头的预编译指令和一些注释信息;编译阶段GCC会对经过预处理的文件进行语法、词法和语义分析,确定代码实际要做的工作,若检查无误,则生成相应的汇编代码文件;汇编阶段将编译后生成的汇编代码转换为机器可以执行的命令;链接的过程是组装各个目标文件的过程,在这个过程中会解决符号依赖和库依赖关系,最终生成可执行文件。

16. 在进程通信机制中,哪种机制效率最高?简述其高效的原因。

共享内存。共享内存之所以是效率最高的一种进程通信方式,主要在于它节省了不同进程间多次读写的时间:若有多个进程将自己的虚拟地址与此块物理内存进行绑定,那么当一个进程对此块内存中的数据进行修改时,其它进程可以直接获得修改后的数据。

17. 对比管道,简单陈述消息队列的特点。

与管道相比,消息队列的通信方式更为灵活:它提供有格式的字节流,无需通信双方额外约定数据传输格式;其中的消息设定为不同类型,又被分配了不同的优先级,新添加的消息总是在队尾,但接收消息的进程可以读取队列中间的数据。此外,消息队列也降低了读写进程间的耦合强度:若接收消息的进程没有接收到消息,发送消息的进程无须等待,可以继续发送消息,消息的读写双方只需关注各自功能的实现情况即可。与FIFO类似,消息队列可以实现无亲缘关系进程间的通信,且独立于通信双方的进程之外,若没有删除内核中的消息队列,即便所有使用消息队列的进程都已终止,消息队列仍存在于内核中,直到内核重新启动、管理命令被执行或调用系统接口删除消息队列时,消息队列才会真正被销毁。

18. 简述使用消息队列实现进程间通信的步骤。

使用消息队列实现进程间通信的步骤如下:

① 创建消息队列;

② 发送消息到消息队列;

③ 从消息队列中读取数据;

④ 删除消息队列。

五、程序设计题(实验指导书)

1. 编写一个应用程序,在Linux系统中,不直接使用sleep函数,利用alarm和pause函数实现系统的睡眠功能。(实验六第五题)

#include

#include // unix standand

#include

void func(int sig)

{

if (sig == SIGALRM)

{

printf("alarm happened.n");

}

}

void mysleep(unsigned int seconds);

int main(void)

{

printf("before mysleep.n");

mysleep(3);

printf("after mysleep.n");

return 0;

}

void mysleep(unsigned int seconds)

{

struct sigaction act = {0};

_handler = func;

sigaction(SIGALRM, &act, NULL);

alarm(seconds);

pause();

}

2. 编写一个应用程序,获取Linux系统的随机数,要求利用time函数的返回值作为srand函数的参数。(实验五第八题)

#include

#include

int main(int argc, char **argv)

{

int i = 0, val = 0;

/* if (argc != 2)

{

printf("usage: %s numn", argv[0]);

return -1;

}

*/

printf("RAND_MAX = %d.n", RAND_MAX); // 2147483647

//srand(atoi(argv[1]));//atoi把字符串转成数字

srand(time(NULL));

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

{

val = rand();

//printf("%d ", val);

printf("%d ", (val % 6));//得到0到6的随机数

}

printf("n");

return 0;

}

3. 编写一个应用程序,在Linux系统某目录下存在文件,利用lseek函数计算任意文件的长度。其中可执行程序的名字是,若计算文件的长度,则输入命令 ,若计算b.c的长度,则输入命令 b.c。(实验五第二题)

#include

#include

#include

#include

#include

#include

int cal_len(const char *pathname)

{

int fd = -1;

int ret = -1;

fd = open(pathname, O_RDONLY);

if (-1 == fd)

{

//printf("n");

perror("打开文件错误!");

// return -1;

return -1;

}

ret = lseek(fd, 0, SEEK_END); return ret;

}

int main(int argc, char *argv[])

{

int fd = -1;

int ret = -1;

if (argc != 2)

{

printf("usage: %s filenamen", argv[0]);

_exit(-1);

}

printf("文件的长度为:%dn", cal_len(argv[1]));

return 0;

}

4. 编写一个应用程序,在Linux系统中,利用lseek函数创建一个空洞文件。(实验五第三题)

#include

#include

#include

#include

#include

#include

int main(int argc, char *argv[])

{

int fd = -1;

char buf[100] = {0};

char writebuf[20] = "abcd";

int ret = -1;

fd = open("", O_RDWR | O_CREAT);

//fd = open("", O_RDONLY);

if (-1 == fd)

{

//printf("n");

perror("打开文件错误!");

// return -1;

_exit(-1);

}

else

{

printf("打开文件的描述符为d = %d.n", fd);

}

ret = lseek(fd, 10, SEEK_SET);

printf("lseek, ret = %d.n", ret);

#if 1

ret = write(fd, writebuf, strlen(writebuf));

if (ret < 0)

{

//printf("write写出错n");

perror("wri te写出错");

_exit(-1);

}

else

{

printf("write向文件中写入的字符数%d n", ret);

}

#endif

#if 1

ret = read(fd, buf, 20);

if (ret < 0)

{

printf("read读出错n");

_exit(-1);

}

else

{

printf("读取了字符数为:%d.n", ret);

printf("缓冲区的字符数为:[%s].n", buf);

}

#endif

close(fd);

_exit(0);

}

5. 编写应用程序,读取文件夹下的文件并判断文件类型、文件属性和文件管理权限。(实验五第六题)

#include

#include

#include

#include

#include

#include

#define NAME ""

int main(void)

{

int ret = -1;

struct stat buf;

memset(&buf, 0, sizeof(buf)); // memset后buf中全是0

ret = stat(NAME, &buf); // stat后buf中有内容了

if (ret < 0)

{

perror("stat");

exit(-1);

}

#if 0

// 判断这个文件属性

//int result = S_ISREG(_mode);

int result = S_ISDIR(_mode);

printf("result = %dn", result);

#endif

#if 1

// 文件权限测试

//unsigned int result = (_mode & S_IRWXU) >> 8;

unsigned int result = ((_mode & S_IRUSR)? 1: 0);

printf("file owner: %u.n", result);

#endif

return 0;

}

6. 在程序中创建子进程,父子进程共同操作一个文件,实现接续写入字符的操作。(实验六第一题)

#include #include

#include

#include

#include

#include

int main(void)

{

// 首先打开一个文件

int fd = -1;

pid_t pid = -1;

fd = open("", O_RDWR | O_TRUNC | O_APPEND | O_CREAT);//如果打开文件有内容,就截断

if (fd < 0)

{

perror("open");

return -1;

}

// fork创建子进程

pid = fork();

if (pid > 0)

{

// 父进程中

printf("parent.n");

write(fd, "hello", 5);

sleep(1);

}

else if (pid == 0)

{

// 子进程

printf("child.n");

write(fd, "world", 5);

sleep(1);

}

else

{

perror("fork");

exit(-1);

}

close(fd);

return 0;

}

7. 编写一个守护进程,使之可以在关闭终端的情况下在后台正常运行。(实验六第二题)

#include

#include

#include

#include

#include

#include

void create_daemon(void);

int main(void)

{

create_daemon();

while (1)

{

printf("I am running.n");

sleep(1);

}

return 0;

}

// 函数作用就是把调用该函数的进程变成一个守护进程

void create_daemon(void)

{

pid_t pid = 0;

pid = fork();

if (pid < 0)

{

perror("fork");

exit(-1);

}

if (pid > 0)

{

exit(0); // 父进程直接退出

}

// 执行到这里就是子进程

// setsid将当前进程设置为一个新的会话期session,目的就是让当前进程

// 脱离控制台。

pid = setsid();

if (pid < 0)

{

perror("setsid");

exit(-1);

}

// 将当前进程工作目录设置为根目录。因为当前进程就要变成脱离控制台的守护进程,放在根目录就不依赖与任何根文件系统的文件上。

chdir("/");

// umask设置为0确保将来进程有最大的文件操作权限

umask(0);

// 关闭所有文件描述符

// 先要获取当前系统中所允许打开的最大文件描述符数目

int cnt = sysconf(_SC_OPEN_MAX);//这个函数是用来获取当前操作系统的配置信息的

int i = 0;

for (i=0; i

{

close(i);

}

//三次,分别对应标准输入、输出、错误

open("/dev/null", O_RDWR);

open("/dev/null", O_RDWR);

open("/dev/null", O_RDWR);

}

8. 利用C语言编写程序,实现功能:用户从终端输入任意字符后统计个数显示,输入end则结束。

#include

#include

int main() {

char buf[255];

while(1)

{

memset(buf, 0, sizeof(buf));

scanf("%s", buf);

if(strcmp(buf, "end") == 0)

break;

printf("字符个数为: = %dn", strlen(buf));

}

return 0;

}

9. 网络编程实验题(详细见实验指导书第七部分网络编程客户端与服务端的程序设计,第二题)。

tcp_client.c

#include

#include

#include

#include

#include

#include

#include

#include

#define BUFFER_SIZE 128

int main(int argc, char *argv[])

{

int sockfd, i;

char buf[BUFFER_SIZE] = "Hello Server";

struct sockaddr_in servaddr;

if(argc < 3)

{

printf("Usage : %s n", argv[0]);

exit(-1);

}

/* 创建Socket */

if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)

{

perror("socket");

exit(-1);

}

/* 设置sockaddr_in结构体中相关参数 */

bzero(&servaddr, sizeof(servaddr));

_family = AF_INET;

_port = htons(atoi(argv[2]));

_addr.s_addr = inet_addr(argv[1]);

/* 调用connect()函数向服务器端建立TCP连接 */

if(connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) == -1)

{

perror("connect");

exit(-1);

}

/* 发送消息给服务器端 */

send(sockfd, buf, sizeof(buf), 0);

i = recv(sockfd, buf, sizeof(buf), 0);

if(i < 0)

//if(recv(sockfd, buf, sizeof(buf), 0) < 0);

{

perror("recv");

exit(-1);

}

printf("recv from server : %sn", buf);

close(sockfd);

return 0;

}

tcp_server.c

#include

#include

#include

#include

#include

#include

#include

#include

#define BUFFER_SIZE 128

int main(int argc, char *argv[])

{

int listenfd, connfd;

struct sockaddr_in servaddr, cliaddr;

socklen_t peerlen;

char buf[BUFFER_SIZE];

if(argc < 3)

{

printf("Usage : %s n", argv[0]);

exit(-1);

}

/* 建立Socket连接 */

if((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)

{

perror("socket");

exit(-1);

}

printf("listenfd = %dn", listenfd);

/* 设置sockaddr_in结构体相关参数 */

bzero(&servaddr, sizeof(servaddr));

_family = AF_INET;

_port = htons(atoi(argv[2]));

_addr.s_addr = inet_addr(argv[1]);

/* 绑定函数bind() */

if(bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0)

{

perror("bind");

exit(-1);

}

}

printf("Bind success!n");

/* 调用listen()函数,设置监听模式 */

if(listen(listenfd, 10) == -1)

{

perror("listen");

exit(-1);

}

printf("");

/* 调用accept()函数,等待客户端的连接 */

peerlen = sizeof(cliaddr);

while(1)

{

if((connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &peerlen)) < 0)

{

perror("accept");

exit(-1);

}

printf("connection from [%s:%d]n", inet_ntoa(_addr), ntohs(_port));

/* 调用recv()函数接收客户端发送的数据 */

memset(buf, 0, sizeof(buf));

if(recv(connfd, buf, BUFFER_SIZE, 0) < 0)

{

perror("recv");

exit(-1);

}

printf("Received a message: %sn", buf);

strcpy(buf, "Welcome to server");

send(connfd, buf, sizeof(buf), 0);

close(connfd);

}

close(listenfd);

return 0;

2023年8月2日发(作者:)

选择

Linux操作系统的应用领域极其广泛,在下列选项中,哪些可能用到了Linux操作系统?(以上全部)

为了提高嵌入式软件的可移植性,应该注意提高软件的(D)。

A. 使用的方便性 B. 安全性 C. 可靠性 D. 硬件无关性

在Linux系统中用于在两个文件之间创建链接的命令是(A)

A.ln B. mkdir C. chmod D. adduser

Linux系统中,文件test的访问权限744,若增加同组用户的写权限,取消文件所有者的执行权限,要在终端输入下列哪个命令(B)

A. chmod u+x, g+w test B. chmod a-x, g+w test

C. chmod 646 test D. chmod g+w test

嵌入式的存储空间常常分为四个分区,其中,存放在第三个分区的是(B)

A. U-boot B. Linux内核 C. 启动参数 D. 文件系统

6. U-boot的常用命令中个,setenv命令的作用是(C)

A. 拷贝文件 B. 打印环境变量 C. 设置环境变量 D. 修改内存

7. 进程可以忽略大部分信号,但下列信号中( C )是不能忽略的。

B. SIGINT C. SIGSTOP D. SIGQUIT

8.Linux操作系统中,fork()系统调用用于创建进程。仔细阅读、分析程序,假设程序正确运行并创建子进程成功,那么,输出到屏幕的正确结果是( C )

main()

{

pid_t pid;

pid = fork();

if (pid = = 0)

printf ("Hello Worldn");

else

if (pid > 0)

printf ("Hello Worldn");

else

printf ("Hello Worldn");

}

A. 什么都没有 B. 1行Hello World

C. 2行Hello World D. 3行Hello World

9. 使用文件前要先打开文件。在成功执行打开文件系统调用后,系统会返回给用户一个( C )。

A.文件长度 B.内存地址 C.文件描述符 D. 文件打开方式

10. 关于虚拟文件系统(VFS)的说法,不正确的是( B )

屏蔽了用户与底层文件系统实现

不支持此不同文件系统之间的相互访问

C. 它位于用户程序和具体的文件系统之间,对用户程序提供了标准的文件系统调用接口

D. 对具体的文件系统,它通过一系列对不同文件系统公用的函数指针来调用具体的文件系统函数,完成实际的操作

11. 下列文件操作函数中,具有缓存式特性的是( D )

A. open B. read C. lseek D. fwrite

12. 下列不属于标准I/O库中预定义的3个流是( D )

A. stdin B. stdout C. stderr D. write

13. 下列不是Linux系统进程类型的是( D )

A.交互进程 B. 批处理进程 C. 守护进程 D.就绪进程

14. 可以使用( D )命令得到IPC机制中所有对象的状态。

A。ls B. cd C. kill D. ipcs

15. 进程可以忽略大部分信号,但下列信号中( C )是不能忽略的。

B. SIGINT C. SIGSTOP D. SIGQUIT

16. 结束后台进程的命令是( A )

B. Ctrl+C C. tar D.

17. 在kill(pid, signum)函数中,pid参数表示kill函数发送信号对象的进程号或进程组号,如果pid<-1,表示( C )

A. 向进程号为PID值的进程发送信号

B. 向与发送信号的进程有相同进程组号的进程发送信号

C. 向组号为pid绝对值的进程组发送信号

D. 未定义

18. 在Linux系统中,涉及到权限且能够彻底终止进程的kill命令中,需要输入的参数是( BD )

A. 2 B. 5 C. 8 D. 9

19. 与并行通信相比,串行通信适用于( A )的情况。

A. 传输距离远 B. 传送速度快 C. 传送信号好 D. 传送费用高

20. Linux系统中,内核以( C )区分设备。

A. 设备节点名 B. 设备名称 C. 设备号 D. 设备节点号

21. 可以使用下面的( C )函数将一个线程挂起。

A. pthread_self B. pthread_pause

C. pthread_join D. pthread_exit

22. 与并行通信相比,串行通信适用于( A )的情况。

A. 传输距离远 B. 传送速度快 C. 传送信号好 D. 传送费用高

23. 下列哪个选项不是使用互斥锁的代码段具有的特性。 ( D )

A. 非繁忙等待 B. 原子性

C. 唯一性 D. 条件满足方可生效

24. Linux驱动程序运行在( A )

A. 内核空间 B. 用户空间 C. 用户空间和内核空间

25. 以下有关嵌入式系统中驱动的说法,错误的是( B )

A. 驱动程序崩溃可能会导致内核崩溃

B. 驱动程序的漏洞不会影响内核的安全性

C. 驱动的效率会影响内核的整体效率

D. 内核会以函数调用的方式调用驱动代码

26. 下面临界区概念论述正确的是( A )。

A. 临界区是指进程中用于实现进程互斥的那段程序代码

B. 临界区是指进程中用于实现进程同步的那段程序代码

C. 临界区是指进程中用于实现进程通信的那段程序代码

D. 临界区是指并发进程中与共享变量有关的程序段

二、填空题

1. 对于Linux而言,所有的设备和文件的操作都用文件描述符来进行,几乎所有基本操作函数都需要它作为参数。

2. 在Linux系统中,基本指令tar命令对文件目录进行压缩和解压缩。

3. 编译器gcc编译过程分为4个步骤:预处理、编译、汇编和链接。

4. 文件的访问权限中,r表示可读,w表示可写,x表示可执行。

5. Linux系统中文件类型用“d”表示文件夹,“-”表示普通文件。

6. Makefile文件主要包含_注释_、编辑目标定义和_适配段_。

7. Linux系统中的vi编辑器有三种基本状态,分别是一般模式_、_编辑模式_和_命令行模式。

8. 在32位Ubuntu系统中默认四字节对齐,给定一个结构体,

struct A

{

char a;

char b;

char c;

short d;

int e;

};

求sizeof(struct A) = ___12____。

9. 由于嵌入式设备端内存较小,不具备调试开发能力,因此需要在PC端开发可以在设备端运行的文件后将文件下载到设备端,这种编译方式叫做___交叉编译。

10. 32位Ubuntu系统,char str[] = “hello”;,sizeof(str) = _6_,sizeof(str[0]) = __1__,strlen(str) = __5__。

11. U-boot的启动过程分为两个阶段,第一阶段是芯片级初始化,由_汇编_语言编写,第二阶段是板级初始化,由_C语言_编写。

12. U-boot中支持的文件系统代码存放在_fs_目录。

13. 在Ubuntu 32位系统中,编写以下程序,请写出打印结果为b=_0xffffff8f_。

#include

#define YYY(x, n) (x & ~(7U<<(n-1)))

int main(void)

{

unsigned int a = 0xFFFFFFFF;

unsigned int b = 0;

b = YYY(a, 5);

printf("b = 0x%x.n", b);

return 0;

}

14. 假设在Linux操作系统环境下执行以下程序:

main()

{

printf("Hello Worldn");

fork();

printf("Hello Worldn");

}

若程序正常运行,子进程创建成功,则终端输出__3__个“Hello World”

15.进程的三种状态有____运行_____、____就绪____、____阻塞____。

16. 在系统API中,___read__函数从文件中读取指定长度的数据到内存中,_____函数是将内存中的数据写入文件中。

17. fork函数在父进程中的返回值是__子进程标识号__,在子进程中的返回值是___0___。

18. 一个运行着的进程打开了一个新的文件,则指向该文件数据结构的关键指针存放在_进程控制块(PCB)_

19. 在Ubuntu系统某目录下存在文件,内容为“I love Linux”,利用以下API进行操作:利用open函数以读写方式打开该文件,利用write写入“abc”三个字符,在利用read函数读取内容,则读出字符的个数是_9_。

20. 使用快捷键组合Ctrl+会发送信号SIGQUIT到前台进程。

21. 解决常规信号不可靠的机制称为信号屏蔽。

22. Linux系统中的大多数信号都可以被忽略,但9号信号SIGKILL和19号信号SIGSTOP是超级用户杀死进程的可靠方法,不能被忽略。

23. 信号递达进程后才可能被处理,信号的处理方式有三种,分别为:忽略、捕捉和默认执行。

24. 在Linux系统中,__线程__是系统调度分派的最小单位,__进程__是资源分配的最小单位。

25. 线程和进程的最大区别在于,__线程__PCB中指向内存资源的三级页表相同,而__进程__PCB中指向内存资源的三级页表不同。

26. Linux系统中那些种类的设备有设备节点__字符设备和网络设备__。

27. 若第一次调用alarm()函数时参数为5,3秒后再次调用alarm()函数,并传入参数1,则第二次调用alarm()函数时,函数的返回值为_2_。

28. Linux2.6版本内核中,设备的主设备号用_12_位来表示,次设备号用_20_位来表示。

三、判断题

1. vi编辑器的底行模式下,输入q和q!表达的意思完全相同。(错)

2. Makefile的假目标可以采用.PHONY关键字定义。(对)

3. vi编辑器从编辑模式回到一般模式按ESC键。(对)

4. cp命令可以复制文件和目录,且命令参数相同。(错)

5. 在Linux系统的安装过程中可以进行网络配置。(对)

6. Linux系统的主要的系统的配置文件主要存放在/var/目录中。(错)

7. 在任何环境下编译C语言程序都不需要引导启动代码。(错)

8. U-boot程序不具备检测更新功能。(错)

9. 在C语言程序中,申请内存后不归还,会导致内存泄漏。(对)

10. C语言中的静态变量的生存期和应用程序相同,可以在所有函数中访问。(错)

11. 进程的属性保存在一个被称为进程控制块的结构体中。(对)

12. 在利用Qt平台开发时,Q_OBJECT是一个宏定义,如果类里面用到了signal或者slots,就必须要声明这个宏。( 对 )

13. 并行和串行通信都要求有固定的数据格式。 ( 错 )

14. PC微机的RS-232C的串行通信接口线上是TTL电平。 ( 错 )

15. 线程是最小的资源分配单位。( 错 )

16. 当waitpid()函数的参数options设置为WNOHANG,该函数不会阻塞等待子进程终止。(对)

17. signal()函数的功能为捕获信号,修改信号向量表中该信号的信号处理函数指针,它不能实现信号屏蔽。(错)

18. 信号SIGINT、SIGQUIT、SIGKILL的默认动作都是终止进程。(对)

19. 在程序中调用kill()函数,若参数pid > 0,则发送信号sig给进程号为pid的进程。(对)

20. 只有指向管道读端的文件描述符打开时,向管道中写入数据才有意义,否则写端的进程会收到内核传来的信号SIGPIPE,默认情况下该信号会导致进程终止。(对)

21. signal()函数和sigaction()函数分别用于发送信号和捕获信号。(错)

四、简单题

1. 在U-boot和Linux内核源码的大部分程序是由C语言编写的,请说明在static、volatile、register、auto关键字在C语言中的作用。

2. Linux系统中的文件类型有哪几种(写五种即可)。

3. 简述Linux系统中kill函数的功能,并对其参数pid进行详细说明。

4. 简述利用Qt平台设计一个对话框主要包括哪些步骤?

(1)创建窗体并在窗体中防止各种控件;

(2)对窗体进行布局管理;

(3)设置各个控件的标签顺序;

(4)创建信号与槽

(5)连接信号与槽

5. 写出嵌入式系统驱动部分,打印信息函数的名称,并与C语言中的printf函数比较异同。

(1)printk在内核源码中用来打印信息的函数

(2)printf和printk最大的差别:printf是C库函数,是在应用层编程中使用的,不能在Linux内核源代码中使用;printk是Linux内核源代码中自己封装出来的一个打印函数,是内核源码中的一个普通函数,只能在内核源码范围中使用,不能再应用编程中使用。

(3)printk相比printf来说还多了打印级别的设置

6. 简述在驱动程序开发和驱动框架中,以下命令和宏定义insmod、rmmod、lsmod、MODULE_DESCRIPTION、MODULE_LICENSE的意义或功能。

insmod:加载驱动模块;

rmmod:卸载驱动模块;

lsmod:用于显示已载入系统的驱动模块;

MODULE_DESCRIPTION:模块用途的简单描述;

MODULE_AUTHOR:声明谁编写了模块,声明编写该模块的作者;

MODULE_LICENSE:宏声明此模块的许可证。

7. kill()函数的参数有两个,分别为pid和sig,其中pid表示接收信号的进程的pid。简单说明该参数的取值,与每种取值所代表的含义。

参数PID的取值可分为4种情况,每种取值代表的含义如下

① PID > 0,则发送信号SIG给进程号为PID的进程;

② PID = 0,则发送信号SIG给当前进程所属组中的所有进程;

③ PID = -1,则发送信号SIG给除1号进程与当前进程的所有进程;

④ PID < -1,则发送信号SIG给属于进程组-PID的所有进程。

8. 简单说明Linux系统中信号的处理方式。

信号递达进程后才可能被处理,信号的处理方式有三种:忽略、捕捉和执行默认动作。大多数信号都可以被忽略,但9号信号SIGKILL和19号信号SIGSTOP是超级用户杀死进程的可靠方法,不能被忽略,也不能被捕获。

9. 简单说明fork函数为何会有两个返回值,以及两个返回值所代表的含义。

第一个返回值是父进程中fork()的返回值,返回的是子进程的PID;第二个返回值是子进程中fork()的返回值,返回的是0。

10. 简述在嵌入式软件程序的的编写过程中,’0’、’0’、0和NULL各自的作用和区别。

'0'是一个转义字符,对应的ASCII编码值是0,本质就是0;

'0'是一个字符,他对应的ASCII编码值是48,本质是48;

0是一个数字,没有ASCII编码,他就是0,本质就是0;

NULL是一个表达式,是强制类型转换为void *类型的0,本质是0;

’0’用法是C语言字符串的结尾标志,一般用来比较字符串中的字符以判断字符串有没有到头;’0’是字符0,对应0这个字符的ASCII编码,一般用来获取0一般用来比较的ASCII码值;0是数字,一般用来比较一个int类型的数字是否等于0;NULL是一个表达式,一般用来表指针是否是一个野指针。

11. C语言程序编写时,头文件包含格式通常有两种,#include和#include“filename.h”,请问#include 和#include“filename.h”的区别是什么?

<>是用来包含系统提供的头文件(就是系统自带的,不是程序员自己写的,譬如C库),””用来包含自己写的头文件;更深层次来说:<>的话C语言编译器只会到系统指定目录(编译器中配置的或者操作系统配置的寻找目录,譬如在Ubuntu中的是usr/include目录,编译器还允许用-I来附加指定其他的包含路径)寻找这个头文件(意思就是不会找当前目录下),如果找不到就会提示头文件不存在。””包含的头文件,编译器会默认先在当前目录下寻找相应的头文件,如果没找到然后再到系统指定的目录去寻找,如果还没找到则提示文件不存在。

12. BootLoader的结构分为两部分,简述各部分的功能,各个阶段都做了什么工作。

第一阶段:SOC内部初始化,即芯片级初始化;第二阶段,SOC外部开发板内部,板级初始化。

第一阶段:

(1)构建异常向量表;

(2)设置CPU为SVC模式;

(3)关看门狗;

(4)开发板供电置锁;

(5)时钟初始化;

(6)DDR初始化;

(7)串口初始化并打印“OK”;

(8)重定位; (9)建立映射表并开启MMU;

(10)跳转到第二阶段。

第二阶段:

初始化CPU、板卡、中断、控制台等;初始化icache和dcache;初始化flash;打印logo;使能中断;获取环境变量;网卡初始化;main_loop循环

13. 简述U-boot启动内核的步骤。

第一步:将内核搬移到DDR中(将内核镜像从它部署的地方(SD卡、nand、远端服务器通过tftp)弄到DDR中的链接地址);

第二步;校验内核格式(zImage、uImage还有其他)、CRC等;

第三步:准备传参(以tag方式,在tag_start和tag_end之间)(地址30000100);

第四步:跳转执行内核(远跳转,把内核转为函数指针,传参)。

14. 描述ifconfig命令和ping命令的功能和用法。

ifconfig命令用于查看系统中的网卡,也可与选项-A一起使用,表示查看所有的网卡。ping命令用于测试网络连通状态,其常用格式为“ ping IP地址&rdquo。

15. 简述Linux系统的gcc编译器的工作流程,并说明每步执行的内容。

GCC的编译过程分为四个步骤,分别是预处理、编译、汇编和链接。预处理阶段主要处理源代码中以“#”开头的预编译指令和一些注释信息;编译阶段GCC会对经过预处理的文件进行语法、词法和语义分析,确定代码实际要做的工作,若检查无误,则生成相应的汇编代码文件;汇编阶段将编译后生成的汇编代码转换为机器可以执行的命令;链接的过程是组装各个目标文件的过程,在这个过程中会解决符号依赖和库依赖关系,最终生成可执行文件。

16. 在进程通信机制中,哪种机制效率最高?简述其高效的原因。

共享内存。共享内存之所以是效率最高的一种进程通信方式,主要在于它节省了不同进程间多次读写的时间:若有多个进程将自己的虚拟地址与此块物理内存进行绑定,那么当一个进程对此块内存中的数据进行修改时,其它进程可以直接获得修改后的数据。

17. 对比管道,简单陈述消息队列的特点。

与管道相比,消息队列的通信方式更为灵活:它提供有格式的字节流,无需通信双方额外约定数据传输格式;其中的消息设定为不同类型,又被分配了不同的优先级,新添加的消息总是在队尾,但接收消息的进程可以读取队列中间的数据。此外,消息队列也降低了读写进程间的耦合强度:若接收消息的进程没有接收到消息,发送消息的进程无须等待,可以继续发送消息,消息的读写双方只需关注各自功能的实现情况即可。与FIFO类似,消息队列可以实现无亲缘关系进程间的通信,且独立于通信双方的进程之外,若没有删除内核中的消息队列,即便所有使用消息队列的进程都已终止,消息队列仍存在于内核中,直到内核重新启动、管理命令被执行或调用系统接口删除消息队列时,消息队列才会真正被销毁。

18. 简述使用消息队列实现进程间通信的步骤。

使用消息队列实现进程间通信的步骤如下:

① 创建消息队列;

② 发送消息到消息队列;

③ 从消息队列中读取数据;

④ 删除消息队列。

五、程序设计题(实验指导书)

1. 编写一个应用程序,在Linux系统中,不直接使用sleep函数,利用alarm和pause函数实现系统的睡眠功能。(实验六第五题)

#include

#include // unix standand

#include

void func(int sig)

{

if (sig == SIGALRM)

{

printf("alarm happened.n");

}

}

void mysleep(unsigned int seconds);

int main(void)

{

printf("before mysleep.n");

mysleep(3);

printf("after mysleep.n");

return 0;

}

void mysleep(unsigned int seconds)

{

struct sigaction act = {0};

_handler = func;

sigaction(SIGALRM, &act, NULL);

alarm(seconds);

pause();

}

2. 编写一个应用程序,获取Linux系统的随机数,要求利用time函数的返回值作为srand函数的参数。(实验五第八题)

#include

#include

int main(int argc, char **argv)

{

int i = 0, val = 0;

/* if (argc != 2)

{

printf("usage: %s numn", argv[0]);

return -1;

}

*/

printf("RAND_MAX = %d.n", RAND_MAX); // 2147483647

//srand(atoi(argv[1]));//atoi把字符串转成数字

srand(time(NULL));

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

{

val = rand();

//printf("%d ", val);

printf("%d ", (val % 6));//得到0到6的随机数

}

printf("n");

return 0;

}

3. 编写一个应用程序,在Linux系统某目录下存在文件,利用lseek函数计算任意文件的长度。其中可执行程序的名字是,若计算文件的长度,则输入命令 ,若计算b.c的长度,则输入命令 b.c。(实验五第二题)

#include

#include

#include

#include

#include

#include

int cal_len(const char *pathname)

{

int fd = -1;

int ret = -1;

fd = open(pathname, O_RDONLY);

if (-1 == fd)

{

//printf("n");

perror("打开文件错误!");

// return -1;

return -1;

}

ret = lseek(fd, 0, SEEK_END); return ret;

}

int main(int argc, char *argv[])

{

int fd = -1;

int ret = -1;

if (argc != 2)

{

printf("usage: %s filenamen", argv[0]);

_exit(-1);

}

printf("文件的长度为:%dn", cal_len(argv[1]));

return 0;

}

4. 编写一个应用程序,在Linux系统中,利用lseek函数创建一个空洞文件。(实验五第三题)

#include

#include

#include

#include

#include

#include

int main(int argc, char *argv[])

{

int fd = -1;

char buf[100] = {0};

char writebuf[20] = "abcd";

int ret = -1;

fd = open("", O_RDWR | O_CREAT);

//fd = open("", O_RDONLY);

if (-1 == fd)

{

//printf("n");

perror("打开文件错误!");

// return -1;

_exit(-1);

}

else

{

printf("打开文件的描述符为d = %d.n", fd);

}

ret = lseek(fd, 10, SEEK_SET);

printf("lseek, ret = %d.n", ret);

#if 1

ret = write(fd, writebuf, strlen(writebuf));

if (ret < 0)

{

//printf("write写出错n");

perror("wri te写出错");

_exit(-1);

}

else

{

printf("write向文件中写入的字符数%d n", ret);

}

#endif

#if 1

ret = read(fd, buf, 20);

if (ret < 0)

{

printf("read读出错n");

_exit(-1);

}

else

{

printf("读取了字符数为:%d.n", ret);

printf("缓冲区的字符数为:[%s].n", buf);

}

#endif

close(fd);

_exit(0);

}

5. 编写应用程序,读取文件夹下的文件并判断文件类型、文件属性和文件管理权限。(实验五第六题)

#include

#include

#include

#include

#include

#include

#define NAME ""

int main(void)

{

int ret = -1;

struct stat buf;

memset(&buf, 0, sizeof(buf)); // memset后buf中全是0

ret = stat(NAME, &buf); // stat后buf中有内容了

if (ret < 0)

{

perror("stat");

exit(-1);

}

#if 0

// 判断这个文件属性

//int result = S_ISREG(_mode);

int result = S_ISDIR(_mode);

printf("result = %dn", result);

#endif

#if 1

// 文件权限测试

//unsigned int result = (_mode & S_IRWXU) >> 8;

unsigned int result = ((_mode & S_IRUSR)? 1: 0);

printf("file owner: %u.n", result);

#endif

return 0;

}

6. 在程序中创建子进程,父子进程共同操作一个文件,实现接续写入字符的操作。(实验六第一题)

#include #include

#include

#include

#include

#include

int main(void)

{

// 首先打开一个文件

int fd = -1;

pid_t pid = -1;

fd = open("", O_RDWR | O_TRUNC | O_APPEND | O_CREAT);//如果打开文件有内容,就截断

if (fd < 0)

{

perror("open");

return -1;

}

// fork创建子进程

pid = fork();

if (pid > 0)

{

// 父进程中

printf("parent.n");

write(fd, "hello", 5);

sleep(1);

}

else if (pid == 0)

{

// 子进程

printf("child.n");

write(fd, "world", 5);

sleep(1);

}

else

{

perror("fork");

exit(-1);

}

close(fd);

return 0;

}

7. 编写一个守护进程,使之可以在关闭终端的情况下在后台正常运行。(实验六第二题)

#include

#include

#include

#include

#include

#include

void create_daemon(void);

int main(void)

{

create_daemon();

while (1)

{

printf("I am running.n");

sleep(1);

}

return 0;

}

// 函数作用就是把调用该函数的进程变成一个守护进程

void create_daemon(void)

{

pid_t pid = 0;

pid = fork();

if (pid < 0)

{

perror("fork");

exit(-1);

}

if (pid > 0)

{

exit(0); // 父进程直接退出

}

// 执行到这里就是子进程

// setsid将当前进程设置为一个新的会话期session,目的就是让当前进程

// 脱离控制台。

pid = setsid();

if (pid < 0)

{

perror("setsid");

exit(-1);

}

// 将当前进程工作目录设置为根目录。因为当前进程就要变成脱离控制台的守护进程,放在根目录就不依赖与任何根文件系统的文件上。

chdir("/");

// umask设置为0确保将来进程有最大的文件操作权限

umask(0);

// 关闭所有文件描述符

// 先要获取当前系统中所允许打开的最大文件描述符数目

int cnt = sysconf(_SC_OPEN_MAX);//这个函数是用来获取当前操作系统的配置信息的

int i = 0;

for (i=0; i

{

close(i);

}

//三次,分别对应标准输入、输出、错误

open("/dev/null", O_RDWR);

open("/dev/null", O_RDWR);

open("/dev/null", O_RDWR);

}

8. 利用C语言编写程序,实现功能:用户从终端输入任意字符后统计个数显示,输入end则结束。

#include

#include

int main() {

char buf[255];

while(1)

{

memset(buf, 0, sizeof(buf));

scanf("%s", buf);

if(strcmp(buf, "end") == 0)

break;

printf("字符个数为: = %dn", strlen(buf));

}

return 0;

}

9. 网络编程实验题(详细见实验指导书第七部分网络编程客户端与服务端的程序设计,第二题)。

tcp_client.c

#include

#include

#include

#include

#include

#include

#include

#include

#define BUFFER_SIZE 128

int main(int argc, char *argv[])

{

int sockfd, i;

char buf[BUFFER_SIZE] = "Hello Server";

struct sockaddr_in servaddr;

if(argc < 3)

{

printf("Usage : %s n", argv[0]);

exit(-1);

}

/* 创建Socket */

if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)

{

perror("socket");

exit(-1);

}

/* 设置sockaddr_in结构体中相关参数 */

bzero(&servaddr, sizeof(servaddr));

_family = AF_INET;

_port = htons(atoi(argv[2]));

_addr.s_addr = inet_addr(argv[1]);

/* 调用connect()函数向服务器端建立TCP连接 */

if(connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) == -1)

{

perror("connect");

exit(-1);

}

/* 发送消息给服务器端 */

send(sockfd, buf, sizeof(buf), 0);

i = recv(sockfd, buf, sizeof(buf), 0);

if(i < 0)

//if(recv(sockfd, buf, sizeof(buf), 0) < 0);

{

perror("recv");

exit(-1);

}

printf("recv from server : %sn", buf);

close(sockfd);

return 0;

}

tcp_server.c

#include

#include

#include

#include

#include

#include

#include

#include

#define BUFFER_SIZE 128

int main(int argc, char *argv[])

{

int listenfd, connfd;

struct sockaddr_in servaddr, cliaddr;

socklen_t peerlen;

char buf[BUFFER_SIZE];

if(argc < 3)

{

printf("Usage : %s n", argv[0]);

exit(-1);

}

/* 建立Socket连接 */

if((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)

{

perror("socket");

exit(-1);

}

printf("listenfd = %dn", listenfd);

/* 设置sockaddr_in结构体相关参数 */

bzero(&servaddr, sizeof(servaddr));

_family = AF_INET;

_port = htons(atoi(argv[2]));

_addr.s_addr = inet_addr(argv[1]);

/* 绑定函数bind() */

if(bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0)

{

perror("bind");

exit(-1);

}

}

printf("Bind success!n");

/* 调用listen()函数,设置监听模式 */

if(listen(listenfd, 10) == -1)

{

perror("listen");

exit(-1);

}

printf("");

/* 调用accept()函数,等待客户端的连接 */

peerlen = sizeof(cliaddr);

while(1)

{

if((connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &peerlen)) < 0)

{

perror("accept");

exit(-1);

}

printf("connection from [%s:%d]n", inet_ntoa(_addr), ntohs(_port));

/* 调用recv()函数接收客户端发送的数据 */

memset(buf, 0, sizeof(buf));

if(recv(connfd, buf, BUFFER_SIZE, 0) < 0)

{

perror("recv");

exit(-1);

}

printf("Received a message: %sn", buf);

strcpy(buf, "Welcome to server");

send(connfd, buf, sizeof(buf), 0);

close(connfd);

}

close(listenfd);

return 0;