printf("账号[%d]:
%-20s余额[%d]:
%d\n",i,s[i].name,i,s[i].pay);
return0;
}
运行结果:
图2读取
程序三:
程序描叙:
要创建一个进程,最基本的系统调用是fork。
fork的作用是根据一个现有的进程复制出一个新进程,原来的进程称为父进程(ParentProcess),新进程称为子进程(ChildProcess)。
在Shell下输入命令可以运行一个程序,是因为Shell进程在读取用户输入的命令之后会调用fork复制出一个新的Shell进程,然后新的Shell进程调用exec执行新的程序
#include
#include
pid_tfork(void);
pid_tvfork(void);
调用fork时,系统将创建一个与当前进程相同的新的进程。
它与原有的进程具有相同的数据、连接关系和在程序同一处执行的连续性。
将原有的进程称为父进程,而把新生成的进程称为子进程。
子进程是父进程的一个复制,子进程获得同父进程相同的数据,但是同父进程使用不同的数据段和堆栈段。
流程图:
源程序:
#include
#include
#include
#include
intmain()
{
pid_tpid,vpid;
intstatus,i;
pid=fork();
if(pid==0)
{
printf("zheshizijinchengjinchenghaoshi:
%d\n",getpid());
sleep(5);
exit(6);
}
else
{
printf("zheshifujinchengzhengzaidengdaizijincheng........\n");
vpid=wait(&status);
i=WEXITSTATUS(status);
printf("dengdaidejinchengdejinchenghaoshi:
%d,jieshuzhutai:
%d\n",vpid,i);
}
}
运行结果:
图3存款
程序四:
程序描述:
此程序主要实现了管道的创建,管道的读写操作。
(1)管道的创建:
表头文件:
#include
定义函数:
intpipe(intfiledes[2]);
此函数用于创建一个管道。
参数filedes是一个两元整型数组,用于存放调用该函数所创建管道的两个文件描述符。
filedes[0]存放管道读取端的文件描述符;filedes[1]存放管道写入端的文件描述符。
调用成功时,返回值为0;调用失败时,返回值为-1。
(2)、管道的读写操作:
read(由已打开的文件读取数据)
表头文件:
#include
定义函数:
ssize_tread(intfd,void*buf,size_tcount);
函数说明:
read()会把参数fd所指的文件传送count个字节到buf指针所指的内存中。
若参数count为0,则read()不会有作用并返回0。
返回值为实际读取到的字节数,如果返回0,表示已到达文件尾或是无可读取的数据,此外文件读写位置会随读取到的字节移动。
附加说明:
如果顺利read()会返回实际读到的字节数,最好能将返回值与参数count作比较,若返回的字节数比要求读取的字节数少,则有可能读到了文件尾、从管道(pipe)或终端机读取,或者是read()被信号中断了读取动作。
当有错误发生时则返回-1,错误代码存入errno中,而文件读写位置则无法预期。
错误代码:
EINTR此调用被信号所中断。
EAGAIN当使用不可阻断I/O时(O_NONBLOCK),若无数据可读取则返回此值。
EBADF参数fd非有效的文件描述词,或该文件已关闭。
write(将数据写入已打开的文件内)
表头文件:
#include
定义函数:
ssize_twrite(intfd,constvoid*buf,size_tcount);
函数说明:
write()会把参数buf所指的内存写入count个字节到参数fd所指的文件内。
当然,文件读写位置也会随之移动。
返回值如果顺利write()会返回实际写入的字节数。
当有错误发生时则返回-1,错误代码存入errno中。
错误代码:
EINTR此调用被信号所中断。
EAGAIN当使用不可阻断I/O时(O_NONBLOCK),若无数据可读取则返回此值。
EADF参数fd非有效的文件描述词,或该文件已关闭。
程序源代码:
Fuzhi.c:
#include
#include
#include
intmain(void)
{
intn,fd[2];
pid_tpid;
charline[80];
if(pipe(fd)<0)
{
printf("pipeerror\n");
exit
(1);
}
pid=fork();
if(pid<0)
{
printf("forkerror\n");
exit
(1);
}
elseif(pid==0)
{
close(fd[1]);//关闭写入端
if(n=read(fd[0],line,80)>0)
printf("子进程从管道读取%d个字符,读取的字符串是:
%s\n",n,line);
close(fd[0]);
}
else
{
close(fd[0]);//关闭读取端
if(write(fd[1],"mynameis吕钰彬!
",80)!
=-1)
printf("父进程向管道写入mynameis吕钰彬!
\n");
close(fd[1]);
waitpid(pid,NULL,0);
}
exit(0);
}
运行结果:
图4父写子读
三、结论(应当准确、完整、明确精练;也可以在结论或讨论中提出建议、设想、尚待解决问题等。
)
Makefile介绍
make命令执行时,需要一个Makefile文件,以告诉make命令需要怎么样的去编译和
链接程序。
信号是进程之间通信的一种方式。
它包括3部分操作:
1.设置信号处理函数。
系统调用signal。
内核调用sys_signal(),设置当前进程对
某信号的处理函数。
2.发送信号.系统调用kill。
内核调用sys_kill()。
向目标进程发送信号。
3.接收并处理信号。
目标进程调用do_signal()处理信号。
从用户态的角度看,目标进程在执行用户态的代码时突然“中断”,转而去执行对应的信号处理函数(同样在用户态)。
等到信号处理函数执行完后,又从原来被中断的代码开始执行。
如何达到这样的效果呢?
由前面的几种内核的伪装现场的手段,我们可以猜出它这次
使用的手段。
比如,要让目标进程执行信号处理函数,在内核态中当然不可能直接调用,但
是可以通过设置pt_regs中的eip来达到这种效果。
但是,要使目标进程在执行完信号处理
函数后,又恢复到被中断的现场继续执行,那得花些技巧。
不过,不外乎设置堆栈。
这一次
还包括了用户态堆栈。
由于恢复的任务比较艰巨,系统干脆提供了一个系统调用sigreturn
。
既然内核希望用户在执行完信号处理函数后,调用sigreturn。
接下去的思路就比较
简单了。
就是先把用户态的eip设置为signal_handler(通过修改pt_regs中的eip来实
现),然后把堆栈中的返回地址改成调用sigreturn的一段代码的入口(当然原来的返回地
址也还是要保存的)并且把相关参数“压入”用户态堆栈。
这样,在源进程发送信号后不久,目标进程被调度到,然后执行到do_signal。
对信
号一一作处理.
pram——指定了RAM起始的物理地址,必须始终存在,并应等于PHYS_OFFSET。
pio——是供arch/arm/kernel/debug-armv.S中的调试宏使用的,包含IO的8MB区域的物理地址。
vio——是8MB调试区域的虚拟地址。
这个调试区域将被位于代码中(通过MAPIO函数)的随后的构架相关代码再次进行初始化。
lFⅨUP(func)——机器相关的修正,在存储子系统被初始化前运行。
lMAPIO(func)——机器相关的函数,用于IO区域的映射(包括上面的调试区)。
lINITIRQ(func)——用于初始化中断的机器相关的函数。
四、参考文献
[1]贾宗璞,许合利.C语言程序设计[M].江苏:
中国矿业大学出版社,2007.
[2]谭浩强.C程序设计(第二版)[M].北京:
清华大学出版社,2001.
[3]谭浩强,张基温,唐永炎编著.C语言程序设计教程[M].北京:
高等教育出版社,1992.
[4]秦友淑,曹化工编著.C语言程序设计教程[M].武汉:
华中理工大学出版社,1996.
[5]曹衍龙,林瑞仲,徐慧编著.C语言实例解析精粹[M].北京:
人民邮电出版社,2005.
[6]黄明等编著.C语言程序设计[M].大连。
大连理工大学出版,1996.
五、指导教师评语
签名:
年月日
课程设计成绩(五级分制)