当前位置:文档之家› 系统调用例子

系统调用例子

系统调用例子
系统调用例子

LINUX系统调用函数

1、开启子进程

fork

fork()函数,Linux系统调用

头文件:

#include

函数定义:

int fork( void );

返回值:

子进程中返回0,父进程中返回子进程ID,出错返回-1

函数说明:

一个现有进程可以调用fork函数创建一个新进程。由fork创建的新进程被称为子进程(child process)。fork函数被调用一次但返回两次。两次返回的唯一区别是子进程中返回0值而父进程中返回子进程ID。

子进程是父进程的副本,它将获得父进程数据空间、堆、栈等资源的副本。注意,子进程持有的是上述存储空间的“副本”,这意味着父子进程间不共享这些存储空间,它们之间共享的存储空间只有代码段。

示例代码:

#include

#include

int main(int argc, void ** argv )

{

int pid = fork();

if(pid == -1 ) {

// print("error!");

} else if( pid = =0 ) {

// print("This is the child process!");

} else {

// print("This is the parent process! child process id = %d", pid);

}

return 0;

}

Fork()系统在Linux中的返回值是没有NULL的.

Error Codes

出错返回错误信息如下:

EAGAIN

达到进程数上限.

ENOMEM

没有足够空间给一个新进程分配.

EPROCLIM

2、复制

dup2

函数名: dup2

功能: 复制文件句柄

用法: int dup2(int oldhandle, int newhandle);

程序例:

#include

#include

#include

#include

int main(void)

{

#define STDOUT 1

int nul, oldstdout;

char msg[] = "This is a test";

/* create a file */

nul = open("DUMMY.FIL", O_CREAT | O_RDWR, S_IREAD | S_IWRITE);

/* create a duplicate handle for standard

output */

oldstdout = dup(STDOUT);

/*

redirect standard output to DUMMY.FIL

by duplicating the file handle onto the

file handle for standard output.

*/

dup2(nul, STDOUT);

/* close the handle for DUMMY.FIL */

close(nul);

/* will be redirected into DUMMY.FIL */

write(STDOUT, msg, strlen(msg));

/* restore original standard output

handle */

dup2(oldstdout, STDOUT);

/* close duplicate handle for STDOUT */

close(oldstdout);

return 0;

}

3、读取进程的结果

popen

[编辑本段]

Linux C

进程I/O函数,与pclose函数一起使用。

表头文件

#include

函数定义

FILE * popen ( const char * command , const char * type );

int pclose ( FILE * stream );

函数说明

popen() 函数通过创建一个管道,调用fork 产生一个子进程,执行一个shell 以运行命令来开启一个进程。这个进程必须由pclose() 函数关闭,而不是fclose() 函数。pclose() 函数关闭标准I/O 流,等待命令执行结束,然后返回shell 的终止状态。如果shell 不能被执行,则pclose() 返回的终止状态与shell 已执行exit 一样。

type 参数只能是读或者写中的一种,得到的返回值(标准I/O 流)也具有和type 相应的只读或只写类型。如果type 是"r" 则文件指针连接到command 的标准输出;如果type 是"w" 则文件指针连接到command 的标准输入。

command参数是一个指向以NULL 结束的shell 命令字符串的指针。这行命令将被传到bin/sh 并使用-c 标志,shell 将执行这个命令。

popen的返回值是个标准I/O 流,必须由pclose来终止。前面提到这个流是单向的。所以向这个流写内容相当于写入该命令的标准输入;命令的标准输出和调用popen的进程相同。与之相反的,从流中读数据相当于读取命令的标准输出;命令的标准输入和调用popen 的进程相同。

返回值

如果调用fork() 或pipe() 失败,或者不能分配内存将返回NULL,否则返回标准I/O 流。

返回错误

popen没有为内存分配失败设置errno 值。

如果调用fork() 或pipe() 时出现错误,errno 被设为相应的错误类型。

如果type 参数不合法,errno将返回EINVAL。

使用举例

if((fp=popen("/usr/bin/uptime","r"))==NULL);

{

sprintf(buf,"error: %s\n", strerror(errno));

....//异常处理

}

else

{

....

pclose(fp);

}

4、创建管道

pipe

所需头文件#include

函数原型 int pipe(int fd[2])

函数传入值fd[2]:管道的两个文件描述符,之后就是可以直接操作者两个文件描述符

返回值成功 0

失败-1

管道是Linux 支持的最初Unix IPC形式之一,具有以下特点:

管道是半双工的,数据只能向一个方向流动;需要双方通信时,需要建立起两个管道;只能用于父子进程或者兄弟进程之间(具有亲缘关系的进程);单独构成一种独立的文件系统:管道对于管道两端的进程而言,就是一个文件,但它不是普通的文件,它不属于某种文件系统,而是自立门户,单独构成一种文件系统,并且只存在与内存中。数据的读出和写入:一个进程向管道中写的内容被管道另一端的进程读出。写入的内容每次都添加在管道缓冲区的末尾,并且每次都是从缓冲区的头部读出数据。

1.2管道的创建:

#include

int pipe(int fd[2])

该函数创建的管道的两端处于一个进程中间,在实际应用中没有太大意义,因此,一个进程在由pipe()创建管道后,一般再fork一个子进程,然后通过管道实现父子进程间的通信(因此也不难推出,只要两个进程中存在亲缘关系,这里的亲缘关系指的是具有共同的祖先,都可以采用管道方式来进行通信)。

1.3管道的读写规则:

管道两端可分别用描述字fd[0]以及fd[1]来描述,需要注意的是,管道的两端是固定了任务的。即一端只能用于读,由描述字fd[0]表示,称其为管道读端;另一端则只能用于写,由描述字fd[1]来表示,称其为管道写端。如果试图从管道写端读取数据,或者向管道读端写入数据都将导致错误发生。一般文件的I/O函数都可以用于管道,如close、read、write等等。

从管道中读取数据:

如果管道的写端不存在,则认为已经读到了数据的末尾,读函数返回的读出字节数为0;当管道的写端存在时,如果请求的字节数目大于PIPE_BUF,则返回管道中现有的数据字节数,如果请求的字节数目不大于PIPE_BUF,则返回管道中现有数据字节数(此时,管道中数据量小于请求的数据量);或者返回请求的字节数(此时,管道中数据量不小于请求的数据量)。注:(PIPE_BUF在include/linux/limits.h中定义,不同的内核版本可能会有所不同。Posix.1要求PIPE_BUF至少为512字节,red hat 7.2中为4096)。

关于管道的读规则验证:

/**************

* readtest.c *

**************/

#include

#include

#include

main()

{

int pipe_fd[2];

pid_t pid;

char r_buf[100];

char w_buf[4];

char* p_wbuf;

int r_num;

int cmd;

memset(r_buf,0,sizeof(r_buf));

memset(w_buf,0,sizeof(r_buf));

p_wbuf=w_buf;

if(pipe(pipe_fd)<0)

{

printf("pipe create error ");

return -1;

}

if((pid=fork())==0)

{

printf(" ");

close(pipe_fd[1]);

sleep(3);//确保父进程关闭写端

r_num=read(pipe_fd[0],r_buf,100);

printf( "read num is %d the data read from the pipe is %d ",r_num,atoi(r_buf));

close(pipe_fd[0]);

exit();

}

else if(pid>0)

{

close(pipe_fd[0]);//read

strcpy(w_buf,"111");

if(write(pipe_fd[1],w_buf,4)!=-1)

printf("parent write over ");

close(pipe_fd[1]);//write

printf("parent close fd[1] over ");

sleep(10);

}

}

/**************************************************

* 程序输出结果:

* parent write over

* parent close fd[1] over

* read num is 4 the data read from the pipe is 111

* 附加结论:

* 管道写端关闭后,写入的数据将一直存在,直到读出为止.

****************************************************/

