系统编程
1
计算机系统的基本构成:
处理器(processor)
主存储器(等待调度的程序)
输入输出模块(二级存储设备,通信设备,终端,键盘,显示器,)
系统总线(地址,控制,数据)
2
CPU-----------IO(轮询,定期主动轮询外设,工作效率低)(虾苗,温度传感器)
CPU-----------IO(中断 外设主动发出请求软中断,硬中断)(8259A,)
CPU-----------IO(DMA直接内存访问 外设与内存的直接访问,跳过CPU)(摄像)
数据开始: (1)确定数据读写方向,
(2)确定读写内存设备的地址,外设数据地址(连续的物理内存)
(3)确定数据的大小,
(4)申请DMA通道,(SD,U盘,MP3)
(5)注册中断
3 两个内部寄存器
存储地址寄存器(MAR)
确定下一次读写的存储器地址
存储缓冲寄存器(MBR)
写入存储器的数据
从存储器中读出的数据
4
.exe内存分配管理:
代码段:程序的入口地址(main)
数据段:初始化为零的,未初始化的
(1.常量字符串,2全局变量3静态变量)
堆:程序员控制(malloc)由下往上,可以在堆中申请大块空间
栈:空间有限4M,不能申请大块空间
(局部变量,形参,函数返回值 )
5
CPU---中断控制器-----中断号------中断向量表-----PC-----换到内存中------从内存中出来
中断分类:(软中断)程序中断非法指令,越界访问内存,除数为0;
(硬中断)时钟中断;IO中断;硬件失效中断;
CPU现场保存到内存空间,进程的上下文,恢复现场
中断上下文:中断与进程
进程上下文:进程与进程
执行时才能响应中断
响应中断;挂起
片内内存 高速缓存:Icach代码
Dcach数据
6
操作系统:
程序开发
编辑器和调试器
程序执行
IO设备访问
文件访问控制
系统访问
现在操作系统的特点
微内核体系结构
多线程
对称多处理
分布式操作系统
面向对象设计
7
与进程的区别:
线程是指进程内的一个执行单元,也是进程内的可调度实体.
(1)地址空间:进程内的一个执行单元;进程至少有一个线程;它们共享进程的地址空间;而进程有自己独立的地址空间;
(2)资源拥有:进程是资源分配和拥有的单位,同一个进程内的线程共享进程的资源
(3)线程是处理器调度的基本单位,但进程不是.
4)二者均可并发执行.
8
进程的创建
#include <pthread.h>
#include <stdio.h>
void* print_xs (void* unused)
{
while (1)
fputc ('x', stderr);
return NULL;
}
int main ()
{
pthread_t thread_id;
pthread_create (&thread_id, NULL, &print_xs, NULL);
while (1)
fputc ('o', stderr);
return 0;
}
信号的同步传输机制
#include"stdio.h"
#include"string.h"
#include"signal.h"
#include"sys/types.h"
#include"unistd.h"
sig_atomic_t sigusr1_count=0;
void handle(void)
{
printf( "this is:" );
}
int main()
{
int i=10;
pid_t pid;
pid=fork();
if(pid==0)
{
sleep(1);
for(i=0;i<10;i++)
{
kill(getppid(),SIGUSR1);
sleep(1);
}
}
else if(pid>0)
{
struct sigaction sa;
memset(&sa,0,sizeof(sa));
sa.sa_handler=&handle;
sigaction(SIGUSR1,&sa,NULL);
while(i--)
{
pause();
printf("pause:%d\n",i);
9
/bin/ — 用来贮存用户命令。目录 /usr/bin 也被用来贮存用户命令。
/sbin/ — 许多系统命令(例如 shutdown)的贮存位置。目录/sbin 中也包括了许多系统命令。
/root/ — 根用户(超级用户)的主目录。
/mnt/ — 该目录中通常包括系统引导后被挂载的文件系统的挂载点。譬如,默认的光盘挂载点是/mnt/cdrom
/boot/ — 包括内核和其它系统启动期间使用的文件。
/lost+found/ — 被 fsck 用来放置零散文件(没有名称的文件)
/lib/ —用来存放系统动态连接共享库的。
/dev/ — 贮存设备文件。
10
进程的总结
(1)概念
一个正在执行的程序
一个正在计算机上执行的程序实例
能分配给处理器并有处理器执行的实体
(2)基本元素
标志符
状态
优先级
程序计数器
内存指针
上下文数据(context)
IO的状态信息
审计信息
(3)状态
运行态
未运行态
(4)创建原因
新的批处理作业
交互登录
操作系统因为提供一项服务而创建
由现有的进程派生
(5)终止原因
正常完成
超过时限
无可用内存
越界
保护错误
算术错误
时间超出
I/0失败
无效指令
特权指令
数据误用
父进程终止
父进程请求
(6)挂起的原因
交换
交互式用户请求
定时
父进程请求
(7)进程管理
进程的创建和终止
进程的调度和分派
进程切换
进程同步和进程间通信
进程控制块的管理
(8)进程的创建
分配进程ID
唯一的
分配空间
初始化进程控制块
设置正确的连接
创建和扩充其他的数据结构
(9)进程的切换时机
时钟中断
进程允许执行的时间片
IO中断
内存失效
进程的地址是虚拟地址
包含这个虚拟地址的内存块调入主存中
Trap
发生错误或异常
进程被转换到退出状态
系统调用
比如打开文件
通常导致进程为阻塞状态
(10)进程切换过程
保存处理器上下文
PC 和 其他的寄存器
更新当前处于运行态的进程的进程控制块
进程的状态改变
把进程的进程控制块移到相应的队列
选择另一个进程执行
更新所选择的进程控制块
进程的状态为运行态
更新内存管理的数据结构
管理地址转换
恢复新进程的处理器上下文
11
头文件总结
(1)声明进程的
#include <sys/types.h>
#include <unistd.h>
(2 )C标准库函数:system()
#include <stdlib.h>
3信号依靠值来区分
#include<signal.h>
(4)wait()族函数系统调用
#include <sys/wait.h>
(5)有关于线程的
#include <pthread.h>
(6)分配内存
#include <malloc.h>
(7)共享内存
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <sys/types.h>
(8)文件映射
#include <sys/mman.h>
(9)创建fifo
#include <sys/types.h>
#include <sys/stat.h>
12
进程间通信方式总结
共享内存(分配 绑定 脱离 释放)
1:效率高,特别是大数据量通信
2:各进程需要协调共同的键值,需要提供额外机制防止竞争条件
3:异步通信
4:和信号量一同使用
内存映射(多进程共享文件进行通信的机制)
1:分配内存
2:读入文件内容至内存
3:内存的内容回写到文件
信号量
1:线程信号量:同步多线程环境的计数器。
2:进程间同步的信号量: System V信号量,操作和sharedmemory类似。
消息队列
1打开或创建消息队列
2读写操作
3获得或设置队列属性
管道
1:单向信息的传递设备
2:用于进程的线程之间或者是父子进程之间通信
3:自动同步进程(管道的容量是有限的当管道写满的时候,写入端自动阻塞管道容量4096字节)
FIFO
1:在文件系统中是一个有名字的管道
2:任何进程都可以打开
3:进程间无需关联
SocketSocket
1:是一种双向通信设备
2:同一主机不同进程间通信
3:不同主机间的通信
A
#include"stdio.h"
#include"sys/shm.h"
#include"string.h"
#include"fcntl.h"
#include"sys/stat.h"
int main()
{
pid_t pid;
int share_id;
share_id=shmget(IPC_PRIVATE,getpagesize(),IPC_CREAT | IPC_EXCL | S_IRUSR | S_IWUSR );
pid=fork();
if(pid==0)
{
char *share1=NULL;
share1=(char*) shmat (share_id, 0,0);
memset(share1,0,getpagesize());
strcpy(share1,"hello,everyone\n");
shmdt(share1);
}
else if(pid>0)
{
char *share2=NULL;
share2=(char*) shmat(share_id,0,0);
printf("read characters from memory!\n");
printf("%s\n",share2);
shmdt(share2);
shmctl(share_id,IPC_RMID,0);
}
return 1;
}
B
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include<stdio.h>
#include<string.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#define MAX 3
union semun
{
int val;
struct semid_ds *buf;
unsigned short int *array;
struct seminfo *_buf;
};
int sig_alloc(key_t key, int sem_flags)
{
return semget (key, MAX, sem_flags);
}
int sig_destory (int semid,int numth)
{
union semun ignored_argument;
return semctl (semid, numth, IPC_RMID,ignored_argument);
}
/*
parray is a set of initialized value
*/
int sig_init (int semid,int *parray)
{
union semun argument;
int i=0;
for(i=0;i<MAX;i++)
{
// values[i] = *(parray+i);
argument.array = parray;
semctl (semid, i, SETALL, argument);
}
}
int sig_wait(int semid,int numth)
{
struct sembuf operations[MAX];
operations[numth-1].sem_num = numth-1;
operations[numth-1].sem_op = -1;
operations[numth-1].sem_flg = SEM_UNDO;
return semop(semid,operations,1);
}
int sig_post(int semid,int numth)
{
struct sembuf operations[MAX];
operations[numth-1].sem_num = numth-1;
operations[numth-1].sem_op = 1;
operations[numth-1].sem_flg = SEM_UNDO;
return semop(semid,operations,1);
}
int main()
{
pid_t pid;
int sig_id,i=0;
int sig_val[MAX]={1,0,0};
sig_id=sig_alloc(0,IPC_CREAT);
sig_init(sig_id,sig_val);
pid=fork();
if(pid==0)
{
while(++i<10)
{
sig_wait(sig_id,3);
printf("*************** \n");
sig_post(sig_id,3);
}
}
else if(pid)
{
i=0;
while(++i<10)
{
sig_wait(sig_id,1);
printf("++++++++++++++++\n");
sig_post(sig_id,1);
}
}
return 1;
}
1,6 Top
C
#include <sys/mman.h>
#include <sys/types.h>
#include<stdio.h>
#include<string.h>
#include<sys/stat.h>
#include<stdlib.h>
#include<fcntl.h>
#include<unistd.h>
#define FILE_LENGTH 100
int main(int argc,char **argv)
{
int fd1,fd2;
char *pfile=NULL;
char *load=NULL;
int num;
if(argc<3)
{
printf("please input more file\n");
return 0;
}
fd1=open(argv[1],O_RDONLY);
fd2=open(argv[2],O_RDWR|O_CREAT,S_IRUSR|S_IWUSR);
printf("fd2=%d\n",fd2);
//fd2=open(argv[2],O_WRONLY);
lseek (fd2, FILE_LENGTH+1, SEEK_SET);
write (fd2, "", 1);
lseek (fd2, 0, SEEK_SET);
printf("num=%d\n",num);
printf("fd2=%d\n",fd2);
pfile=(char*)mmap(0,FILE_LENGTH,PROT_WRITE|PROT_READ,MAP_PRIVATE,fd2,0);
read(fd1,pfile,FILE_LENGTH);
write(fd2,pfile,FILE_LENGTH);
close(fd2);
printf("pfile=%d\n",pfile);
munmap(pfile,FILE_LENGTH);
close(fd1);
return 1;
}
D
#include <sys/mman.h>
#include <sys/types.h>
#include<stdio.h>
#include<string.h>
#include<sys/stat.h>
#include<stdlib.h>
#include<fcntl.h>
#include<unistd.h>
int main ()
{
int fds[2];
pid_t pid;
pipe (fds);
pid = fork ();
if (pid == (pid_t) 0)
{
char str[10000];
sleep(1);
close(fds[1]);
read(fds[0],str,10000);
printf("%s",str);
close(fds[0]);
}
else if(pid>0)
{
FILE*fp;
char a[80];
close(fds[0]);
fp=(fopen("copy1.c","r"));
if(fp==NULL)
{
printf("can not open!!");
exit(0);
}
else
{
while(1)
{
if(fread(a,80,1,fp)==0) break;
write(fds[1],a,sizeof(a));
}
}
wait();
close(fds[1]);
fclose(fp);
return 0;
}
13
静态库在程序编译时会被连接到目标代码中,程序运行时将不再需要该静态库。
动态库在程序编译时并不会被连接到目标代码中,而是在程序运行是才被载入,因此在程序运行时还需要动态库存在。
静态情况下,他把库直接加载到程序里,而在动态链接的时候,他只是保留接口,将动态库与程序代码独立。这样就可以提高代码的可复用度,和降低程序的耦合度。
14
判断文件结束有两种方法:EOF和feof()
查看stdio.h可以看到如下定义:
#define EOF (-1)
#define _IOEOF 0x0010
#define feof(_stream) ((_stream)->_flag & _IOEOF)
有人说EOF只能用于文本文件,其实不然,还要看定义的变量的类型。下面这段程序对文本文件和二进制文件都可以:
int c;
while((c=fgetc(fp)) != EOF)
{
printf("%X/n", c);
}
如果读到了FF,由于c定义为int型,所以实际上c=0x000000FF,不等于EOF(-1=0xFFFFFFFF),因此不会误判为文件结尾。
但是如果把c定义为char类型,就有可能产生混淆了。
char c;
while((c=fgetc(fp)) != EOF)
{
printf("%X/n", c);
}
因为文本文件中存储的是ASCII码,而ASCII码中FF代表空值(blank),一般不使用,所以如果读文件返回了FF,说明已经到了文本文件的结尾。但是如果是二进制文件,其中可能会包含FF,因此不能把读到EOF作为文件结束的条件,此时只能用feof()函数。
在VC里,只有当文件位置指针(fp->_ptr)到了文件末尾,然后再发生读/写操作时,标志位(fp->_flag)才会被置为含有_IOEOF。然后再调用feof(),才会得到文件结束的信息。因此,如果运行如下程序:
char c;
while(!feof(fp))
{
c = fgetc(fp);
printf("%X/n", c);
}
会发现多输出了一个FFFFFFFF,原因就是在读完最后一个字符后,fp->flag仍然没有被置为_IOEOF,因而feof()仍然没有探测到文件结尾。直到再次调用fgetc()执行读操作,feof()才能探测到文件结尾。这样就多输出了一个-1(即FFFFFFFF)。
正确的写法应该是:
char c;
c = fgetc(fp);
while(!feof(fp))
{
printf("%X/n", c);
c = fgetc(fp);
}
这么说其实feof()是可以用EOF来代替的喽?不是,这里还有一个问题。fgetc返回-1时,有两种情况:读到文件结尾或是读取错误。因此我们无法确信文件已经结束, 因为可能是读取错误! 这时我们需要feof()。
15
中断处理程序不能睡眠,用信号量
进程占用资源时间较长时,用信号量(进程上下文),时间较短时用自旋锁(等待获取自旋锁);
3大块(CPU,RAM,FLASH)
操作系统是常驻内存的可执行软件一旦响应就是死循环;
处于运行态的进程只有一个,其他的都是就绪态;
WINDOW:段
LINUX;页
物理地址是实际地址
虚拟地址:32bit 4G-1 0————3G用户空间的虚拟地址3G-------------4G内核空间
虚拟——物理内存的映射
辅助存储-硬盘(交换分区)
CPU----MMU-----物理内存
缺页:内存空间不足;动态扩展