lab1
sleep
直接将argv[1]赋值给sleep系统调用即可
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
int main(int argc, char *argv[])
{
char *sleeptime_char;
int sleeptime;
if (argc <= 1)
{
printf("sleep need one parm");
exit(1);
}
if (argc > 2)
{
printf("too many parm");
exit(1);
/* code */
}
sleeptime_char = argv[1];
sleeptime = atoi(sleeptime_char);
sleep(sleeptime);
exit(0);
}
|
pingpong
在pingpong.c中,有两种思路
只创建一个管道,父子都通过这个管道进行读写,父进程在写入之后必须wait等待子进程读取并在写入之后,在进行读取操作。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
#include "kernel/types.h"
#include "user.h"
int main(int argc,char* argv[]){
//创建两个管道,分别实现ping、pong的读写
int p[2];
pipe(p);
char readtext[10];//作为父进程和子进程的读出容器
//子程序读出
int pid = fork();
if(pid==0){
read(p[0],readtext,10);
printf("%d: received %s\n",getpid(),readtext);
write(p[1],"pong",10);
exit(0);//子进程一定要退出
}
//父程序写入
else{
write(p[1],"ping",10);
wait(0);//父进程阻塞,等待子进程读取
read(p[0],readtext,10);
printf("%d: received %s\n",getpid(),readtext);
exit(0);//父进程一定要退出
}
return 0;
}
|
需要建立两个管道分别用到作为读写管道。管道一头只作为读,另一头只作为写。当父子进程都需要读写时,创建两个管道,一个父读子写,一个父写子读。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
|
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
int main(int argc, char *argv[])
{
//0 read 1 write
int child_fd[2];
int parent_fd[2];
pipe(child_fd);
pipe(parent_fd);
if (fork()==0)
{
char buf[80];
close(child_fd[1]);
read(child_fd[0],buf,sizeof(buf));
close(child_fd[0]);
printf("%d: received p%sng\n",getpid(),buf);
close(parent_fd[0]);
write(parent_fd[1],"o",1);
close(parent_fd[1]);
exit(0);
}
else
{
char buf[80];
close(child_fd[0]);
write(child_fd[1],"i",1);
close(child_fd[1]);
close(parent_fd[1]);
read(parent_fd[0],buf,sizeof(buf));
close(parent_fd[0]);
printf("%d: received p%sng\n",getpid(),buf);
exit(0);
}
return 0;
}
|
其中思路二更好理解,两个管道各司其职
primes
main函数中负责创建一个管道,将2-35全部写入管道中。在child process中负责调用一个创建子进程的函数。
该函数主要做以下几件事:
- 读取parent process送入的每个数字,将其打印prime i。不用担心main函数的顺序,因为如果main函数没有write数字也没有关闭写描述符,那么child process的read函数会阻塞。
- 继续读取parent process管道中的数字,如果没有数字代表不需要在创建child process,退出。如果仍有数字,继续创建child process。
- 通过fork函数创建child process,在当前的process中需要判断该数字是否为素数,为素数则写入子进程的管道中。只有在数字2创建的进程需要一一判断,后续进程皆为素数产生的进程。
Tips:如果有指向管道写端的文件描述符没有关闭,而持有写端的进程也没有向管道内写入数据的时候,那么管道剩余的数据被读取后,再次read会被阻塞,之后有数据可读会再次返回。 如果所有的写端均被关闭,那么再次read会直接返回0,就像读到了文件末尾一样。
如果管道内没有其他prime,则不再创建子进程
注意wait,必须等到所有child process都结束了parent process才能结束
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
|
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
int isprime(int n)
{
int j, flag = 1;
for (j = 2; j <= n / 2; ++j)
{
if (n % j == 0)
{
flag = 0;
break;
}
}
return flag;
}
int create_process(int parent_fd[2])
{
char bytes[4];
close(parent_fd[1]);
/*如果有指向管道写端的文件描述符没有关闭,而持有写端的进程也没有向管道内
写入数据的时候,那么管道剩余的数据被读取后,再次read会被阻塞,之后有数据
可读会再次返回。
如果所有的写端均被关闭,那么再次read会直接返回0,就像读到了文件末尾一样。
*/
read(parent_fd[0], bytes, sizeof(bytes));
int prime = *(int *)bytes;
printf("prime %d\n", prime);
/*如果管道内没有其他prime,则不再创建子进程*/
if (read(parent_fd[0], bytes, sizeof(bytes)) == 0)
{
close(parent_fd[0]);
exit(0);
}
int child_fd[2];
pipe(child_fd);
int pid = fork();
if (pid < 0)
{
printf("fork error");
exit(1);
}
else if (pid == 0)
{
create_process(child_fd);
exit(0);
}
else
{
close(child_fd[0]);
do
{
int selectprime = *(int *)bytes;
if (isprime(selectprime))
{
write(child_fd[1], bytes, sizeof(bytes));
}
} while (read(parent_fd[0], bytes, sizeof(bytes)));
close(parent_fd[0]);
close(child_fd[1]);
//必须等到所有子进程都结束了才能exit
wait((int *)0);
exit(0);
}
}
int main(int argc, char *argv[])
{
int parent_fd[2];
pipe(parent_fd);
int count = 35;
int pid = fork();
if (pid < 0)
{
printf("fork error");
exit(1);
}
else if (pid == 0)
{
create_process(parent_fd);
exit(0);
}
else
{
close(parent_fd[0]);
for (int i = 2; i <= count; i++)
{
char bytes[4];
bytes[3] = (i >> 24) & 0xff;
bytes[2] = (i >> 16) & 0xff;
bytes[1] = (i >> 8) & 0xff;
bytes[0] = i & 0xff;
write(parent_fd[1], bytes, sizeof(bytes));
}
close(parent_fd[1]);
wait((int *)0);
exit(0);
}
}
|
find
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
|
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
#include "kernel/fs.h"
int find(char *path, char *filename)
{
char buf[512], *p;
int fd;
struct dirent de;
struct stat st;
int findflag = 0;
if ((fd = open(path, 0)) < 0)
{
fprintf(2, "find: cannot open %s\n", path);
return -1;
}
if (fstat(fd, &st) < 0)
{
fprintf(2, "find: cannot stat %s\n", path);
close(fd);
return -1;
}
// 将P指针指向path的最后buf+1的位置赋值"/"
strcpy(buf, path);
p = buf + strlen(buf);
*p++ = '/';
while (read(fd, &de, sizeof(de)) == sizeof(de))
{
if (de.inum == 0)
{
continue;
}
if (strcmp(de.name, ".") == 0 || strcmp(de.name, "..") == 0)
{
continue;
}
// 将指定路径目录下的dirent的name赋值给p,由此buf获取完整的路径
memmove(p, de.name, DIRSIZ);
*(p + DIRSIZ) = 0;
// 可以通过完整的文件名获取文件的stat,由此可以获取其type
if (stat(buf, &st) < 0)
{
printf("find: cannot stat %s\n", buf);
continue;
}
switch (st.type)
{
case T_FILE:
if (strcmp(de.name, filename) == 0)
{
printf("%s\n", buf);
findflag = 1;
}
break;
case T_DIR:
findflag = find(buf, filename);
break;
}
}
close(fd);
return findflag;
}
int main(int argc, char *argv[])
{
int findflag = 0;
if (argc < 2)
{
printf("find need para\n");
exit(1);
}
else if (argc == 2)
{
findflag = find(".", argv[1]);
}
else if (argc == 3)
{
findflag = find(argv[1], argv[2]);
}
else
{
printf("too many para\n");
exit(1);
}
if (findflag == 0)
{
printf("can not find the file\n");
exit(1);
}
exit(0);
}
|
xargs
以find . | xargs grep hello为例
xargs之需要处理|之后的参数,而|之前的参数保存在line中,创建一个字符容量为4的字符数组,1:cmd即例子中的grep,2:argv[2]即例子中的hello,3:line即例子中find .的结果,4:0代表结束。通过while持续读一个字符到line字符数组中,直到读到换行符结束。之后在子进程中调用exec系统调用,参数即为cmd和容量为4的字符数组。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
|
#include "kernel/types.h"
#include "kernel/stat.h"
#include "kernel/param.h"
#include "user/user.h"
#define MAXLINE 32
int main(int argc, char *argv[])
{
if (argc < 3)
{
printf("xargs need at least three params");
exit(1);
}
char *cmd = argv[1];
char line[MAXLINE];
// 管道前产生的结果放入line中
memset(line, 0, sizeof(line));
// 读入的每一个char
int i = 0;
char ch;
while (read(0, &ch, sizeof(ch)) != 0)
{
if (ch == '\n')
{
char *child_argv[4];
// 1: cmd 2: argv[2] 3:line 4:0表示结束
child_argv[0] = cmd;
child_argv[1] = argv[2];
child_argv[2] = line;
child_argv[3] = 0;
if (fork() == 0)
{
exec(cmd, child_argv);
}
else
{
wait((int *)0);
// parent process need to wait child process
}
memset(line,0,sizeof(line));
i=0;
}
else
{
line[i++] = ch;
}
}
exit(0);
}
|
Tips:字符之间可以用==比较,可以用=赋值
字符串之间不可以用==比较,要用strcmp
字符串比较
1
2
3
4
5
6
7
8
9
10
11
12
|
int main()
{
char *str1="hello";
char str2[]="hello";
printf("%d\n",str1=="hello");
printf("%d\n",str2=="hello");
printf("%d\n",strcmp(str1,"hello"));
printf("%d\n",strcmp(str2,"hello"));
return 0;
}
|
输出结果为1 0 0 0
1.字符串变量比较不能直接用==,但是可以用变量地址和字符串用==比较,如果地址相同,字符串会相等
char *str1 = “hello”;和”hello”的地址是相同的,所以返回结果相等
str2 == “hello”地址不相等。char str2[] = “hello”; 这里str2并不是指针,类型里已经说明它是一个数组,所以这会是另一个内存地址,于是str2与”hello”的地址是不同的。
综上:字符串的比较不能用==
2.字符串比较用strcmp函数
strcmp(str1,”hello”),strcmp(str2,”hello”)都是成立的
由于”hello”是字符串常量,编译器会进行优化:
所有的”hello”都是相同的,整个程序中只需要有一个”hello”字符串。
然后所有引用”hello”这个字符串的“指针变量”都赋值成相同的地址。
3.字符串赋值不能用=