文件复制
//将一个文件从from拷贝到to中
int copyfile(int from,int to)
{
char *bp;
char buf[1024];
int r,w;//r 读 w 写
int total=0;
for(;;){//循环复制
r=read(from,buf,1024);
if (r<=0) break;
bp=buf;
w=write(to,bp,r);
if (w<=0) break;
total+=w;
r-=w;
bp+=w;
}
if(r==0) return total;
else exit(5);
}
驱动程序
first_drv驱动程序需要以下几步
(1)写出驱动程序first_drv_open first_drv_write
(2)需要定义file_operations结构体来封装驱动函数first_drv_open first_drv_write
对于字符设备来说,常用file_operations以下几个成员:
(3) 模块加载函数,通过函数 register_chrdev(major, “first_drv”, &first_drv_fops) 来
注册字符设备
(4)写驱动的first_drv_init 入口函数来调用这个register_chrdev()注册函数,
(5)通过module_init()来修饰入口函数,使内核知道有这个函数
(6)写驱动的first_drv_exit出口函数,调用这个unregister_chrdev()函数卸载,
(7) 通过module_exit()来修饰出口函数
(8) 模块许可证声明, 最常见的是以MODULE_LICENSE( “GPL v2” )来声明
/*1写出驱动程序first_drv_open first_drv_write */
/*inode结构表示具体的文件,file结构体用来追踪文件在运行时的状态信息。*/
static int first_drv_open(struct inode *inode, struct file *file)
{
printk(“first_drv_open\n”); //打印,在内核中打印只能用printk()
return 0;
}
/*参数filp为目标文件结构体指针,buffer为要写入文件的信息缓冲区,count为要写入信息的长度,ppos为当前的偏移位置,这个值通常是用来判断写文件是否越界*/
static ssize_t first_drv_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
{
printk(“first_drv_write\n”); //打印,在内核中打印只能用printk()
return 0;
}
/*2定义file_operations结构体来封装驱动函数first_drv_open first_drv_write */
static struct file_operations first_drv_fops = {
.owner = THIS_MODULE, //被使用时阻止模块被卸载
.open = first_drv_open,
.write = first_drv_write,
};
/*4写first_drv_init入口函数来调用这个register_chrdev()注册函数*/
int first_drv_init(void)
{
register_chrdev (111, “first_drv”, &first_drv_fops); //111:主设备号,”first_drv”:设备名
return 0;
}
/*5 module_init修饰入口函数*/
module_init(first_drv_init);
/*6 写first_drv_exit出口函数*/
void first_drv_exit(void)
{
unregister_chrdev (111, “first_drv”); //卸载驱动,只需要主设备号和设备名就行
}
/*7 module_exit修饰出口函数*/
module_exit(first_drv_exit);
/*8许可证声明*/
MODULE_LICENSE( "GPL v2" );
内存布局
#include<stdio.h>
#include<malloc.h>
#include<unistd.h>
int bss_var;
int data_var0=1;
int main(int argc,char **argv)
{
printf("below are addresses of types of process's mem\n");
printf("Text location:\n");
printf("\tAddress of main(Code Segment):%p\n",main);
printf("____________________________\n");
int stack_var0=2;
printf("Stack Location:\n");
printf("\tInitial end of stack:%p\n",&stack_var0);
int stack_var1=3;
printf("\tnew end of stack:%p\n",&stack_var1);
printf("____________________________\n");
printf("Data Location:\n");
printf("\tAddress of data_var(Data Segment):%p\n",&data_var0);
static int data_var1=4;
printf("\tNew end of data_var(Data Segment):%p\n",&data_var1);
printf("____________________________\n");
printf("BSS Location:\n");
printf("\tAddress of bss_var:%p\n",&bss_var);
printf("____________________________\n");
char *b = sbrk((ptrdiff_t)0);
printf("Heap Location:\n");
printf("\tInitial end of heap:%p\n",b);
brk(b+4);
b=sbrk((ptrdiff_t)0);
printf("\tNew end of heap:%p\n",b);
return 0;
}
打印结果
below are addresses of types of process's mem
Text location:
Address of main(Code Segment):0x8048388
____________________________
Stack Location:
Initial end of stack:0xbffffab4
new end of stack:0xbffffab0
____________________________
Data Location:
Address of data_var(Data Segment):0x8049758
New end of data_var(Data Segment):0x804975c
____________________________
BSS Location:
Address of bss_var:0x8049864
____________________________
Heap Location:
Initial end of heap:0x8049868
New end of heap:0x804986c
段 | 输出 |
---|---|
代码段 | main |
data | 全局变量(已赋值):int data_var0=1; 或 static(已赋值):static int data_var1=4; |
bss | 全局变量(未赋值) |
堆 | 初始:char *b = sbrk((ptrdiff_t)0); 增加:brk(b+4); b=sbrk((ptrdiff_t)0); |
栈 | Main内:int stack_var0=2; |
进程
fork – 创建一个新进程
exec – 用一个新进程去执行一个命令行
exit – 正常退出进程
abort – 非正常退出一个进程
kill – 杀死进程或向一个进程发送信号(kill -s 9 [pid])
wait – 等待子进程结束
sleep – 将当前进程休眠一段时间
getpid – 取得进程编号
getppid – 取得父进程编号
fork()
在语句pid=fork()之前,只有一个进程在执行,但在这条语句之后,变成两个进程在执行,这两个进程的代码部分完全相同。原先就存在是父进程,新出现的叫子进程。
父子进程pid不同,返回值也不同。调用一次,返回两次
fork()返回值
在父进程中,fork返回新创建子进程的pid;
在子进程中,fork返回0;
如果出现错误,fork返回一个负值;(进程数达系统上限/内存不足)
#include<sys/types.h>
#include<unistd.h>
int main(){
pid_t pid;
pid = fork();
if(pid<0){
printf("error");
}else if(pid==0){
printf("son");
}else{
printf("parent");
}
}
进程间六种通信方式
管道(pipe)和有名管道( named pipe )
管道可用于具有亲缘关系进程间的通信;
有名管道具有管道所具有的功能外,它还允许无亲缘关系进程间的通信。
信号(signal)
信号是在软件层次上对中断机制的一种模拟,它是比较复杂的通信方式,用于通知进程有某事件发生,一个进程收到一个信号与处理器收到一个中断请求效果上可以说是一样的
消息队列
具有写权限的进程可以按照一定的规则向消息队列中添加新消息;对消息队列有读权限的进程则可以从消息队列中读取消息。消息队列克服了信号承载信息量少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。
共享内存(Shared memory)
多个进程可以访问同一块内存空间,不同进程可以及时看到对方进程中对共享内存中数据的更新。这种通信方式需要依靠某种同步机制,如互斥锁和信号量等。
信号量(Semaphore)
主要作为进程之间以及同一进程的不同线程之间的同步和互斥手段。
套接字(Socket)
更为一般的进程间通信机制,它可用于网络中不同机器之间的进程间通信,应用非常广泛。
匿名管道
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(){
int fd[2];
pid_t pid;
char buf[64]="parent!\n";//父进程要写入管道的信息
char line[64];
//创建管道并检查结果
if(0!=pipe(fd)){printf("管道创建失败");return 0;}
pid = fork();
if(pid<0){printf("子进程创建失败");return 0;}
else if(pid>0){ //父进程
close(fd[0]); //关闭读管道,父进程只能向管道写入数据
write(fd[1],buf,strlen(buf)); //写数据到管道
close(fd[1]); //关闭写管道
}
else{
close(fd[1]); //关闭写管道,子进程只能向管道读取数据
read(fd[0],line,64); //从管道读取数据
printf("%s",line);
close(fd[0]); //关闭读管道
}
}
消息队列
创建和打开
int msgget (key_t key, int flag)
key:返回新的或已有队列的ID,IPC_PRIVATE
添加消息
int msgsnd (int msqid, struct msgbuf *msgp, size_t msgsz, int flag)
其中:msqid是消息队列的队列ID;
msgp是消息内容所在的缓冲区;
msgsz是消息的大小;
msgflg是标志,IPC_NOWAIT若消息并没有立交发送而调用进程会立即返回
读取消息
int msgrcv (int msqid, struct msgbuf *msgp, size_t msgsz,long msgtyp, int flag)
msqid是消息队列的引用标识符;
msgp是接收到的消息将要存放的缓冲区;
msgsz是消息的大小;
msgtyp是期望接收的消息类型;
msgflg是标志
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/types.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#define MSGKEY 75
struct msgform{
long mtype; //消息类型
char mtext[250];//消息内容
}msg;
int msgqid,pid,*pint,i;
void client(){
printf( "client with pid %d, parent pid %d \n", getpid(), getppid());
msgqid=msgget(MSGKEY, 0777);//打开75#消息队列
pid = getpid(); //获取client的进程ID
pint = (int *)msg.mtext; //把正文的内容传给pint,并强制转换类型
*pint = pid; //消息内容为client的进程ID
msg.mtype = 1; //消息类型为1
msgsnd(msgqid,&msg,sizeof(int),0); //发送消息(往消息队列添加消息)
msgrcv(msgqid,&msg,250,pid,0); //接收(读取)消息
printf("(client): receive reply from pid=%d\n",*pint); //显示server进程标识数
exit(0);
}
void server(){
printf("server with pid %d, parent pid %d. \n", getpid(), getppid());
msgqid = msgget(MSGKEY,0777|IPC_CREAT); //创建75#消息队列
msgrcv(msgqid,&msg,250,1,0); //接收(读取)消息
pint = (int *)msg.mtext; //把正文的内容传给pint,并强制转换类型
pid = *pint; //获得ci1en进程标识数
printf("(server): serving for client pid=%d\n", pid);
msg.mtype = pid; //消息类型为c1iet进程标识
*pint = getpid(); //获取server进程标识数,消息内容为server的进程ID
msgsnd(msgqid,&msg,sizeof(int),0); //发送消息
exit(0);
}
int main(){
while((i=fork())==-1); //创建子进程1
if(!i){
server(); //子进程1作为服务器
}
while((i=fork())==-1); //创建子进程2
if(!i){
client(); //子进程2作为客户机
}
wait(0); //主进程等待子进程结束
wait(0); //主进程等待子进程结束
}
socket
server
ServerSocket server=new ServerSocket(8000);
Socket socket=server.accept();
BufferedReader br=new BufferedReader(new InputStreamReader(socket.getInputStream()));
PrintWriter pw=new PrintWriter(socket.getOutputStream(),true);
String s=br.readLine();
pw.println(s);
br.close();
pw.close();
client
Socket socket=new Socket("localhost",2000);
BufferedReader br=new BufferedReader(new InputStreamReader(socket.getInputStream()));
PrintWriter pw=new PrintWriter(socket.getOutputStream(),true);
pw.println("hello2");
System.out.println(br.readLine());
内存分配算法
位图:内存被划分成分配单元,分配单元对应于位图中的一位,0表示空闲,1表示占用。
占有K个分配单元的进程调入内存时,存储管理器搜索位图,在位图中找出有K个连续0的内存分配单元。
链表:维护一个记录已分配和空闲内存段的链表,链表中的一个结点为一个进程,或者是两个进程间的空闲区。表中的每一项都指定了如下内容:空闲区(H)还是进程§、起始地址、长度以及指向下一项的指针。当有新进程或换入时,有以下四种算法
首次适应算法FirstFit——空闲链表按地址递增的次序链接,优先分配内存中低址部分的空闲区。可保留高址大空闲区。
循环适应算法NextFit——从上次找到的空闲分区的下一个空闲分区开始查找。应设置一起始查询指针。该算法使空闲分区分布得更均匀。
最佳适应算法BestFit——空闲链表按分区大小递增的次序链接,优先分配内存中满足条件的最小的空闲区。
最坏适应算法WorstFit——空闲链表按分区大小递减的次序链接,优先分配内存中最大的空闲区。