考前复习
1. 分区与目录对应,叫做:挂载
2. 信号在软件层上叫:中断机制,是一种异步中断
3. gcc编译过程:预处理、编译、汇编、连接
4. 编译有线程的文件加:-lpthread
5. 父进程等待子进程用:wait()、waitpid()
6. Linux主要的信号安装函数有:signal()、sigaction()
7. Linux由C和汇编编写完成
8. Linux启动用到的程序在:/boot目录下
9. 一对一线程机制,就是一个用户线程对应一个:内核线程
10. vim中,在命令模式下按:“ : ” 进入底行模式
11. Linux文件系统由:引导块、超级块、数据块、索引节点,这四部分组成,超级块存放文件的控制信息
12. 线程本身调用pthread_exit()函数可以退出线程
13. 向消息队列发送信息的函数是:msgrcv()
14. stat()系统调用可以根据文件描述符来操作文件的特性
15. Memcheck是一个内存检查器,Cachegrind是检查程序中缓存使用出现的问题
16. setitimer()用于设置定时器,当计时时间到时,向进程发送SIGALRM信号
17. 当进程的属性设置为:分离状态,该线程结束时立刻释放它所占有的系统资源
18. 接到一个信号并终止,属于异常终止一个进程
19. 创建线程私有数据命令:pthread_create()
20. 适用于进程之间的通信的通信方式:套接字
21. 创建和打开消息队列的函数为:msgget()
22. 通过调用pid_t waitpid(pit_t pid, int *statloc, int options)得到进程的退出信息,当第一个参数pid取为-1时,表示等待任意子进程退出,相当于wait()
23. int kill(pid_t pid , int signo);当第一个参数pid>0时,表示发送信号给进程id为pid的进程
24. shmat()将共享主存区映射到进程虚拟地址空间
25. 修改消息队列状态信息的命令是msgctl()
26. gdb调试程序时,next: 单步运行,不进入函数内部;step:单步运行,进入函数内部
27. Linux系统的设备文件分为:字符设备文件、块设备文件、网络设备文件
28. I/O提供了三种类型的缓存:全缓冲、行缓冲、不带缓冲
29. 信号的生命周期:信号诞生、信号在进程中注册、信号处理函数执行完毕、信号在进程中注销
30. 互斥锁的两中状态:开锁、上锁
31. 在I/O库中,rewind()将文件流指针指向文件的起始位置
32. 没有明确给定的全局变量和静态变量存放在:未初始化数据区
33. geteuid()用于得到进程的:用户有效UID
34. 一个进程是PCB结构与数据的组合
35. 父进程调用wait() 可能出现的三种情况?
1. 当有子进程退出时wait返回子进程的pid并且获取到子进程的退出状态
2. 当没有子进程退出时 父进程将阻塞等待子进程退出
3. 当没有子进程时,wait返回-1
36. 返回调用进程的进程标识号的系统函数是:getpid()
37. 什么是线程?进程和线程的区别?
1. 线程是操作系统进程中能够并发执行的实体,是处理器调度和分派的基本单位。
2. 区别 :
1. 调度
在传统操作系统中进程是拥有资源和调度的基本单位
在引入了线程的操作系统中 线程是操作系统分派和调度的基本单位
进程是拥有资源的基本单位
2. 并发性
进程之间可以并发执行,在同一进程中的线程也可以并发执行
3. 拥有资源
进程是拥有系统资源的基本单位
线程基本上不拥有系统资源,但也拥有其运行所必要的资源,在同一进程中的线程共享进程的地址空间
4. 系统开销
创建或撤销进程时,系统都要为其创建和回收进程控制块(PCB)
进程的创建和撤销的开销远大于线程的开销
在进程切换时,涉及到当前进程CPU环境的保存以及新被调度运行进程的CPU环境的设置,而线程的切换则仅需保存和设置少量寄存器内容
进程切换的开销也是远大于线程的
38. pid_t fork() 返回值的意义?
若执行成功,父进程中将返回子进程(新创建的进程)的PID,类型为pid_t,子进程将返回0,以区别父子进程;
若执行失败,则在父进程中返回-1,错误原因存储在errno中
39. 在进程中,return和exit() 的区别?
如果是在main函数中exit和return都是终止进程 但是return会弹栈 exit不会;
如果是在函数中return 返回到上级函数调用的位置,exit会终止进程
40. 什么是孤儿进程?谁负责回收孤儿进程的内核空间资源?
父进程先于子进程结束 则子进程称为孤儿进程 由init 0接管并回收其内核资源
41. 什么是僵尸进程?如何消灭僵尸进程?
僵尸进程 :子进程结束但是父进程未回收其内核资源,可以通过调用wait()或waitpid() 或者使其成为孤儿进程 由操作系统回收其内核资源
42. 进程对可靠信号和不可靠信号的处理过程。
不可靠信号:不支持排队 如果在进程屏蔽信号时 给其发送多个不可靠信号,在取消屏蔽后只会接收到一个
可靠信号:支持排队 如果在进程屏蔽信号时 发送多个信号,在取消屏蔽后会按照先后顺序接收到所有可靠信号
43. 信号的定义及其分类。
信号是一种软中断,用来通知进程发生了异步事件, 0~31分为不可靠信号, 32~63为可靠信号
44. 匿名管道及其特点。
匿名管道:用于实现有亲缘关系的进程之间相互通信 ,创建在内核空间,通信结束后消失
45. 有名管道和匿名管道的区别?
无名管道用于实现具有亲缘关系的进程间通信 ;有名管道用于实现非亲缘关系的进程间通信;
有名管道 是一个特殊的文件 结束通信后内容消失但文件不消失;无名管道 结束通信后消失
46. 进程和线程的区别?
线程是操作系统进程中能够并发执行的实体,是处理器调度和分派的基本单位。
区别 :
1.调度
在传统操作系统中进程是拥有资源和调度的基本单位
在引入了线程的操作系统中 线程是操作系统分派和调度的基本单位
进程是拥有资源的基本单位
2.并发性
进程之间可以并发执行,在同一进程中的线程也可以并发执行
3.拥有资源
进程是拥有系统资源的基本单位
线程基本上不拥有系统资源,但也拥有其运行所必要的资源,在同一进程中的线程共享进程的地址空间
4.系统开销
创建或撤销进程时,系统都要为其创建和回收进程控制块(PCB)
进程的创建和撤销的开销远大于线程的开销
在进程切换时,涉及到当前进程CPU环境的保存以及新被调度运行进程的CPU环境的设置,而线程的切换则仅需保存和设置少量寄存器内容
进程切换的开销也是远大于线程的
47. Please describe the difference of signal() and sigaction() in brief
不同点
signal()
安装的信号不能向信号处理函数传递信息
sigaction()
可设置进程的信号掩码,返回设置之前的sigaction结构
安装的信号可以向信号处理函数传递信息
相同点
都可以为指定的信号设置信号处理函数
共用同一个内核函数do_sigaction()
编程:
实验一
1.创建文件file1,写入字符串“abcdefghijklmn”;
2.创建文件file2,写入字符串“ABCDEFGHIJKLMN”;
3.读取file1中的内容,写入file2,使file2中的字符串内容为“abcdefghijklmn ABCDEFGHIJKLMN”
创建file1,file2 文件*
IO_open.c
#include<stdio.h>
#include<fcntl.h>
#include<unistd.h>
#include<string.h>
int main(int argc,char *argv[])
{
int IO_OPEN,IO_OPEN_1;//文件描述符
int write_num1,write_num2=-1;
char *buf="abcdefghijklmn";
char *buf2="ABCDEFGHIJKLMN";
if(argc<3)
{
printf("ERROR!number<3\n") ;
return -1;
}
IO_OPEN=open(argv[1],O_CREAT|O_RDWR,0755);
if(IO_OPEN==-1)
{
perror("open");
return -1;
}
IO_OPEN_1=open(argv[2],O_CREAT|O_RDWR,0755);
if(IO_OPEN_1==-1)
{
perror("open");
return -1;
}
write_num1=write(IO_OPEN,buf,strlen(buf));
write_num2=write(IO_OPEN_1,buf2,strlen(buf2));
close(IO_OPEN);
close(IO_OPEN_1);
return 0;
}
实现file1中的字符串写入file2中
IO_file_rw.c
#include<stdio.h>
#include<fcntl.h>
#include<unistd.h>
int main(int argc,char *argv[])
{
int fd,fd2=-1;
int rd_num,rd_num2,wt_num=-1;
char buf[128]={0};
char buf1[128]={0};
if(argc<3)
{
printf("ERROR!\n");
return -1;
}
if((fd=open(argv[1],O_RDONLY))==-1)//以读写方式打开file1
{
perror("open");
return -1;
}
if((fd2=open(argv[2],O_RDWR))==-1)//打开file2
{
perror("open");
return -1;
}
rd_num=read(fd,buf,128);
rd_num2=read(fd2,buf1,128);
/* 先将file2 文件中的字符串向后移动rd_num(file中的字符串长度)个位置*/
lseek(fd2,rd_num,SEEK_SET);
write(fd2,buf1,rd_num2);
lseek(fd2,0,SEEK_SET);
write(fd2,buf,rd_num);
/* lseek(fd2,0,SEEK_SET); //先将file1中的字符串写入file2 再将file2中的字符串追加到后面
write(fd2,buf,rd_num);
write(fd2,buf1,rd_num2);*/
close(fd2);
close(fd);
return 0;
}
实验二
编写代码,完成以下功能:
1.创建新文件,该文件具有用户读写权限
2.采用dup/dup2/fcntl复制一个新的文件描述符,通过新文件描述符向文件写入“class_name”字符串;
3.通过原有的文件描述符读取文件中的内容,并且打印显示;
#include<stdio.h>
#include<fcntl.h>
#include<unistd.h>
#include<string.h>
#include<sys/stat.h>
int main(int argc,char *argv[])
{
int fd=-1;
int fd2=-1;//新的文件描述符
int wrnum,rdnum=0;
char *buf="hello world\n";
char buf1[128]={0};
umask(0);//修改umask值
fd=open(argv[1],O_CREAT|O_RDWR,0600);
if(fd==-1)
{
perror("open");
return -1;
}
//复制文件描述符
fd2=dup(fd);//选取未被使用的最小的文件描述法
// fd2=dup2(fd,5);//使用指定的文件描述符 如果该文件描述符已被使用则关闭该文件描述符 重新使用
// fd2=fcntl(fd,F_DUPFD,1);//实现文件描述符复制,选择大于等于1的未被使用的文件描述符
wrnum=write(fd2,buf,strlen(buf));//使用新的文件描述符写入字符串
if(wrnum==0)//如果写入失败
{
perror("write");
return -1;
}
//此时文件指针位于文件末尾
lseek(fd,0,SEEK_SET);//将文件指针指向文件开始
rdnum=read(fd,buf1,128);//使用老的文件描述符将文件中的信息读取到buf1中
if(rdnum==0)
{
perror("read");
return -1;
}
printf("%s",buf1);//打印出读取的字符串
close(fd);
close(fd2);
return 0;
}
编写程序完成以下功能:
1. 递归遍历home目录,打印出所有文件和子目录名称及节点号。
2. 判断文件类型,如果是子目录,继续进行递归遍历,直到遍历完所有子目录为止。
IO_dirread.c源码
#include<stdio.h>
#include<fcntl.h>
#include<unistd.h>
#include<dirent.h>
#include<sys/stat.h>
#include<string.h>
int main(int argc,char *argv[])
{
int dir_read(const char *str);
dir_read(argv[1]);
return 0;
}
int dir_read(const char * str)
{
struct stat buf;
struct dirent *dirp=NULL;
DIR *dir=NULL;
char BUF[512]={0};
if((dir=opendir(str))==NULL)
{
perror("opendir");
return -1;
}
while((dirp=readdir(dir))!=NULL)
{
sprintf(BUF,"%s%s%s",str,"/",dirp->d_name);//获取目录的绝对路径
if(dirp->d_name[0]=='.')//跳过隐藏文件
continue;
if(stat(BUF,&buf)==-1)//需要使用绝对路径
{
perror("stat");
return -1;
}
if(S_ISDIR(buf.st_mode))//如果当前文件为目录,则递归查看其子目录
{
printf("%s %ld\n",BUF,dirp->d_ino);
dir_read(BUF);
}
else
{
printf("%s %ld \n",dirp->d_name,dirp->d_ino);
}
}
closedir(dir);
}
实验三
创建子进程
1.在子进程中打开文件file1,写入自己的“班级_姓名_学号”
2.父进程读取file1中的内容,并且打印显示。
3.在父进程中获取已经结束的子进程的状态信息,打印该信息,并且打印结束的子进程的进程号。
#include<stdio.h>
#include<fcntl.h>
#include<unistd.h>
#include<wait.h>
#include<stdlib.h>
#include<string.h>
#include<sys/types.h>
#include<sys/stat.h>
int main(int argc,char *argv[])
{
int fd,fd2=-1;
int i;
pid_t pid,pid_wait=-1;
int status;
char *buf="2班 a 007";
char BUF[128]={0};
if((pid=fork())==-1)
{
perror("fork");
exit(EXIT_FAILURE);
}
if(pid==0)
{
if((fd=open("myfile",O_CREAT|O_RDWR,0777))==-1)//在子进程中打开文件
{
perror("open");
exit(EXIT_FAILURE);
}
if(write(fd,buf,strlen(buf))==-1)
{
perror("write");
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
}
else
{
pid_wait=wait(&status);//使父进程处于阻塞状态
if((WIFEXITED(status)))//判断子进程是否正常终止
{
printf("normal exit,return value:%d\n",WEXITSTATUS(status));
}
printf("child PID: %d\n",pid_wait);
if((fd=open("myfile",O_RDONLY))==-1)
{
perror("parent open");
exit(EXIT_FAILURE);
}
if(read(fd,BUF,128)==-1)//读取文件
{
perror("read");
exit(EXIT_FAILURE);
}
printf("read myfile: %s\n",BUF);
close(fd);
exit(EXIT_SUCCESS);
}
}
实验四,第三题
1.编写程序实现以下功能:
利用有名管道文件实现进程间通信,要求
写进程向有名管道文件写入10次“hello world”
读进程读取有名管道文件中的内容,并依次打印。
创建有名管道源码
#include<stdio.h>
#include<fcntl.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/stat.h>
/*
*有名管道实现非亲缘关系的进程通信
*进程一 向有名管道中写入数据,如果进程二不已可读方式打开管道则进程一阻塞,直到进程二以可读方式打开管道
* */
int main(int argc,char *argv[])
{
int fd;
if(mkfifo("FIFO",0777)==-1)//创建有名管道
{
perror("mkfifo");
exit(EXIT_FAILURE);
}
printf("管道FIFO创建成功!\n");
return 0;
}
2.写进程
#include<stdio.h>
#include<fcntl.h>
#include<unistd.h>
#include<stdlib.h>
/*
*写进程
* */
int main(int argc,char *argv[])
{
int write_fd;
char buf[]="hello world";
if((write_fd=open("FIFO",O_WRONLY))==-1)
{
perror("open");
exit(EXIT_FAILURE);
}
for(int i=0;i<10;i++)
{
if(write(write_fd,buf,sizeof(buf)-1)==-1)
{
perror("write");
exit(EXIT_FAILURE);
}
usleep(10);
}
exit(EXIT_SUCCESS);
}
读进程
#include<stdio.h>
#include<fcntl.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
/*
*读进程
* */
int main(int argc,char *argv[])
{
int read_fd;
char buf[128]={0};
if((read_fd=open("FIFO",O_RDONLY))==-1)
{
perror("open");
exit(EXIT_FAILURE);
}
for(int i=0;i<10;i++)
{
if(read(read_fd,buf,sizeof(buf))==-1)
{
perror("write");
exit(EXIT_FAILURE);
}
printf("%s\n",buf);
memset(buf,0,sizeof(buf));
}
exit(EXIT_SUCCESS);
}