构建xv6,跟着做一步步做。
sleep (easy)
很简单,把参数全部输出即可。
需要学习一下正则表达式。
^ 表示开头
*表示匹配零次或多次
$表示结尾
.匹配除换行符外的所有字符
一种是这种:
一种是跟着文件,有两个参数(或更多:
删除文件,其实就是解除链接。
- sysproc.c
uint64
sys_sleep(void)
{
int n;
uint ticks0;
argint(0, &n);
if(n < 0)
n = 0;
acquire(&tickslock);
ticks0 = ticks;
while(ticks - ticks0 < n){
if(killed(myproc())){
release(&tickslock);
return -1;
}
sleep(&ticks, &tickslock);
}
release(&tickslock);
return 0;
}
syscall中有三个传参的函数:
// syscall.c
void argint(int, int*);
int argstr(int, char*, int);
void argaddr(int, uint64 *);
第一个参数代表第几个参数。
acquire 得到锁,release释放锁,这里锁是自旋锁。
ticks记录时钟中断的次数,tickslock是它的锁。
myproc()返回当前的struct proc *(当前没有就返回0);struct proc*是描述进程或任务的数据结构。
int
killed(struct proc *p)
{
int k;
acquire(&p->lock);
k = p->killed;
release(&p->lock);
return k;
}
killed()从获取当前进程的killed变量的值。
如果进程的killed变量被设置为1,则退出进程。
- user.h
int sleep(int);
- usys.s
sleep:
li a7, SYS_sleep
ecall
ret
这小节做完去补risc-v。
代码:
#include "../kernel/types.h"
#include "../kernel/stat.h"
#include "user.h"
int
main (int argc, char *argv[])
{
if(argc <= 1){
fprintf(2,"sleep: missing operand\n");
exit(1);
}
int time = atoi(argv[1]);
sleep(time);
exit(0);
}
结果
使用pipe来传输字节
#include "../kernel/types.h"
#include "user.h"
int
main (int argc, char *argv[])
{
if(argc > 1){
fprintf(2,"usage: pingpong\n");
exit(1);
}
int p[2];
pipe(p);
char buf[2];
if(fork() == 0){
read(p[0],buf,1);
close(p[0]);
fprintf(1,"%d: received ping\n",getpid());
write(p[1],"a",1);
close(p[1]);
exit(0);
}else{
write(p[1],"a",1);
close(p[1]);
read(p[0],buf,1);
close(p[0]);
fprintf(1,"%d: received pong\n",getpid());
}
exit(0);
}
介绍
并发编程是为了使程序更加清晰,而不是更高效。
这不是什么
这篇文章讲的不是那种需要关注细节的线程模型,而是csp
通信顺序进程
截止1978,共享内存是嘴常用的进程通信机制,信号,临界区,管程是同步机制。
Hoare用同步通信解决了这两个问题。
Hoare的csp语言通过未缓冲通道进行发送和接收值,由于通道未缓冲,发送操作会阻塞到值被发送到接收器,这样就完成了同步机制。
Hoare的一个例子是把80行的卡片重排为125行的输出。它的解决方案是,一个进程读取一个卡片,一个一个字符发给另一个进程。接收字符的进程将字符排成125个一组发给打印机。这看起简单,但是如果没有io缓冲区,处理过程会很繁琐。实际上,io缓冲库只是这两个进程的封装,他们对外提供单字符通道接口。
另一个例子,生成小于1000的所有素数。伪代码如下:
p = get a number from left neighbor
print p
loop:
n = get a number from left neighbor
if (p does not divide n)
send n to right neighbor
这个线性管道的性质不能代表一般csp的性质,但是即使是严格的线性管道,也是很强大。在linux中管道和过滤器方法非常著名。
Hoare的进程通信比传统unix的shell管道更加普遍。事实上,Hoare给了一个例子,一个3*3的进程可以用于计算一个向量和3*3的矩阵相乘。
由于通信的管道不是一级对象,不能被存储在变量,作为参数,或者通过通道。导致在写程序时,必须固定通信结构。所以我们写程序打印开始的1000素数而不是n个素数,乘以一个向量3*3的矩阵而不是n*n的矩阵。
Pan and Promela
pan的csp方言有连接,选择和循环。
Newsqueak
unlike in CSP and Squeak, channels can be stored in variables, passed as arguments to functions, and sent across channels.
Alef
将Newsqueak的思想应用于一种成熟的系统编程语言。
p = get a number from left neighbor
print p
loop:
n = get a number from left neighbor
if (p does not divide n)
send n to right neighbor
思路:
写一个函数,递归,没有数字传给右边时就停止递归。
#include "../kernel/types.h"
#include "../kernel/stat.h"
#include "user.h"
#define N 35
int nums[N];
void prime(){
int p[2];
pipe(p);
if(fork() == 0){
close(p[1]);
int i;
for(i=0; 1; i++ )
if(read(p[0],nums+i,4) == 0) break;
close(p[0]);
nums[i] = 0;
if(i)
prime();
exit(0);
}else{
close(p[0]);
printf("prime %d\n",nums[0]);
for(int i=1; nums[i]; i++){
if(nums[i] % nums[0])
write(p[1],nums+i,4);
}
close(p[1]);
}
wait(0);
exit(0);
}
int main(int argc ,char* argv[]){
for(int i=0; i<N-1; i++){
nums[i] = i+2;
}
prime();
exit(0);
}
find (moderate)
查看ls.c
void
ls(char *path)
{
char buf[512], *p;
int fd;
struct dirent de;//目录项
struct stat st;//文件信息结构
if((fd = open(path, 0)) < 0){
fprintf(2, "ls: cannot open %s\n", path);
return;
}
if(fstat(fd, &st) < 0){
fprintf(2, "ls: cannot stat %s\n", path);
close(fd);
return;
}/* 获取目录的属性结构体 */
switch(st.type){
case T_FILE:
printf("%s %d %d %l\n", fmtname(path), st.type, st.ino, st.size);
break;
case T_DIR:
if(strlen(path) + 1 + DIRSIZ + 1 > sizeof buf){
printf("ls: path too long\n");
break;
}
strcpy(buf, path);
p = buf+strlen(buf);
*p++ = '/';
while(read(fd, &de, sizeof(de)) == sizeof(de)){
if(de.inum == 0)
continue;
memmove(p, de.name, DIRSIZ);
p[DIRSIZ] = 0;
if(stat(buf, &st) < 0){
printf("ls: cannot stat %s\n", buf);
continue;
}
printf("%s %d %d %d\n", fmtname(buf), st.type, st.ino, st.size);
}
break;
}
close(fd);
}
dirent的结构体:
struct dirent {
ushort inum;\* inode number *\
char name[DIRSIZ];
};
stat :
struct stat {
int dev; // File system's disk device
uint ino; // Inode number
short type; // Type of file
short nlink; // Number of links to file
uint64 size; // Size of file in bytes
};
文件直接打印,目录通过遍历打印。
memmove用于复制字符串。
思路:
和ls的思路类似,可以直接将ls的代码复制过来加以修改。
代码:
void
find(char* path,char* name){
char buf[512], *p;
int fd;
struct dirent de;
struct stat st;
if((fd = open(path, 0)) < 0){
fprintf(2, "find: %s: No such file or directory\n", path);
return;
}
if(fstat(fd, &st) < 0){
fprintf(2, "find: cannot stat %s\n", path);
close(fd);
return;
}
switch(st.type){
case T_FILE:
for(p=path+strlen(path); p >= path && *p != '/'; p--)
;
p++;
if(!strcmp(p,name))
printf("%s\n", path);
break;
case T_DIR:
if(strlen(path) + 1 + DIRSIZ + 1 > sizeof buf){
printf("find: path too long\n");
break;
}
strcpy(buf, path);
p = buf+strlen(buf);
*p++ = '/';
while(read(fd, &de, sizeof(de)) == sizeof(de)){
if(de.inum == 0 || !strcmp(de.name,".") || !strcmp(de.name,".."))
continue;
memmove(p, de.name, DIRSIZ);
p[DIRSIZ] = 0;
find(buf,name);
}
break;
}
close(fd);
}
int
main(int argc, char* argv[]){
if(argc <= 2){
fprintf(2,"usage: find path target\n");
exit(0);
}
find(argv[1],argv[2]);
exit(0);
}
一开始以为是和正常shell中的find,就直接按照正常的设计:
#include"../kernel/types.h"
#include"../kernel/stat.h"
#include"user.h"
#include"../kernel/fs.h"
void
find(char* path){
char buf[512], *p;
int fd;
struct dirent de;
struct stat st;
if((fd = open(path, 0)) < 0){
fprintf(2, "find: %s: No such file or directory\n", path);
return;
}
if(fstat(fd, &st) < 0){
fprintf(2, "find: cannot stat %s\n", path);
close(fd);
return;
}
switch(st.type){
case T_FILE:
printf("%s\n", path);
break;
case T_DIR:
if(strlen(path) + 1 + DIRSIZ + 1 > sizeof buf){
printf("find: path too long\n");
break;
}
strcpy(buf, path);
p = buf+strlen(buf);
*p++ = '/';
while(read(fd, &de, sizeof(de)) == sizeof(de)){
if(de.inum == 0 || !strcmp(de.name,".") || !strcmp(de.name,".."))
continue;
memmove(p, de.name, DIRSIZ);
p[DIRSIZ] = 0;
find(buf);
}
break;
}
close(fd);
}
int
main(int argc, char* argv[]){
int i;
if(argc < 2){
find(".");
exit(0);
}
for(i = 1; i<argc ; i++)
find(argv[i]);
exit(0);
}
要注意的是:"." 、".."这两个文件要注意不要死迭代了。
结果
休息了。
xargs (moderate)
思路:
有参数和没参数的。有参数需要每n个参数一组作为命令的参数。没参数的就按每MAXARG个参数一组。这样就统一了。
结果
回顾了一下基本的指令。文件描述符,输入输出重定向,管道。