向管道中写入数据:

向管道中写入数据时,linux将不保证写入的原子性,管道缓冲区一有空闲区域,写进程就会试图向管道写入数据。如果读进程不读走管道缓冲区中的数据,那么写操作将一直阻塞。

注:只有在管道的读端存在时,向管道中写入数据才有意义。否则,向管道中写入数据的进程将收到内核传来的SIFPIPE信号,应用程序可以处理该信号,也可以忽略(默认动作则是应用程序终止)。

对管道的写规则的验证1:写端对读端存在的依赖性

#include

#include

main()

{

int pipe_fd[2];

pid_t pid;

char r_buf[4];

char* w_buf;

int writenum;

int cmd;

memset(r_buf,0,sizeof(r_buf));

if(pipe(pipe_fd)<0)

{

printf("pipe create error ");

return -1;

}

if((pid=fork())==0)

{

close(pipe_fd[0]);

close(pipe_fd[1]);

sleep(10);

exit();

}

else if(pid>0)

{

sleep(1); //等待子进程完成关闭读端的操作

close(pipe_fd[0]);//write

w_buf="111";

if((writenum=write(pipe_fd[1],w_buf,4))==-1)

printf("write to pipe error ");

else

printf("the bytes write to pipe is %d ", writenum);

close(pipe_fd[1]);

}

}

则输出结果为:Broken pipe,原因就是该管道以及它的所有fork()产物的读端都已经被关闭。如果在父进程中保留读端,即在写完pipe后,再关闭父进程的读端,也会正常写入pipe,读者可自己验证一下该结论。因此,在向管道写入数据时,至少应该存在某一个进程,其中管道读端没有被关闭,否则就会出现上述错误(管道断裂,进程收到了SIGPIPE信号,默认动作是进程终止)

对管道的写规则的验证2:linux不保证写管道的原子性验证#include

#include

#include

main(int argc,char**argv)

{

int pipe_fd[2];

pid_t pid;

char r_buf[4096];

char w_buf[4096*2];

int writenum;

int rnum;

memset(r_buf,0,sizeof(r_buf));

if(pipe(pipe_fd)<0)

{

printf("pipe create error ");

return -1;

}

if((pid=fork())==0)

{

close(pipe_fd[1]);

while(1)

{

sleep(1);

rnum=read(pipe_fd[0],r_buf,1000);

printf("child: readnum is %d ",rnum);

}

close(pipe_fd[0]);

exit();

}

else if(pid>0)

{

close(pipe_fd[0]);//write

memset(r_buf,0,sizeof(r_buf));

if((writenum=write(pipe_fd[1],w_buf,1024))==-1) printf("write to pipe error ");

else

printf("the bytes write to pipe is %d ", writenum); writenum=write(pipe_fd[1],w_buf,4096);

close(pipe_fd[1]);

}

}

输出结果:

the bytes write to pipe 1000

the bytes write to pipe 1000 //注意,此行输出说明了写入的非原子性

the bytes write to pipe 1000

the bytes write to pipe 1000

the bytes write to pipe 1000

the bytes write to pipe 120 //注意,此行输出说明了写入的非原子性

the bytes write to pipe 0

the bytes write to pipe 0

......

结论:

写入数目小于4096时写入是非原子的!

如果把父进程中的两次写入字节数都改为5000,则很容易得出下面结论:

写入管道的数据量大于4096字节时,缓冲区的空闲空间将被写入数据(补齐),直到写完所有数据为止,如果没有进程读数据,则一直阻塞。

1.4管道应用实例:

实例一:用于shell

管道可用于输入输出重定向,它将一个命令的输出直接定向到另一个命令的输入。比如,当在某个shell程序(Bourne shell或C shell等)键入who│wc -l后,相应shell程序将创建who以及wc两个进程和这两个进程间的管道。考虑下面的命令行:

$kill -l 运行结果见附一。

$kill -l | grep SIGRTMIN 运行结果如下:

30) SIGPWR 31) SIGSYS 32) SIGRTMIN 33) SIGRTMIN+1

34) SIGRTMIN+2 35) SIGRTMIN+3 36) SIGRTMIN+4 37) SIGRTMIN+5

38) SIGRTMIN+6 39) SIGRTMIN+7 40) SIGRTMIN+8 41) SIGRTMIN+9

42) SIGRTMIN+10 43) SIGRTMIN+11 44) SIGRTMIN+12 45) SIGRTMIN+13

46) SIGRTMIN+14 47) SIGRTMIN+15 48) SIGRTMAX-15 49) SIGRTMAX-14

实例二:用于具有亲缘关系的进程间通信

下面例子给出了管道的具体应用,父进程通过管道发送一些命令给子进程,子进程解析命令,并根据命令作相应处理。

#include

#include

main()

