系统编程.docx
《系统编程.docx》由会员分享,可在线阅读,更多相关《系统编程.docx(55页珍藏版)》请在冰豆网上搜索。
系统编程
第1章:
系统开发基本概念
1.操作系统基本构成
内核3大作用:
管理硬件(内核里面许多附加驱动程序);管理内存(分页管理,换入换出);管理进程(控制进程声明周期)
管理系统工具、应用程序和硬件的交互,和硬件的操作必须通过内核。
(内核提供系统调用)
管理交换空间、文件系统、精灵程序;
系统调用:
内核给用户提供的一组函数,用户通过它们可以直接操作内核实现具体功能。
库函数:
例如stdio.h里面输入输出库函数,完全是基于系统调用实现。
Shell:
本质上也是通过系统调用实现。
用户应用程序:
可以借助以上三者(本质借助系统调用)实现和内核的交互。
2.Linux系统基本概念
文件(系统上数据/etc/passwd|设备/dev/sda1的组织方式)
文件系统:
管理文件的一个数据结构、逻辑组织
程序(可运行代码,静态)
进程(程序运行实例|整个过程,动态)
线程:
轻量级进程,只需要很小的资源,就可以维持运行,进程需要大量资源。
信号:
进程间通信技术之一。
$kill-9uid
客户机服务器:
由资源提供消耗而定,与网络无关
3.linux系统启动流程
第一阶段:
bootlooder
1)硬件自检,初始化芯片CPU
2)初始化少量外围设备
3)启动选择器(双系统),加载操作系统内核
第二阶段:
linux内核启动阶段
1)挂载initrd文件,形成最小根文件系统;(/boot/initrd*)
2)以最小文件系统为依托,导入整个文件系统,执行/sbin/init文件,实现系统的启动
0号进程:
内核进程
ps-ef|grep/sbin/init==>1号进程
第三阶段:
用户进程启动阶段
由1号进程启动多个子进程,支持系统启动
1)exec/etc/init.d/*执行相关文件(shell脚本),启动相应程序,实现系统配置
2)exec/sbin/getty初始化标准输入、输出、错误设备
exec/bin/login产生用户登录界面
3)1号进程管理孤儿进程(父进程死掉的子进程)
4.linux内存布局
系统启动以后,对于32位平台,4G内存分为两部分,用户空间3G(0x00000000-0xC00000000),内核空间1G(0xC0000000-0xFFFFFFFF);
进程操作的数据、使用的资源属于用户空间,执行用户空间的代码,我们称之为进程处于用户态。
内核态:
进程在某些时间点操作内核数据或资源或运行内核代码。
通过系统调用实现。
5.系统调用
全部都是系统提供的C语言函数。
系统调用标准,一个四个:
ISOC(C语言标准,由C语言实现)
IEEEPOSIX(系统调用的构成,规定函数[名字、参数含义、返回值含义,出错标识]相关标准,最小系统调用标准)
SignleUNIXSpecification(用于扩展【建议性】的系统调用标准)
FIPS(基本淘汰,联邦信息处理标准,美国政府制定)
UNIX支持POSIX标准,windows逐步向该标准靠拢
第2章:
文件I/O
1.文件描述符
FILE结构体:
流如何关联到指定文件?
fopen();
typedefstruct
{
int-fd;//文件号文件描述符
int-cleft;//缓冲区中剩下的字符
int-mode;//文件操作模式
chsr*nextc;//下一个字符位置
char*buff;//文件缓冲区位置
}FILE;
进程如果要操作指定文件,必须通过内核,建立3个数据结构(文件描述符表FDT、文件表项、I节点),一级一级关联到文件。
文件表项FI:
文件状态信息(操作状态,偏移量、文件指针计数器等)
I节点:
文件属性信息(一般稳定,不轻易变化),类型、长度...(V节点属于UNIX)
包含文件描述符表的头文件:
/usr/src/linux-headers-3.2.0-23/include/linux/sched.h
structtask_struct{};存放进程相关信息的结构体
structfiles_struct*files;//文件描述符表,长度1024
2.文件I/O系统调用
openreadwritelseek(调整偏移量)close五个函数
练习:
实现cp命令,src/m_cp.c
size_t:
32位平台,typedefunsignedintsize_t;
ssize_t:
typedefsignedintssize_t;
creat()函数,早期版本,不推荐使用。
off_tlseek(intfd,off_toffset,intwhence);
off_t:
带符号的长整形数signedlongint
返回值是绝对偏移量(相对文件头)。
练习1:
通过lseek函数,计算文件长度,计算当前文件当前偏移量值。
src/lseek.c修改src/m_cp.c,每次读写,打印偏移量
练习2:
空洞(二进制0)文件的制作:
lseek(fd,10L,SEEK_END);//OK
lseek(fd,-10L,SEEK_SET);//ERROR
src/hole_file.c
$od-cfile.txt查看文件内容(字符形式)
3.I/O效率
读写数据时,发生中断次数越少,CPU效率更高。
一个磁盘块4096字节,即4KB时,效率最高,硬件相关。
4.文件共享
不同进程同时打开同一个文件,文件描述符表、文件表项、I节点的使用情况。
I节点共享,其他各自对立。
思考,两个进程同时操作同一文件追加数据,是否会遇到问题?
练习:
模拟上述情况的发生。
src/f_append1.csrc/f_append2.c
如何解决该问题?
让一个进程操作文件时,不允许别的进程做类似的操作。
即把操作调整成为原子操作。
5.原子操作--文件追加
打开文件时O_APPEND标记,操作文件时,保存文件长度、调整偏移量、修改文件长度三件事情成为一个原子操作,密不可分。
设置该标志后,调整偏移量不可用。
原子操作--文件中间部分插入
pread函数
pwrite函数
原子操作--文件创建
判断文件是否存在,创建文件,两件事是一个原子操作,即可避免问题。
open(“a.txt”,O_WRONLY|O_CREAT|O_EXCL,0777);
作业:
完整cat命令的使用:
Catcopy(STDIN_FILENO,STDOUT_FILENO);
cata.txtb.txtc.txtcopy(fd,STDOUT_FILENO)
cat都是元字符,属于shell提供的;
cat>p.txt
catp.txt
扩展:
cat命令用+替换<;用-替换>;
思考如何实现?
第二天授课:
6.文件描述符操作
Intdup(intfd);返回新的文件描述符(从FDT选最小的minfd),返回的minfd(和fd公用同一个文件指针)也
Intdup2(intfd1,intfd2);断开fd2与原来文件的关联(closefd2),将其关联到fd1文件描述符上。
(将fd1拷贝到fd2上,返回fd2成功),以上两个操作时原子操作。
练习:
使用dup2函数实现cat+(<)a.txt-(>)b.txt
src/my_cat.c
作业1:
完善src/my_cat.c
7.fcntl函数,修改文件结构信息
Intfcntl(intfd,intcmd,...);
第二个参数cmd,决定了函数的实际功能(共5种)。
1)fcntl(3,F_DUPFD);==dup(3);复制描述符,返回新描述符
close(5);fcntl(3,F_DUPFD,5);==dup2(3,5);把3复制到5,返回5。
2种区别:
dup2()是原子操作;出错errno;
2)修改FDT中fd的值,暂时不关注(第四章);
3)获得,修改文件状态标志(FI);
intval=fcntl(3,F_GETFL);返回值即可获得
fcntl(3,F_SETGL,val2);修改(val2覆盖)(val|var2追加)
去除val&=~var2;fcntl(3,F_SETFL,val)
注意:
其中O_RDONLYO_WRONLYO_RDWR并非各占1位,他们分别对应值0,1,2,且他们互斥。
如果判断,则(val&O_ACCMODE)==O_RDWRONLY。
。
。
练习:
获取一个文件描述符的读写权限。
src/read_or_write.c
练习:
实现追加、去除、修改文件状态标志模块函数。
include/io.h
打开一个文件,写入数据,添加O_APPEND标记,调整偏移量到头部,继续写入数据,验证是否添加标记成功!
Src/add_flag.c
8.ioctl函数I/O其他操作
主要针对设备文件,进行其他相关操作。
intioctl(intfd,intrequest,...);
后面两个参数,往往根据不同的驱动接口进行设置。
9.高级IO技术
1)阻塞IO模型,之前做的所有操作全部都属于阻塞模型,(主要针对于低速设备)
2)非阻塞IO模型,如果可以读写,就成功;不能读写,就返回错误信息,做别的事情
3)I/O多路转换,介于以上两者之间
4)信号驱动IO模型,信号章节讲解
5)异步IO模型,淘汰技术
练习:
非阻塞模式处理低速外围设备(键盘、屏幕)的输入和输出;
src/nonblock_read.c
src/nonblock_write.c/etc/ld.so.cache文件很大,读取数据然后输出到屏幕上,设置标准输出为非阻塞模式,看效果。
./a.outa.txt出错信息,调试信息输出到a.txt里面,方便观察
第三天授课
10.I/O多路转换
intselect(intnumfds,fd_set*readfds,fd_set*writefds,
fd_set*exceptfds,structtimeval*timeout);
fd_set:
若干个描述符构成的集合,是long类型的数组,共占128字节(1024位).exceptfds:
出现异常的
select系统调用,让内核观察一组文件描述符,返回准备就绪的描述符个数;如果出错返回-1;如果超时返回0;
structtimeval{//观察的时间
longtv_sec;//second秒
longty_usec;//usecod微秒
};
练习:
将0号描述符加入到文件描述符集中,设置内核观测,如果0号可用,操作,否则继续观测。
src/select_test.c
11.文件锁(记录锁)
多进程同时操作一个文件,如何实现数据操作,不至于出现紊乱。
记录锁分类:
共享读锁独占写锁
如何上锁:
fcntl函数实现
intfcntl(intfd,intcmd,...structflock*flockptr);
cmd:
F_SETLKF_SETLKW(wait阻塞)F_GETLK设置、获取锁
structflock{
shortl_type;//锁类型F_RDLCKF_WRLCKF_UNLCK
off_tl_start;//偏移量offset
shortl_whence;//相对位置SEEK_SETSEEK_CURSEEK_END
off_tl_len;//锁长度0表示整个文件
pid_tl_pid;//进程号(上锁时不需要,查询时使用)
};
include/io.hsrc/io.c
intlock_reg(fd,cmd,type,start,offset,len);
#defineREAD_LOCK[W](fd,start,offset,len)lock_reg(...)
练习:
两个进程同时对同一个文件一个字节一个字节写入数据,对文件上锁以实现数据安全。
src/append_file.c
注意:
文件锁分为建议性锁和强制性锁两类,默认全部是建议性锁。
Linux下命令设置强制性锁,$mount-Oomand/dev/sda3/opt
作业:
两个进程同时对同一文件(锁全部)读取数据,能否同时向下执行。
src/read_file.c
两个进程一个读取一个写入数据,验证能否同时执行。
src/read_write_file.c
扩展:
使用基本系统调用开发C语言标准库。
FILE*fp=fopen(“sss.txt”,”r”);
fflush(fp);
fclose(fp);
intc=fgetc(fp);
fwrite(buff,size,num,fp);
fread(buff,size,num,fp);
12.存储映射
用户空间,存储映射区,存在于堆空间和栈空间之间;
内核空间,也有内存映射区;
void*mmap(void*addr,size_tlen,intprot,intflag,intfileds,off_toff);返回值是映射的首地址。
void*addr:
确定存储映射的首地址,一般0,由系统提供
size_tlen:
映射长度
intprot:
映射模式PROT_READPROT_WRITEPROT_EXECPROT_NONE
intflag:
映射类型(是否立即同步)MAP_FIXEDMAP_SHAREDMAP_PRIVATE
intfileds:
文件描述符,操作的文件
off_toff:
文件偏移量
注意:
映射时往往不按照用户指定的len值映射,一般都会多。
不能对空白文件映射,没有任何意义。
解除映射
intmunmap(caddr_taddr,size_tsize);
练习:
映射一个文件到用户空间,然后读取,修改文件信息。
src/mmap_test.c
练习:
利用映射实现cp命令,src/mmap_cp.c
第4天:
(复习)
第2章文件和目录
1.stat结构体
用来描述文件属性信息;
1.struct stat
2.{
3.
4. dev_t st_dev; /* -文件所在设备的ID*/
5.
6. ino_t st_ino; /* inode number -inode节点号*/
7.
8. mode_t st_mode; /* protection -保护模式?
*/
9.
10. nlink_t st_nlink; /* -链向此文件的连接数(硬连接)*/
11.
12. uid_t st_uid; /* user ID of owner -user id*/
13.
14. gid_t st_gid; /* group ID of owner - group id*/
15.
16. dev_t st_rdev; /* -设备号,针对设备文件*/
17.
18. off_t st_size; /* total size, in bytes -文件大小,字节为单位*/
19.
20. blksize_t st_blksize; /* blocksize for ilesystem I/O -系统块的大小*/
21.
22. blkcnt_t st_blocks; /* numberofblocks allocated -文件所占块数*/
23.
24. time_t st_atime; /* time of last access -最近存取时间*/
25.
26. time_t st_mtime; /* time of last modification -最近修改时间*/
27.
28. time_t st_ctime; /* time of last status change - */
29.
30.};
intstat(constchar*pathname,structstat*buf);
intfstat(intfd,structstat*buf);
操作符号链接文件时,获得的是源文件的属性信息;
intlstat(constchar*pathname,structstat*buf);
可以用来获得符号文件本身的属性信息;
成功返回0,失败返回-1;
练习:
分别通过文件名、文件描述符获得文件属性信息,打印文件长度。
Src/lstat_test.c
char*ctime(time_tconst*time_value);将时间转换成能看懂的格式。
2.文件类型
七个宏用来判断七种文件类型:
普通文件(regularfile)S_ISREG()
目录文件(directoryfile)S_ISDIR()
块特殊文件(blockspecialfile)S_ISBLK()
字符特殊文件(chatacterspecialfile)S_ISCHR()
FIFO(namedpipe)S_ISFIFO()
套接字(socket)S_ISSOCK()
符号链接(symboliclink)S_ISLNK()
练习:
获得文件属性信息,判断其文件类型。
src/file_type.c
3.设置用户ID、组ID(所有进程都有角色)
实际用户ID(getuid()),实际组ID:
/bin/login实际登录的用户(谁启动了该进程,整个进程运行期间,不变)
有效用户ID(geteuid()),有效组ID:
(决定进程在访问文件时的权限问题)
多数情况下,实际ID和有效ID相同。
特殊情况下(进程对应的文件有设置位时,有效用户ID在进程执行过程中演变成由文件的拥有者决定)。
$passwd修改实际用户密码/usr/bin/passwd
$ls-l/usr/bin/passwd
$-rwsr-xr-x1rootroot412844月92012/usr/bin/passwd
在程序的执行过程中临时改变有效用户为root,通过保留用户ID、组ID实现。
4.文件权限
9中访问权限位:
S_IRUSR,S_IWUSR,S_IXUSR
S_IRGRP,S_IWGRP,S_IXGRP
S_IROTH,S_IWOTH,S_IXOTH
S_ISUID,S_ISGID设置用户ID和组ID。
练习:
获取文件权限,ls-l形式输出src/file_perm.c
作业:
实现简易版本的ls-l功能。
src/myls_l.c
5.access函数
用于验证实际用户能否访问指定文件
Intaccess(constchar*pathname,intmode);
成功返回0,失败返回-1;
Mode取值:
R_OK读权限W_OKX_OKF_OK:
是否存在
练习:
判断实际用户对指定文件的访问权限。
src/access.c
6.umask函数
创建屏蔽字(掩码):
创建文件时起作用。
创建文件:
0777-0111==06660110110110
&~umask(022)0111101101
0110100100==>644
创建目录:
0777&~(umask)==>
mode_tumask(mode_tmode);设置新的掩码,返回值是以前掩码
练习:
修改掩码,然后创建新文件src/umask.c
7.chmodfchmod函数
修改文件权限位
主要:
若要修改,满足条件:
进程有效ID(geteuid())==所有者ID实际ID:
getuid();
进程具有超级用户权限
练习:
修改指定文件权限位。
src/chmod_test.c
./a.out644a.txt
8.修改用户拥有者所属组
Intchown(char*pathname,uid_towner,gid_tgroup);
Intfchown(intfd,uid_towner,gid_tgroup);
//针对链接文件
Intlchown(char*pathname,uid_towner,gid_tgroup);
注意:
非超级用户不能修改用户拥有者uid。
非超级用户如果要修改gid,需要保证第二个参数的值为owner==-1|geteuid();
练习:
修改制定文件的uid和gid;src/chown_test.c
第5天:
9.文件截断(从尾部截,修改Inode中文件长度值)
inttruncate(char*pathname,off_tl);
Intftruncate(intfd,off_tl);
成功返回0,出错返回-1
练习:
修改制定文件长度;src/truncate_test.c
10.文件系统概念
索引块、数据块
硬链接:
指向同一个I节点(磁盘块,与内核Inode不同),指向同一个数据块;
软链接:
指向不同I节点,有自己对应的数据块;
$ls-li(I节点编号)a.txt
11.硬链接操作(操作目录数据块内容)
必须同一个分区;
不能对目录创建硬链接;
练习:
建立一个链接文件,然后删除源文件。
src/link_test.c
12.软链接操作
读取链接文件本身数据,返回读取字节数,失败返回-1;
ssize_treadlink(char*pathname,char*buff,size_tsize);
创建链接文件,成功返回0,失败返回-1;
Intsymlink(char*actualpath,char*sympath);
练习:
创建一个软链接文件,然后读取链接文件数据、链接到文件数据。
Src/readlink.c./a.outa.txta.ls
注意:
可以对不存在的文件创建软链接文件;
可以跨文件系统;
13.文件时间
st_atime:
文件数据(数据块)最后访问时间read(用户可以修改)
st_mtime:
文件数据(数据块)最后修改时间write(用户可以修改)
st_ctime:
I节点(文件属性)最后修改时间(用户无法操作)
思考:
如果对文件追加,以上三个数据都有哪些改变?
14.修改时间属性
intutime(char*pathname,structutimbuf*times);
成功返回0,失败返回-1;
structutimbuf{
time_t