{

int pipe_fd[2];

pid_t pid;

char r_buf[4];

char** w_buf[256];

int childexit=0;

int i;

int cmd;

memset(r_buf,0,sizeof(r_buf));

if(pipe(pipe_fd)<0)

{

printf("pipe create error ");

return -1;

if((pid=fork())==0)

//子进程:解析从管道中获取的命令,并作相应的处理

{

printf(" ");

close(pipe_fd[1]);

sleep(2);

while(!childexit)

{

read(pipe_fd[0],r_buf,4);

cmd=atoi(r_buf);

if(cmd==0)

{

printf("child: receive command from parent over now child process exit "); childexit=1;

}

else if(handle_cmd(cmd)!=0)

return;

sleep(1);

}

close(pipe_fd[0]);

exit();

}

else if(pid>0)

//parent: send commands to child

{

close(pipe_fd[0]);

w_buf[0]="003";

w_buf[1]="005";

w_buf[2]="777";

w_buf[3]="000";

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

write(pipe_fd[1],w_buf[i],4);

close(pipe_fd[1]);

}

}

//下面是子进程的命令处理函数(特定于应用):

int handle_cmd(int cmd)

{

if((cmd<0)||(cmd>256))

//suppose child only support 256 commands

{

printf("child: invalid command ");

return -1;

printf("child: the cmd from parent is %d ", cmd);

return 0;

}

1.5管道的局限性

管道的主要局限性正体现在它的特点上:

只支持单向数据流;只能用于具有亲缘关系的进程之间;没有名字;管道的缓冲区是有限的(管道制存在于内存中,在管道创建时,为缓冲区分配一个页面大小);

管道所传送的是无格式字节流,这就要求管道的读出方和写入方必须事先约定好数据的格式,比如多少字节算作一个消息(或命令、或记录)等等;

Linux管道的实现机制

在Linux中,管道是一种使用非常频繁的通信机制。从本质上说,管道也是一种文件,但它又和一般的文件有所不同,管道可以克服使用文件进行通信的两个问题,具体表现为:限制管道的大小。实际上,管道是一个固定大小的缓冲区。在Linux中,该缓冲区的大小为1页,即4K字节,使得它的大小不象文件那样不加检验地增长。使用单个固定缓冲区也会带来问题,比如在写管道时可能变满,当这种情况发生时,随后对管道的write()调用将默认地被阻塞,等待某些数据被读取,以便腾出足够的空间供write()调用写。

读取进程也可能工作得比写进程快。当所有当前进程数据已被读取时,管道变空。当这种情况发生时,一个随后的read()调用将默认地被阻塞,等待某些数据被写入,这解决了read()调用返回文件结束的问题。

注意:从管道读数据是一次性操作,数据一旦被读,它就从管道中被抛弃,释放空间以便写更多的数据。

1. 管道的结构

在Linux 中,管道的实现并没有使用专门的数据结构,而是借助了文件系统的file结构和VFS的索引节点inode。通过将两个file 结构指向同一个临时的VFS 索引节点,而这个VFS 索引节点又指向一个物理页面而实现的。如图7.1所示。

图7.1 管道结构示意图

图7.1中有两个file 数据结构,但它们定义文件操作例程地址是不同的,其中一个是向管道中写入数据的例程地址,而另一个是从管道中读出数据的例程地址。这样,用户程序的系统调用仍然是通常的文件操作,而内核却利用这种抽象机制实现了管道这一特殊操作。

2.管道的读写

管道实现的源代码在fs/pipe.c中,在pipe.c中有很多函数,其中有两个函数比较重要,即管道读函数pipe_read()和管道写函数pipe_wrtie()。管道写函数通过将字节复制到VFS 索引节点指向的物理内存而写入数据,而管道读函数则通过复制物理内存中的字节而读出数据。当然,内核必须利用一定的机制同步对管道的访问,为此,内核使用了锁、等待队列和信号。

当写进程向管道中写入时,它利用标准的库函数write(),系统根据库函数传递的文件描述符,可找到该文件的file 结构。file 结构中指定了用来进行写操作的函数(即写入函数)地址,于是,内核调用该函数完成写操作。写入函数在向内存中写入数据之前,必须首先检查VFS 索引节点中的信息,同时满足如下条件时,才能进行实际的内存复制工作:

内存中有足够的空间可容纳所有要写入的数据;

内存没有被读程序锁定。

如果同时满足上述条件,写入函数首先锁定内存,然后从写进程的地址空间中复制数据到内存。否则,写入进程就休眠在VFS 索引节点的等待队列中,接下来,内核将调用调度程序,而调度程序会选择其他进程运行。写入进程实际处于可中断的等待状态,当内存中有足够的空间可

以容纳写入数据,或内存被解锁时,读取进程会唤醒写入进程,这时,写入进程将接收到信号。当数据写入内存之后,内存被解锁,而所有休眠在索引节点的读取进程会被唤醒。

管道的读取过程和写入过程类似。但是,进程可以在没有数据或内存被锁定时立即返回错误信息,而不是阻塞该进程,这依赖于文件或管道的打开模式。反之,进程可以休眠在索引节点的等待队列中等待写入进程写入数据。当所有的进程完成了管道操作之后,管道的索引节点被丢弃,而共享数据页也被释放。

因为管道的实现涉及很多文件的操作,因此,当读者学完有关文件系统的内容后来读pipe.c 中的代码,你会觉得并不难理解。

5、建立fifo

mkfifo

mkfifo函数使用

[code]mkfifo(建立实名管道)

相关函数

pipe,popen,open,umask

表头文件

#include

#include

定义函数

int mkfifo(const char * pathname,mode_t mode);

函数说明

mkfifo ()会依参数pathname建立特殊的FIFO文件,该文件必须不存在,而参数mode 为该文件的权限(mode%~umask),因此umask值也会影响到FIFO文件的权限。Mkfifo()建立的FIFO文件其他进程都可以用读写一般文件的方式存取。当使用open()来打开FIFO文件时,O_NONBLOCK旗标会有影响

1、当使用O_NONBLOCK 旗标时,打开FIFO 文件来读取的操作会立刻返回,但是若还没有其他进程打开FIFO 文件来读取,则写入的操作会返回ENXIO 错误代码。

2、没有使用O_NONBLOCK 旗标时,打开FIFO 来读取的操作会等到其他进程打开FIFO 文件来写入才正常返回。同样地,打开FIFO文件来写入的操作会等到其他进程打开FIFO 文件来读取后才正常返回。

返回值

若成功则返回0,否则返回-1,错误原因存于errno中。

错误代码

EACCESS 参数pathname所指定的目录路径无可执行的权限

EEXIST 参数pathname所指定的文件已存在。

ENAMETOOLONG 参数pathname的路径名称太长。

ENOENT 参数pathname包含的目录不存在

ENOSPC 文件系统的剩余空间不足

ENOTDIR 参数pathname路径中的目录存在但却非真正的目录。

EROFS 参数pathname指定的文件存在于只读文件系统内。

示例1:

#include

#include

#include

#include

int main(void)

{

char buf[80];

int fd;

unlink( "zieckey_fifo" );

mkfifo( "zieckey_fifo", 0777 );

if ( fork() > 0 )

{

char s[] = "Hello!\n";

fd = open( "zieckey_fifo", O_WRONLY );

write( fd, s, sizeof(s) );

//close( fd );

}

else

{

fd = open( "zieckey_fifo", O_RDONLY );

read( fd, buf, sizeof(buf) );

printf("The message from the pipe is:%s\n", buf ); //close( fd );

}

return 0;

}

执行

hello!

示例2:

#include

#include

#include

#include

#include

int main( int argc, char **argv )

{

mode_t mode = 0666;

if ( argc !=2 )

{

printf( "Usage:[%s] fifo_filename\n", argv[0] ); return -1;

}

if (mkfifo( argv[1], mode)<0 )

{

perror( "mkfifo");

return -1;

}

return 0;

} [/code]

6、获得key_t

ftok

ftok函数

系统建立IPC通讯(如消息队列、共享内存时)必须指定一个ID值。通常情况下,该id 值通过ftok函数得到。

ftok原型如下:

key_t ftok( char * fname, int id )

fname就时你指定的文件名,id是子序号。

在一般的UNIX实现中,是将文件的索引节点号取出,前面加上子序号得到key_t的返回值。

如指定文件的索引节点号为65538,换算成16进制为0x010002,而你指定的ID值为38,换算成16进制为0x26,则最后的key_t返回值为0x26010002。

查询文件索引节点号的方法是:ls -i

当删除重建文件后,索引节点号由操作系统根据当时文件系统的使用情况分配,因此与原来不同,所以得到的索引节点号也不同。

如果要确保key_t值不变,要目确保ftok的文件不被删除,要么不用ftok,指定一个固定的key_t值,比如:

#define IPCKEY 0x111

char path[256];

sprintf( path, "%s/etc/config.ini", (char*)getenv("HOME") );

msgid=ftok( path, IPCKEY );[/code]

同一段程序,用于保证两个不同用户下的两组相同程序获得互不干扰的IPC键值。

由于etc/config.ini(假定)为应用系统的关键配置文件,因此不存在被轻易删除的问题——即使被删,也会很快被发现并重建(此时应用系统也将被重起)。

ftok()的设计目的也在于此。

下面是一段具体的代码例子:

key_t keyShareMem ;

if((keyShareMem = ftok(AFC_SHARE_MEMORY_NAME.c_str(), 0)) == -1) { cerr << "ERROR:"<< m_nThisThreadType<<" CBaseMessageDeal() keyShareMem ftok: " << errno << ":" << strerror(errno)<

throw new AfcInitAfcResourceException("CBaseMessageDeal::CBaseMessageDeal ftok keyShareMem") ;

}

if ( (m_shmID = shmget(keyShareMem, 0, AFC_SHM_RW)) < 0) {

cerr << "ERROR:"<< m_nThisThreadType<<" CBaseMessageDeal() shmget exist: "<

throw new AfcInitAfcResourceException("CBaseMessageDeal::CBaseMessageDeal() shmget exist") ;

}

if((m_afcShareMemoryBegin=(char *)shmat(m_shmID, NULL, 0)) == (void *) -1){

cerr << "ERROR:"<< m_nThisThreadType<<" CBaseMessageDeal() shmat: " << errno << ":" << strerror(errno) << endl ;

throw new AfcInitAfcResourceException("CBaseMessageDeal::CBaseMessageDeal shmat") ;

}

read系统调用流程

Read 系统调用在用户空间中的处理过程 Linux 系统调用(SCI,system call interface)的实现机制实际上是一个多路汇聚以及分解的过程,该汇聚点就是 0x80 中断这个入口点(X86 系统结构)。也就是说,所有系统调用都从用户空间中汇聚到 0x80 中断点,同时保存具体的系统调用号。当 0x80 中断处理程序运行时,将根据系统调用号对不同的系统调用分别处理(调用不同的内核函数处理)。系统调用的更多内容,请参见参考资料。 Read 系统调用也不例外,当调用发生时,库函数在保存 read 系统调用号以及参数后,陷入 0x80 中断。这时库函数工作结束。Read 系统调用在用户空间中的处理也就完成了。 回页首 Read 系统调用在核心空间中的处理过程 0x80 中断处理程序接管执行后,先检察其系统调用号,然后根据系统调用号查找系统调用表,并从系统调用表中得到处理 read 系统调用的内核函数 sys_read ,最后传递参数并运行 sys_read 函数。至此,内核真正开始处理 read 系统调用(sys_read 是 read 系统调用的内核入口)。 在讲解 read 系统调用在核心空间中的处理部分中,首先介绍了内核处理磁盘请求的层次模型,然后再按该层次模型从上到下的顺序依次介绍磁盘读请求在各层的处理过程。 Read 系统调用在核心空间中处理的层次模型 图1显示了 read 系统调用在核心空间中所要经历的层次模型。从图中看出:对于磁盘的一次读请求,首先经过虚拟文件系统层(vfs layer),其次是具体的文件系统层(例如 ext2),接下来是 cache 层(page cache 层)、通用块层(generic block layer)、IO 调度层(I/O scheduler layer)、块设备驱动层(block device driver layer),最后是物理块设备层(block device layer)

linux实验_添加系统调用-完整版

实验一添加一个新的系统调用 一、实验目的 理解操作系统内核与应用程序的接口关系;加深对内核空间和用户空间的理解;学会增加新的系统调用。 二、实验内容与要求 首先增加一个系统调用函数,然后连接新的系统调用,重建新的Linux内核,用新的内核启动系统,使用新的系统调用(2.4内核和2.6内核任选一个) 三、实验指导(2.6版本) ⑴获得源代码(本次实验的内核版本是2.6.22.5,必须是root用户) 1.从教育在线上下载内核源代码到本地磁盘;保存在/usr/src目录下 2.进入终端,输入命令cd /usr/src 进入/usr/src目录(可以输入ls命令会发现目录下有一个名为LINUX_2_6_22_5.TAR.BZ2的压缩文件) 3.当前目录下(/usr/src)输入命令tar –xjvf LINUX_2_6_22_5.TAR.BZ2 解压缩源代码,命令执行完毕后,会出现/usr/src/linux-2.6.22.5文件夹 4.修改文件夹下的3个文件 第一,编辑/usr/src/linux-版本号/kernel/sys.c文件,添加函数: asmlinkage long sys_mycall(long number) { printk(“call number is %d\n”,number); return number; } 第二,修改/usr/src/linux-版本/include/asm-i386/unistd.h 添加一行#define __NR_mycall 324 到当前的最大系统调用号之后,比如原来最大的是323,在323的这一行之后加上一行#define __NR_mycall 324 修改#define NR_systemcalls 的值,改成原来的值+1,比如原来是324 改成325 第三,编辑/usr/src/linux-版本/arch/i386/kernel/syscall_table.S,在文件最后加上一行:.long sys_mycall 5.重新编译内核 在终端输入命令,进入源代码文件夹,cd /usr/src/linux-2.6.22.5 依次执行如下命令:make mrproper make clean make xconfig (自己配置内核,出现图形对话框后,直接点保存,关闭) make (耗时最长,大约20分钟) make modules_install (安装模块) 以上命令执行完毕后,会在当前目录下生成一个名为System.map的文件,会在/usr/src/linux-版本号/arch/i386/boot/下生成一个bzImage文件。

操作系统实验一 系统调用

huixing操作系统实验一 姓名:廖桉冬学号:09012431 日期:15/3/27 实验内容: 使用系统调用,用C或C++写一个程序,实现如下功能:从一个文件中读出数据,写入另一个文件中。 实验要求: 具有良好的交互性 使用者可输入源文件和目的文件的路径和文件名。 具有完善的错误处理机制 针对可能出现的各种错误,要有相应的错误提示输出,并作相应处理。 在Windows和Linux操作系统上调试并运行 实验目的: 通过实验,加深对系统调用概念的理解,了解其实现机制以及使用方式。 通过在Linux操作系统上编写和调试简单程序,进一步熟悉Linux操作系统的使用,初步掌握linux环境下的C或C++编译和调试工具,为进一步理解和学习Linux操作系统的内核结构和核心机制作准备。 设计思路和流程图 读入源文件/目标文件名-->打开文件流-(打开是否正常)->将源文件字符流读出暂存-->将字符流输出到目标文件; 主要数据结构及其说明 string:暂存文件名 ifstream/ofstream:文件流输入输出 a:字符暂存 源程序并附上注释 #include #include #include using namespace std; int main() { //读取文件名 string infile; string outfile; cout<<"输入你想要读取的文件名/(路径) :"<

cin>>infile; cout<<"输入你想要写入的文件名/(路径) :"<>outfile; //打开文件流 ifstream f_in(infile.c_str()); ofstream f_out(outfile.c_str(),ios::app); if(!f_in) { cout<<"源文件不存在."<>a; f_out<

操作系统试题与答案

一、填空题(20分,每空1分) 1、操作系统设计得两个目标就是易用与高效。 2、P、V操作必须成对出现,有一个P操作就一定有一个V操作。 3、临界资源就是指系统中一次只允许一个进程使用得资源,而临界区就是指涉及到临界资源得代码段。 4、在请求式分页系统中,页框得分配有一种方式称为固定分配,固定分配有两种不同得方式,分别就是平均分配与按比率分配。 5、在请求式分页存储管理系统中,不能在计算机中实现得页面淘汰算法就是最佳算法,选择淘汰不再使用或最远得将来才使用得页得算法就是先进先出算法,选择淘汰在主存驻留时间最长得页得算法就是最近最少使用. 6、文件得结构就就是文件得组织形式,从用户观点出发所瞧到得文件组织形式称为文件得逻辑结构;从实现观点出发,文件在外存上得存放组织形式称为文件得物理结构. 7、文件得目录组织形式主要有单级目录、二级目录、树型目录与图型目录等. 8、设备得寻址方式主要有直接I/O指令与存储器映射I/O指令. 9、协同进程间一般通过信箱进行间接通信。 二、选择题(20分,每题2分) 1、紧耦合系统就就是 4 。 (1)分时操作系统(2)分布式操作系统 (3)网络操作系统(4)并行操作系统 2、以下不属于操作系统部件得就是2。 (1)进程管理(2)数据库管理 (3)保护系统(4)命令解释器系统 3、如P与V操作得信号量S初值为4,则现在S=-1,表示有 1 个进程在等待。(1)1(2)2 (3) 3 (4)5 4、用V操作可以唤醒一个进程,被唤醒得进程状态变为1。 (1)就绪(2)运行(3)阻塞(4)完成 5、所有就绪状态得进程按建立得先后顺序形成一个对列,从队列首挑选一个进程,分给时间片q,投入运行.当时间片到时,而又没有完成得进程,将再次加入到队列尾,排队等待下一轮调度。这种进程调度算法称为2。 (1)循环轮转调度算法 (2)优先数调度算法 (3)固定周期轮转调度算法 (4)多级队列调度算法 6、页式存储管理得快表一般存放在4。 (1)内存(2)外存(3)硬盘(4)CACHE 7、虚拟存储器得最大容量由 2 决定。 (1)内存容量 (2) 程序得地址空间 (3)内外存容量 (4)计算机得地址机构 8、可以分配给多个进程得设备就是 1 。 (1)共享设备(2)块设备 (3)独占设备(4)互斥设备 9、光盘上得文件一般可以采用3存取方式.

操作系统答案及评分标准

东莞理工学院(本科)2008-2009第一学期 操作系统(A卷)答案及评分标准 一、术语解释(共20 分每题2分) 进程;进程是程序在一个数据集合上运行的过程,它是系统进行资源分配和调度的一个独立单位;(2分) 中断;CPU暂停当前程序的执行,转去处理计算机内部或外部发生的突发事件,完成突发事件处理后回到原来程序执行的过程;(2分) 重定位;从程序的逻辑地址映射到物理地址的过程;(2分) 页面抖动;刚调进内存的页面不久又被调出,而调出后不久又被调入,CPU大量的时间花费的页面调入调出的现象;(2分) 文件的物理组织;文件在存储设备上的组织结构;(2分) 分时;将CPU的运行时间划分成若干个时间段(称为时间片),循环轮流分配给各个程序(用户)使用; (2分) 死锁;在一个进程集合中的每个进程,都在等待仅由该集合中的另一进程才能引发的事件,而无限期地僵持下去的局面; (2分) 吞吐量;单位时间内完成的作业数; (2分) 进程同步;一个进程执行到某一点后要等到另一进程执行到某点才能继续往下执行的一种进程间关系;(2分) 虚拟存储器;只进程分配部分所需内存,剩下内存需求等到进程真正需要访问这些存储器时才被调入,但在用户看来却分配到了所有所需内存,这种内存管理方式成为虚拟存取器。(2分) (评分标准:因为各人表述方式不同,对每一小题,术语基本解释清楚即可得全分) 二、简答题(共30分每题5分) 1. 处理器有用户态和核心态等2种工作状态(4分),设置2种工作状态的原因是为了对操作系统内核进行保护(1分)。 2. 当CPU要访问的页面不在内存中时,就发生缺页中断(3分);发生缺页中断时,CPU转缺页中断处理程序执行,将待访问页面从外村调入内存,返回被中断程序,重新启动引起缺页的指令(2分)。 3. 高级调度是作业调度,为作业分配资源,将作业调入内存,并为之创建进程(2分);低级调度为进程调度,为进程分配处理器(2分);作业调度为进程调度做准备(1分)。 3. 死锁产生的4个必要条件是:互斥条件、占用且等待条件、循环等待条件、不可抢占条件。(答对1个得2分,2个得3分,3个得4分,4个得5分) 5. 操作系统是一种系统软件,是若干程序模块所的集合,它负责管理和控制计算机系统的硬件、软件资源的分配、调度和管理,使系统高效、安全地运行(2分);为用户提供简单、直观、灵活的用户接口和使用环境(1分);操作系统的主要功能是实现对计算机系统软硬件资源的管理(2分)。 6.引进高级通信机构的目的是为了在进程间实现大量数据的交换(4分);进程间通信机制的实例有:管道、消息通信、共享内存等(列出1种即可,1分)。 三、理解题(共20分每题5分) 1.设备分配;设备回收;设备启动;I/O操作;中断处理;缓冲区管理;(评分标准:每项1分,最多5分) 2. 进程正常结束;进程出错终止;运行时间片用完;等待I/O操作;优先级更高度进程就绪;(评分标准:每项1分,最多5分) 3. 有效地址 (1,500)的物理地址为:1024×3+500=3572; (3分) 有效地址 (2,3000)不合法;(1分) 有效地址(3,100)的页号超出页表范围,出现地址越界;(1分)

linux添加系统调用实验步骤

首先,进入到内核源码目录/usr/src/linux-2.6.34中,添加自己的系统调用号。 lyh@lyh:~$ cd /usr/src/linux-2.6.34/ 系统调用号在unistd_32.h文件中定义。内核中每个系统调用号都是 以“__NR_"开头的,在该文件中添加自己的系统调用号 lyh@lyh:/usr/src/linux-2.6.34$ sudo vim arch/x86/include/asm/unistd_32.h #define __NR_pwritev 334 #define __NR_rt_tgsigqueueinfo 335 #define __NR_perf_event_open 336 #define __NR_recvmmsg 337 #define __NR_mycall 338 #ifdef __KERNEL__ #define NR_syscalls 339 在内核源文件中该行为#define NR_syscalls 338,在系统调用执行的过程中,system_call()函数会根据该值来对用户态进程的有效性进行检查。如果这个号大于或等于NR_syscalls,系统调用处理程序终止。所以应该将原来的#define NR_syscalls 338修改为#define NR_syscalls 339 其次,在系统调用表中添加相应的表项 (1)lyh@lyh:/usr/src/linux-2.6.34$ sudo vim arch/x86/kernel/syscall_table_32.S ENTRY(sys_call_table) .long sys_restart_syscall .long sys_exit ………………(这里省略了部分) .long sys_rt_tgsigqueueinfo .long sys_perf_event_open .long sys_recvmmsg .long sys_mycall (2)lyh@lyh:/usr/src/linux-2.6.34$ sudo vim arch/h8300/kernel/syscalls.S #include #include

添加系统调用实验报告

一、构建基本的实验环境 1.1基本实验环境与前提条件 Windows7 、Word 2010、Vmware WorkStation 8.5、AdobeReader ReadHatLinux 9.0,gcc,vi Linux内核[V2.4.18] 1.2虚拟机的安装及使用 1.3将Linux 内核源代码及配置文件传送给虚拟机上的Red Hat Linux V9.0 系统 配置网络时遇到这个问题, Determining IP information for eth0... failed; no link present. Check cable? 通过查找资料发现是系统的Bug, 解决方法如下: 到/etc/sysconfig/network-scripts/ifcfg-eth0 在文件最后一行中加入 check_link_down () { return 1; } 另外如果存在/etc/sysconfig/networking/profiles/default/ifcfg-eth0 文件,则同样在其中加入这一段东西即可,然后重启系统。 设置网络为DHCP,重新启动就可以,啦,直接上图

最后将内核代码下载到/root目录下 二、Linux 内核编译、配置与调试 2.1 内核配置与编译 2.1.1、解压内核源代码文件 tar -zxf linux-2.4.18.tar.gz 2.1.2、解压后如下

2.1.3、拷贝linux,命名为linux-2.4.18 cp -r linux linux-2.4.18 2.1.4、移动config-2.4.18forMP.txt到linux-2.4.18根目录,替换掉.config 2.1.5、进入linux-2.4.18目录,配置和编译内核模块 make oldconfig make dep

linux题库填空简答及标准答案

linux题库填空简答及答案

————————————————————————————————作者:————————————————————————————————日期:

二、填空题 26. 在Linux系统中,以_文件的_方式访问设备。 27. Linux内核引导时,从文件_/etc/fstad___中读取要加载的文件系统。 28. Linux文件系统中每个文件用__i节点_____来标识。 29. 某文件的权限为:d-rw-_r--_r--,用数值形式表示该权限644,该文件属性是目录。 30. 静态路由设定后,若网络拓扑结构发生变化,需由__系统管理员___修改路由的设置。 31. 网络管理的重要任务是:__控制___和_监控_______。 32. 安装Linux系统对硬盘分区时,必须有两种分区类型:文件系统分区___ 和___交换分区________。 33. 编写的Shell程序运行前必须赋予该脚本文件__执行___权限。 二、填空题(每题2分,共20分) 1.安装Linux系统对硬盘分区时,必须有两种分区类型:Linux原始分区(根分区) 和Linux 交换分区。 2.在Linux的两种链接文件中,只能实现对文件链接的一种方式是:软链接(符号链接)。3.Linux主要采用了请求调页和写时复制 _两种动态内存管理技术实现了物理内存以On demand方式动态分配。 4.对于System V类型的共享内存页面,Linux基于__Clock____算法决定哪些页面应当被换出物理内存。 5.在Linux与中断相关的三个核心数据结构中,用做抽象的中断控制器的数据结构是_ hw_interrupt_type _____,它包含一系列处理中断控制器特有的操作。 6. 通过将_ request _____动态链入块设备控制结构blk_dev_struct,Linux设备管理器有 效的实现了物理设备和缓冲区之间的异步读写通讯。 7.将/home/stud1/wang目录做归档压缩,压缩后生成wang.tar.gz文件,并将此文件保存到/home目录下,实现此任务的tar命令格式__tar czvf wang.tar.gz /home/stud1/wang____。 8.对于给定的文件file,统计其中所有包含字符串”WHU”的行数的一条命令是_grep WHU file | wc -l 9. 对于Shell脚本程序,若输入参数数量多于9个,则程序遍历每个参数可通过使用 __shift__命令实现。 10.在System V进程通讯方式中,ipc_perm结构描述对一个系统IPC对象的存取权限,而用于定位IPC对象的引用标志符key可以依据键值分成_公有____和___私有___两种类型。 二、填空题(每空1分,共20分) 1.在Linux2.4.0版本中,进程有 ___6___ 种状态,进程使用 __exit____ 系统调用后进入僵死状态。 2.在Linux 中,管道分为 __2____ 种类型,若创建或打开管道时获得的描述符存放在fd 中,则fd[1]是 _管道写描述符_ 。 3.Linux为用户提供的接口有 shell、XWINDOW、系统调用 4.Linux在I386体系结构中支持 __两级____分页机构。 5.每个设备文件名由主设备号和从设备号描述。第二块IDE硬盘的设备名为__hdb____,它上面的第三个主分区对应的文件名是 __hdb3____ 。 6. 超级块是描述 __文件系统属性____ 信息的数据结构,索引节点是描述 __文件属性____

操作系统教程第版课后答案

操作系统教程第5版课后答案 费祥林、骆斌编着 第一章操作系统概论 习题一 一、思考题 1.简述现代计算机系统的组成及层次结构。 答:现代计算机系统由硬件和软件两个部分组成。是硬件和软件相互交织形成的集合体,构成一个解决计算问题的工具。硬件层提供基本可计算的资源,包括处理器、寄存器、内存、外存及I/O设备。软件层由包括系统软件、支撑软件和应用软件。其中系统软件是最靠近硬件的。 2、计算机系统的资源可分成哪几类?试举例说明。 答:包括两大类,硬件资源和信息资源。硬件资源分为处理器、I/O设备、存储器等;信息资源分为程序和数据等。 3.什么是操作系统?操作系统在计算机系统中的主要作用是什么? 答:操作系统是一组控制和管理计算机硬件和软件资源,合理地对各类作业进行调度,以及方便用户使用的程序的集合。 操作系统在计算机系统中主要起4个方面的作用。 (1)服务用户观点——操作系统提供用户接口和公共服务程序 (2)进程交互观点——操作系统是进程执行的控制者和协调者 (3)系统实现观点——操作系统作为扩展机或虚拟机 (4)资源管理观点——操作系统作为资源的管理者和控制者 4.操作系统如何实现计算与操作过程的自动化? 答:大致可以把操作系统分为以下几类:批处理操作系统、分时操作系统、实时操作系统、网络操作系统和分布式操作系统。其中批处理操作系统能按照用户预先规定好的步骤控制作业的执行,实现计算机操作的自动化。又可分为批处理单道系统和批处理多道系统。单道系统每次只有一个作业装入计算机系统的主存储器运行,多个作业可自动、顺序地被装入运行。批处理多道系统则允许多个作业同时装入主存储器,中央处理器轮流地执行各个作业,各个作业可以同时使用各自所需的外围设备,这样可以充分利用计算机系统的资源,缩短作业时间,提高系统的吞吐率 5.操作系统要为用户提供哪些基本的和共性的服务? 答:(1)创建程序和执行程序;(2)数据I/O和信息存取;(3)通信服务;(4)差错检测和处理。为了保证高效率、高质量的工作,使得多个应用程序能够有效的共享系统资源,提高系统效率,操作系统还具备一些其他的功能:资源分配,统计,保护等。 6.试述操作系统所提供的各种用户接口。 答:操作系统通过程序接口和操作接口将其服务和功能提供给用户。程序接口由一组系统调用组成,在应用程序中使用“系统调用”可获得操作系统的低层服务,访问或使用系统管理的各种软硬件资源,是操作系统对外提供服务和功能

系统调用方式文件编程题库

Linux文件编程函数 一简述几个基本知识—— 1 Linux应用程序编程所用到的函数,主要有两种方式提供: 系统调用方式函数库方式 2 如何学习这些函数? 三步学习法: 第一步:借助工具书,查找函数名;《Unix环境高级编程》第二步:在Linux系统中,利用man命令查看函数信息,并填写函数学习手册。 第三步:实践,编写代码。 3 VI概念——文件描述符 性质:一个数字 特别含义:其功能类似于身份证号码,通过身份证号码,可以将对应的公民;在Linux系统中,每一个打开的文件,都对应一个数字,通过这个唯一的数字,可以找到这个打开的文件,并对其进行操作,比如读、写等。 二首先学习系统调用方式提供的函数—— 4 学习以下7个函数—— 打开文件创建文件关闭文件读文件写文件文件定位 复制文件描述符 5 打开文件——open 范例1:打开已经存的文件 open.c

#include #include #include void main() { int fd;/*文件描述符*/ fd = open("/home/test.c",O_RDWR); if(fd<0) printf("Open file fali!\n"); else printf("Open file sucessfully!\n"); } 范例2:利用open函数创建新文件 open_creat.c #include #include #include void main() {

操作系统教程第5版部分习题标准答案

第一章: 一、3、10、15、23、27、35 3.什么是操作系统?操作系统在计算机系统中的主要作用是什么? 操作系统是管理系统资源、控制程序执行、改善人机界面、提供各种服务,并合理组织计算机工作流程和为用户有效地使用计算机提供良好运行环境的一种系统软件. 主要作用 (1)服务用户—操作系统作为用户接口和公共服务程序 (2)进程交互—操作系统作为进程执行的控制者和协调者 (3)系统实现—操作系统作为扩展机或虚拟机 (4)资源管理—操作系统作为资源的管理者和控制者 10.试述系统调用与函数(过程)调用之间的区别。 (1)调用形式和实现方式不同; (2)被调用的代码位置不同; (3)提供方式不同 15.什么是多道程序设计?多道程序设计有什么特点? 多道程序设计是指允许多个作业(程序)同时进入计算机系统内存并执行交替计算的方法。从宏观上看是并行的,从微观上看是串行的。 (1)可以提高CPU、内存和设备的利用率; (2)可以提高系统的吞吐率,使单位时间内完成的作业数目增加; (3)可以充分发挥系统的并行性,使设备和设备之间,设备和CPU之间均可并行工作。 23.现代操作系统具有哪些基本功能?请简单叙述之。 (1)处理器管理; (2)存储管理; (3)设备管理; (4)文件管理; (5)联网与通信管理。 27.什么是操作系统的内核? 内核是一组程序模块,作为可信软件来提供支持进程并发执行的基本功能和基本操作,通常驻留在内核空间,运行于内核态,具有直接访问计算机系统硬件设备和所有内存空间的权限,是仅有的能够执行特权指令的程序。 35.简述操作系统资源管理的资源复用技术。

系统中相应地有多个进程竞争使用资源,由于计算机系统的物理资源是宝贵和稀有的,操作系统让众多进程共享物理资源,这种共享称为资源复用。 (1)时分复用共享资源从时间上分割成更小的单位供进程使用; (2)空分复用共享资源从空间上分割成更小的单位供进程使用。 . 二、2、5 2、答:画出两道程序并发执行图如下: (1) (见图中有色部分)。 (2)程序A无等待现象,但程序B有等待。程序B有等待时间段为180ms至200ms间(见 图中有色部分)。 5、答:画出三个作业并行工作图如下(图中着色部分为作业等待时间):

Linux 系统调用实现机制实验报告-内核安装详解

Linux系统调用实现机制实验报告 实验目的: 熟悉Linux系统调用过程,掌握系统调用的基本原理并在实验中实现系统调用的添加。 实验所需软件: 实验平台:VMware WorkStation; 系统环境:Red Hat Linux9.0; 传输工具:Ftp server(Serv USetup); 实验过程: 一、实验环境的搭建 (一)在Window下安装虚拟机VMware WorkStation; (二)在虚拟机上安装Red Hat 9.0系统; (三)在Window下安装Ftp Server,实现Linux与windows文件共享。 1. Ftp服务器的配置 打开Ftp Server,在[管理控制台]中选择[新建域],系统会弹出配置域向导的对话框,这里按要求填入相应信息,即可配置成功一个ftp服务器。步骤1要求用户填入域的[名称]和[说明],[名称]必须填写,[说明]可以不填。例如:在名称中输入[Linux实验],选择[下一步],便进入到域向导的步骤2。 步骤2是服务器访问协议和端口的配置,默认即可,点击[下一步]进入步骤3。步骤3是IP地址的配置,输入Windows主机的IP地址(202.112.147.176)。 确认无误后,点击[完成]。接下来要做的就是为域添加用户,根据添加用户向导,逐个填写[用户名],[密码],[根目录(即共享的文件目录)]以及[访问权限]。本次实验的配置为:tml(用户名),密码为空,E:\安全操作系统\实验材料(存放config-2.4.18forMP.txt和linux-2.4.18.tar.gz的目录),完全访问(访问权限)。到此,服务器的配置已经完成。 2. 在Linux下访问服务器,并下载文件

linux0.11系统调用原理及实验总结

Linux0.11系统调用原理及实验总结 1系统调用的原理 1.1概述 系统调用是一个软中断,中断号是0x80,它是上层应用程序与Linux系统内核进行交互通信的唯一接口。通过int 0x80,就可使用内核资源。不过,通常应用程序都是使用具有标准接口定义的C函数库间接的使用内核的系统调用,即应用程序调用C函数库中的函数,C函数库中再通过int 0x80进行系统调用。 所以,系统调用过程是这样的: 应用程序调用libc中的函数->libc中的函数引用系统调用宏->系统调用宏中使用int 0x80完成系统调用并返回。 另外一种访问内核的方式是直接添加一个系统调用,供自己的应用程序使用,这样就不再使用库函数了,变得更为直接,效率也会更高。 1.2相关的数据结构 在说具体的调用过程之前,这里先要说几个数据结构。 1.2.1 系统调用函数表 系统调用函数表sys_call_table是在sys.h中定义的,它是一个函数指针数组,每个元素是一个函数指针,它的值是各个系统提供的供上层调用的系统函数的入口地址。也就是说通过查询这个表就可以调用软中断0x80所有的系统函数处理函数。 1.2.2 函数指针偏移宏 这是一系列宏,它们的定义在unistd.h中,基本形式为#define _NR_name value,name为系统函数名字,value是一个整数值,是name所对应的系统函数指针在sys_call_table中的偏移量。 1.2.3 系统调用宏 系统调用宏_syscalln(type,name)在内核的unistd.h文件中定义的,对它展开就是: type name(参数列表) { 调用过程; }; 其中,n为参数个数,type为函数返回值类型,name为所要调用的系统函数的名字。在unistd.h 中共定义了4个这样的宏(n从0到3),也就是说,0.11核中系统调用最多可带3个参数。

操作系统复习题与答案

《操作系统》练习及参考答案 第1章操作系统概述 1.3.1 选择最合适的答案 1.一般用户更喜欢使用的系统是()。 A.手工操作 B.单道批处理 C.多道批处理 D.多用户分时系统 2. 与计算机硬件关系最密切的软件是()。 A.编译程序 B.数据库管理系统 C.游戏程序 D.OS 3. 现代OS具有并发性和共享性,是()的引入导致的。 A.单道程序 B. 磁盘 C. 对象 D.多道程序 4. 早期的OS主要追求的是()。 A.系统的效率 B.用户的方便性 C.可移植 D.可扩充性 5.()不是多道程序系统 A.单用户单任务 B.多道批处理系统 C.单用户多任务 D.多用户分时系统 6.()是多道操作系统不可缺少的硬件支持。 A.打印机 B.中断机构 C.软盘 D.鼠标 7. 特权指令可以在()执行。 A.目态 B.浏览器中 C.任意的时间 D.进程调度中 8. 没有了()计算机系统就启动不起来。 A.编译器 B.DBMS C.OS D.浏览器 9. 通道能够完成()之间的数据传输。 A.CPU与外设 B.存与外设 C.CPU与主存 D.外设与外设 10. 操作系统的主要功能有()。 A.进程管理、存储器管理、设备管理、处理机管理 B.虚拟存储管理、处理机管理、进程调度、文件系统 C.处理机管理、存储器管理、设备管理、文件系统 D.进程管理、中断管理、设备管理、文件系统 11. 单处理机计算机系统中,()是并行操作的。 A.处理机的操作与通道的操作是并行的 B.程序与程序 C.主程序与子程序 D.用户程序与操作系统程序 12. 处理机的所有指令可以在()执行。 A.目态 B.浏览器中 C.任意的时间 D.系统态 13.()功能不是操作系统直接完成的功能。 A.管理计算机硬盘 B.对程序进行编译 C.实现虚拟存储器 D.删除文件

Linux内核中增加一个系统调用

选题要求:在Linux内核中增加一个系统调用,并编写对应的linux应用程序。利用该系统调用能够遍历系统当前所有进程的任务描述符,并按进程父子关系将这些描述符所对应的进程id(PID)组织成树形结构显示。

目录 一.程序的主要设计思路,实现方式 (1) 1.1 添加系统调用的两种方法 (1) 1.1.1编译内核法 (1) 1.1.2内核模块法 (1) 1.2 程序的主要设计思路 (1) 1.3 环境 (2) 二.程序的模块划分,及对每个模块的说明 (2) 2.1 通过内核模块实现添加系统调用 (2) 2.1.1修改系统调用的模块 (2) 2.1.2获取sys_call_table的地址 (2) 2.1.3清除内存区域的写保护 (3) 2.2 编写系统调用指定自己的系统调用 (4) 2.2.1内核的初始化函数 (4) 2.2.2自己的系统调用服务例程 (4) 2.2.3移除内核模块时,将原有的系统调用进行还原 (6) 2.2.4模块注册相关 (6) 2.3 编写用户态的测试程序 (6) 2.4 编写Makefile文件 (7) 三.所遇到的问题及解决的方法 (8) 3.1 进程个数确定 (8) 3.2 被更改的系统调用号的选择 (8) 3.3 获取系统调用表的地址 (8) 3.4 内核和用户态数据交换 (8) 四.程序运行结果及使用说明 (8) 4.1 将编译出来的内核模块hello.ko加载到内核中 (8)

4.2通过dmesg查看输出信息是否正确 (9) 4.3运行测试程序,输出树状打印结果(部分结果截图) (9) 4.4卸载自定义模块 (10) 五.附录 (11) 5.1 内核模块程序hello.c (11) 5.2 测试程序hello_test.c (14) 5.3Makefile文件 (14)

(1)-系统调用篇

Windows的地址空间分用户模式与内核模式,低2GB的部分叫用户模式,高2G的部分叫内核模式,位于用户空间的代码不能访问内核空间,位于内核空间的代码却可以访问用户空间 一个线程的运行状态分内核态与用户态,当指令位于用户空间时,就表示当前处于内核态,当指令位于内核空间时,就处于内核态. 一个线程由用户态进入内核态的途径有3种典型的方式: 1、主动通过int 2e(软中断自陷方式)或sysenter指令(快速系统调用方式)调用系统服务函数,主 动进入内核 2、发生异常,被迫进入内核 3、发生硬件中断,被迫进入内核 现在讨论第一种进入内核的方式:(又分为两种方式) 1、通过老式的int 2e指令方式调用系统服务(因为老式cpu没提供sysenter指令) 如ReadFile函数调用系统服务函数NtReadFile Kernel32.ReadFile() //点号前面表示该函数的所在模块 { //所有Win32 API通过NTDLL中的系统服务存根函数调用系统服务进入内核 NTDLL.NtReadFile(); } NTDLL.NtReadFile() { Mov eax,152 //我们要调用的系统服务函数号,也即SSDT表中的索引,记录在eax中 If(cpu不支持sysenter指令) { Lea edx,[esp+4] //用户空间中的参数区基地址,记录在edx中 Int 2e //通过该自陷指令方式进入KiSystemService,‘调用’对应的系统服务 } Else { Lea edx,[esp +4] //用户空间中的参数区基地址,记录在edx中 Sysenter //通过sysenter方式进入KiFastCallEntry,‘调用’对应的系统服务 } Ret 36 //不管是从int 2e方式还是sysenter方式,系统调用都会返回到此条指令处 } Int 2e的内部实现原理: 该指令是一条自陷指令,执行该条指令后,cpu会自动将当前线程的当前栈切换为本线程的内核栈(栈分用户栈、内核栈),保存中断现场,也即那5个寄存器。然后从该cpu的中断描述符表(简称IDT)中找到这个2e中断号对应的函数(也即中断服务例程,简称ISR),jmp 到对应的isr处继续执行,此时这个ISR 本身就处于内核空间了,当前线程就进入内核空间了 Int 2e指令可以把它理解为intel提供的一个内部函数,它内部所做的工作如下 Int 2e { Cli //cpu一中断,立马自动关中断 Mov esp, TSS.内核栈地址 //切换为内核栈,TSS中记录了当前线程的内核栈地址 Push SS

二.掌握系统调用的实现过程,通过编译内核方法,增加一个新

二.掌握系统调用的实现过程,通过编译内核方法,增加一个新的系统调用。另编写一个应用程序,调用新增加的系统调用。 (1) 实现的功能是:文件拷贝; 操作步骤: 1。先在/usr/src/linux-2.4.18-3/kernel/sys.c文件末尾添加mycopy_s.c里的代码。 2。修改文件 /usr/src/linux-2.4.18-3/include/asm-i386/unistd.h文件在文件中相应位置加上: #define __NR_mycopy 239 3.修改文件 /usr/src/linux-2. 4.18-3/arch/i386/kernel/entry.S文件 在文件中相应位置加上: .long SYMBOL_NAME(sys_mycopy) 编译内核:(过程中要先后使用的命令如下,其中后两步操作为非必要,若不执行,则新内核下某些系统功能将无法实现) make mrproper make oldconfig make dep make clean make bzImage make modules make modules_install maek install 这几步均成功完成后,新内核已经生成,执行如下步骤: cp /usr/src/linux-2.4.18-3/arch/i386/boot/bzImage /boot/bzImage-new cp /usr/src/linux-2.4.18-3/System.map /boot/System.map-new ln –sf /boot/System.map-new /boot/System.map 然后进入 /etc/lilo.conf(本机采用Lilo配置),添加如下代码: image=/boot/bzImage-new label=linux-new root=/dev/hda1 /* hda1为安装linux的分区 */ 然后进入 /sbin,运行lilo,完成配置。 重启系统后选择标签为linux-new的新内核进入。 在新内核下测试系统调用,其运行结果如下: [YAKUZA$root] ls copy.c test.c [YAKUZA$root] gcc mycopy_test.c –o mycopy_test [YAKUZA$root] ls mycopy_test mycopy_test.c test.c [YAKUZA$root] cat test.c #include main()

read系统调用流程

Read 系统调用在用户空间中得处理过程 Linux 系统调用(SCI,system call interface)得实现机制实际上就是一个多路汇聚以及分解得过程,该汇聚点就就是 0x80 中断这个入口点(X86 系统结构)。也就就是说,所有系统调用都从用户空间中汇聚到 0x80 中断点,同时保存具体得系统调用号。当 0x80 中断处理程序运行时,将根据系统调用号对不同得系统调用分别处理(调用不同得内核函数处理)。系统调用得更多内容,请参见参考资料。 Read 系统调用也不例外,当调用发生时,库函数在保存 read 系统调用号以及参数后,陷入 0x80 中断。这时库函数工作结束。Read 系统调用在用户空间中得处理也就完成了。 回页首 Read 系统调用在核心空间中得处理过程 0x80 中断处理程序接管执行后,先检察其系统调用号,然后根据系统调用号查找系统调用表,并从系统调用表中得到处理 read 系统调用得内核函数 sys_read ,最后传递参数并运行 sys_read 函数。至此,内核真正开始处理 read 系统调用(sys_read 就是 read 系统调用得内核入口)。 在讲解 read 系统调用在核心空间中得处理部分中,首先介绍了内核处理磁盘请求得层次模型,然后再按该层次模型从上到下得顺序依次介绍磁盘读请求在各层得处理过程。 Read 系统调用在核心空间中处理得层次模型 图1显示了 read 系统调用在核心空间中所要经历得层次模型。从图中瞧出:对于磁盘得一次读请求,首先经过虚拟文件系统层(vfs layer),其次就是具体得文件系统层(例如 ext2),接下来就是 cache 层(page cache 层)、通用块层(generic block layer)、IO 调度层(I/O scheduler layer)、块设备驱动层(block device driver layer),最后就是物理块设备层(block device layer)

Linux系统调用与ptrace分析(实验报告)_[文档在线提供]

Linux系统调用与ptrace分析 概述 1.Linux的系统结构 在Linux系统结构中,最核心的是计算机硬件,它提供对Linux软件的支持,靠近硬件的内层是Linux内核程序(即操作系统)。内核直接和硬件打交道是程序和硬件之间的接口或界面。它对一切外层程序提供公共服务,把外部程序同硬件隔离开。内核程序大致可分为文件系统管理,进程管理,内存管理等几部分。进程管理又分为低级进程管理和高级进程管理。低级进程管理主要包括:进程调度分配,控制占用处理器的程序和基本的进程通信。高级进程管理主要包括:进程的创建,终止,进程间通信,进程在内存和外存之间的转储,信号机构和进程间跟踪控制等。内核程序的外层是实用程序,内核提供对实用程序的支持,两层之间的界面是系统调用。内核外的实用程序通过系统调用来和内核打交道。实现的过程是通过一种特殊的指令(陷入指令)进入内核,然后转入相应的系统调用处理程序。这也是本文将主要讨论的问题。 2.80386体系结构 80386的体系结构承认两类事件。 1.异常(exceptions) 2.中断(interrupts) 他们两都会引起“上下文转换”同时建立一个过程或任务,中断可以随时随地发生(包括在执行程序时)所以用来响应硬件信号。而异常则由指令内部错误引起。 每一个异常或中断都有一个唯一的标识符,在linux中被称为向量。 指令内部异常和NMI(不可屏蔽中断)的中断向量的范围从0—31。32-255的任何向量都可以用做 1.可屏蔽中断 2.编程(调试)异常 至于可屏蔽中断则取决于该系统的硬件配置。外部中断控制器在中断响应周期把中断向量放到总线上。 3.Linux系统调用流程概述 L inux系统调用的流程非常简单,它由0x80号中断进入系统调用入口,通过使用系统调用表保存系统调用服务函数的入口地址来实现,本文首先分析一般Linux系统调用的流程,然后再分析Linux系统调用sys_ptrace().

相关主题
文本预览
相关文档 最新文档