@ -0,0 +1,77 @@ | |||
Datalab: bits.c | |||
Malloclab: mm.c | |||
Shelllab: tsh.c | |||
Malloc Lab | |||
before uploaded to gitlab, mark the progress in ucloud. | |||
(it seems that it cannot work in my own lxc) | |||
write the code with the help of CSAPP and its code. | |||
downloaded some trace file from github | |||
saved in ./traces | |||
./mdriver -a -g -v -t traces/ to autograde. | |||
deal with some bugs with blogs. | |||
2020/12/24-25: | |||
design the structure of heap with the help of CSAPP | |||
2020/12/26: | |||
write some helpful micros and functions with the help of CSAPP | |||
start to write mm_init and mm_malloc, but it's not tested | |||
2020/12/27: | |||
test mm_init,mm_malloc and debug | |||
start to write mm_free | |||
2020/12/28: | |||
test mm_free and debug | |||
write some part of mm_realloc | |||
2020/12/29: | |||
test mm_realloc and debug | |||
total autograded and modify heap | |||
2020/12/30-31: | |||
code modified with some blogs: | |||
1.deal with some heap errors | |||
2.adjust some expression to make codes simplier | |||
3.intensify the limitation of helper function | |||
4.modify some function and variable name | |||
5.complete the micros | |||
annotation completed with the help of CSAPP | |||
upload to ucloud and gitlab | |||
2021/1/19-20(Modification start): | |||
1.plan to use explict list instead of implict list. | |||
2.add some micros and edit mm_init. | |||
2021/1/21-22: | |||
1.edit mm_malloc and some related func. | |||
2021/1/23: | |||
1.final modification, with some helpful imformation from blogs. | |||
Shell Lab | |||
2021/1/19: | |||
1.start to design func with the help of CSAPP and blogs. | |||
2021/1/20-23: | |||
1.finish func eval, buildin_cmd, do_bgfg and waitfg. | |||
2.first push to master. | |||
2021/1/24: | |||
1.finish other funcs. | |||
2.try to debug in my lxc. | |||
3.if alright, push to master. | |||
BAD NEWS: | |||
don't pass test, so I need several days to debug with the help of some blogs. | |||
if pass, I will push to master. | |||
2021/1/24: | |||
debug successfully. |
@ -0,0 +1,456 @@ | |||
/* | |||
* CS:APP Data Lab | |||
* | |||
* <Please put your name and userid here> | |||
* | |||
* bits.c - Source file with your solutions to the Lab. | |||
* This is the file you will hand in to your instructor. | |||
* | |||
* WARNING: Do not include the <stdio.h> header; it confuses the dlc | |||
* compiler. You can still use printf for debugging without including | |||
* <stdio.h>, although you might get a compiler warning. In general, | |||
* it's not good practice to ignore compiler warnings, but in this | |||
* case it's OK. | |||
*/ | |||
#if 0 | |||
/* | |||
* Instructions to Students: | |||
* | |||
* STEP 1: Read the following instructions carefully. | |||
*/ | |||
You will provide your solution to the Data Lab by | |||
editing the collection of functions in this source file. | |||
INTEGER CODING RULES: | |||
Replace the "return" statement in each function with one | |||
or more lines of C code that implements the function. Your code | |||
must conform to the following style: | |||
int Funct(arg1, arg2, ...) { | |||
/* brief description of how your implementation works */ | |||
int var1 = Expr1; | |||
... | |||
int varM = ExprM; | |||
varJ = ExprJ; | |||
... | |||
varN = ExprN; | |||
return ExprR; | |||
} | |||
Each "Expr" is an expression using ONLY the following: | |||
1. Integer constants 0 through 255 (0xFF), inclusive. You are | |||
not allowed to use big constants such as 0xffffffff. | |||
2. Function arguments and local variables (no global variables). | |||
3. Unary integer operations ! ~ | |||
4. Binary integer operations & ^ | + << >> | |||
Some of the problems restrict the set of allowed operators even further. | |||
Each "Expr" may consist of multiple operators. You are not restricted to | |||
one operator per line. | |||
You are expressly forbidden to: | |||
1. Use any control constructs such as if, do, while, for, switch, etc. | |||
2. Define or use any macros. | |||
3. Define any additional functions in this file. | |||
4. Call any functions. | |||
5. Use any other operations, such as &&, ||, -, or ?: | |||
6. Use any form of casting. | |||
7. Use any data type other than int. This implies that you | |||
cannot use arrays, structs, or unions. | |||
You may assume that your machine: | |||
1. Uses 2s complement, 32-bit representations of integers. | |||
2. Performs right shifts arithmetically. | |||
3. Has unpredictable behavior when shifting an integer by more | |||
than the word size. | |||
EXAMPLES OF ACCEPTABLE CODING STYLE: | |||
/* | |||
* pow2plus1 - returns 2^x + 1, where 0 <= x <= 31 | |||
*/ | |||
int pow2plus1(int x) { | |||
/* exploit ability of shifts to compute powers of 2 */ | |||
return (1 << x) + 1; | |||
} | |||
/* | |||
* pow2plus4 - returns 2^x + 4, where 0 <= x <= 31 | |||
*/ | |||
int pow2plus4(int x) { | |||
/* exploit ability of shifts to compute powers of 2 */ | |||
int result = (1 << x); | |||
result += 4; | |||
return result; | |||
} | |||
FLOATING POINT CODING RULES | |||
For the problems that require you to implent floating-point operations, | |||
the coding rules are less strict. You are allowed to use looping and | |||
conditional control. You are allowed to use both ints and unsigneds. | |||
You can use arbitrary integer and unsigned constants. | |||
You are expressly forbidden to: | |||
1. Define or use any macros. | |||
2. Define any additional functions in this file. | |||
3. Call any functions. | |||
4. Use any form of casting. | |||
5. Use any data type other than int or unsigned. This means that you | |||
cannot use arrays, structs, or unions. | |||
6. Use any floating point data types, operations, or constants. | |||
NOTES: | |||
1. Use the dlc (data lab checker) compiler (described in the handout) to | |||
check the legality of your solutions. | |||
2. Each function has a maximum number of operators (! ~ & ^ | + << >>) | |||
that you are allowed to use for your implementation of the function. | |||
The max operator count is checked by dlc. Note that '=' is not | |||
counted; you may use as many of these as you want without penalty. | |||
3. Use the btest test harness to check your functions for correctness. | |||
4. Use the BDD checker to formally verify your functions | |||
5. The maximum number of ops for each function is given in the | |||
header comment for each function. If there are any inconsistencies | |||
between the maximum ops in the writeup and in this file, consider | |||
this file the authoritative source. | |||
/* | |||
* STEP 2: Modify the following functions according the coding rules. | |||
* | |||
* IMPORTANT. TO AVOID GRADING SURPRISES: | |||
* 1. Use the dlc compiler to check that your solutions conform | |||
* to the coding rules. | |||
* 2. Use the BDD checker to formally verify that your solutions produce | |||
* the correct answers. | |||
*/ | |||
#endif | |||
/* | |||
* bitAnd - x&y using only ~ and | | |||
* Example: bitAnd(6, 5) = 4 | |||
* Legal ops: ~ | | |||
* Max ops: 8 | |||
* Rating: 1 | |||
*/ | |||
int bitAnd(int x, int y) { | |||
int temp1; | |||
int temp2; | |||
int temp3; | |||
int res; | |||
temp1=~x; | |||
temp2=~y; | |||
temp3=temp1|temp2; | |||
res=~temp3; | |||
// use (a AND b) == Not((Not a) or (Not b)) | |||
return res; | |||
} | |||
/* | |||
* getByte - Extract byte n from word x | |||
* Bytes numbered from 0 (LSB) to 3 (MSB) | |||
* Examples: getByte(0x12345678,1) = 0x56 | |||
* Legal ops: ! ~ & ^ | + << >> | |||
* Max ops: 6 | |||
* Rating: 2 | |||
*/ | |||
int getByte(int x, int n) { | |||
int mask; | |||
int temp; | |||
int res; | |||
mask = (0xff); | |||
temp = x >> (n * 8); | |||
res=mask&temp; | |||
//get the mask 11111111 fit the wanted bit | |||
return res; | |||
} | |||
/* | |||
* logicalShift - shift x to the right by n, using a logical shift | |||
* Can assume that 0 <= n <= 31 | |||
* Examples: logicalShift(0x87654321,4) = 0x08765432 | |||
* Legal ops: ! ~ & ^ | + << >> | |||
* Max ops: 20 | |||
* Rating: 3 | |||
*/ | |||
int logicalShift(int x, int n) { | |||
int temp1; | |||
int temp2; | |||
int res; | |||
//temp1=(x|-1)<<(32-n); | |||
//temp2=x>>n; | |||
//res=temp1^temp2; | |||
temp1 = ~(1<<31); //n=0,>>-1 wrong! temp1:011111......1 (32) | |||
temp2 = ((temp1 >> n) << 1)+1; //a trick to erase the top n 1s created by the arithmetic shift. | |||
res = temp2 & (x >> n); | |||
return res; | |||
} | |||
/* | |||
* bitCount - returns count of number of 1's in word | |||
* Examples: bitCount(5) = 2, bitCount(7) = 3 | |||
* Legal ops: ! ~ & ^ | + << >> | |||
* Max ops: 40 | |||
* Rating: 4 | |||
*/ | |||
int bitCount(int x) { | |||
int count; | |||
int temp1; | |||
int temp2; | |||
int temp3; | |||
int mask1; | |||
int mask2; | |||
int mask3; | |||
int mask4; | |||
int mask5; | |||
temp1 = (0x55) + (0x55 << 8); | |||
temp2 = (0x33) + (0x33 << 8); | |||
temp3 = (0x0f) + (0x0f << 8); | |||
mask1 = (temp1) + (temp1 << 16); | |||
mask2 = (temp2) + (temp2 << 16); | |||
mask3 = (temp3) + (temp3 << 16); | |||
mask4 = (0xff) + (0xff << 16); | |||
mask5 = (0xff) + (0xff << 8); | |||
count = (x & mask1) + ((x >> 1) & mask1); | |||
count = (count & mask2) + ((count >> 2) & mask2); | |||
count = (count & mask3) + ((count >> 4) & mask3); | |||
count = (count & mask4) + ((count >> 8) & mask4); | |||
count = (count & mask5) + ((count >> 16) & mask5); | |||
return count; | |||
} | |||
/* | |||
* bang - Compute !x without using ! | |||
* Examples: bang(3) = 0, bang(0) = 1 | |||
* Legal ops: ~ & ^ | + << >> | |||
* Max ops: 12 | |||
* Rating: 4 | |||
*/ | |||
int bang(int x) { | |||
int temp1; | |||
int temp2; | |||
int res; | |||
temp1=(~x)+1; //0x00000000 0x80000000 compute (-x) | |||
temp2=(x>>31)&1; | |||
res=1^((((temp1^x)>>31)&1)|temp2); | |||
//???A nice trick??? | |||
return res; | |||
} | |||
/* | |||
* tmin - return minimum two's complement integer | |||
* Legal ops: ! ~ & ^ | + << >> | |||
* Max ops: 4 | |||
* Rating: 1 | |||
*/ | |||
int tmin(void) { | |||
int res; | |||
res=(-1)<<31; | |||
return res; | |||
} | |||
/* | |||
* fitsBits - return 1 if x can be represented as an | |||
* n-bit, two's complement integer. | |||
* 1 <= n <= 32 | |||
* Examples: fitsBits(5,3) = 0, fitsBits(-4,3) = 1 | |||
* Legal ops: ! ~ & ^ | + << >> | |||
* Max ops: 15 | |||
* Rating: 2 | |||
*/ | |||
int fitsBits(int x, int n) { | |||
int temp1; | |||
int temp2; | |||
int res; | |||
temp1=x>>(n-1); | |||
temp2=x>>31; | |||
res=temp1^temp2; | |||
return !res; | |||
} | |||
/* | |||
* divpwr2 - Compute x/(2^n), for 0 <= n <= 30 | |||
* Round toward zero | |||
* Examples: divpwr2(15,1) = 7, divpwr2(-33,4) = -2 | |||
* Legal ops: ! ~ & ^ | + << >> | |||
* Max ops: 15 | |||
* Rating: 2 | |||
*/ | |||
int divpwr2(int x, int n) { | |||
int temp1=x>>31; | |||
int temp2=(1<<n)+(~0); //2^n-1 | |||
x=x+(temp1&temp2); | |||
return x>>n; | |||
} | |||
/* | |||
* negate - return -x | |||
* Example: negate(1) = -1. | |||
* Legal ops: ! ~ & ^ | + << >> | |||
* Max ops: 5 | |||
* Rating: 2 | |||
*/ | |||
int negate(int x) { | |||
int res; | |||
res=(~x)+1;; | |||
return res; | |||
} | |||
/* | |||
* isPositive - return 1 if x > 0, return 0 otherwise | |||
* Example: isPositive(-1) = 0. | |||
* Legal ops: ! ~ & ^ | + << >> | |||
* Max ops: 8 | |||
* Rating: 3 | |||
*/ | |||
int isPositive(int x) { | |||
int temp1; | |||
int temp2; | |||
int res; | |||
temp1=x>>31; | |||
temp2=temp1&1; | |||
res=(!temp2)&(!!x); | |||
return res; | |||
} | |||
/* | |||
* isLessOrEqual - if x <= y then return 1, else return 0 | |||
* Example: isLessOrEqual(4,5) = 1. | |||
* Legal ops: ! ~ & ^ | + << >> | |||
* Max ops: 24 | |||
* Rating: 3 | |||
*/ | |||
int isLessOrEqual(int x, int y) { | |||
int s1,s2,valid,res; | |||
//first judge two nums are both + or -, if not,easily judge. | |||
//if it does,try x-y or y-x | |||
s1=(x>>31)&1; | |||
s2=(y>>31)&1; | |||
valid=s1^s2; //equal? | |||
res=y+(~x)+1; | |||
return (((!(res>>31))&1)&(!valid))|(valid&(s1)&(!s2)); | |||
} | |||
/* | |||
* ilog2 - return floor(log base 2 of x), where x > 0 | |||
* Example: ilog2(16) = 4 | |||
* Legal ops: ! ~ & ^ | + << >> | |||
* Max ops: 90 | |||
* Rating: 4 | |||
*/ | |||
int ilog2(int x) { | |||
int count; | |||
int temp1; | |||
int temp2; | |||
int temp3; | |||
int temp4; | |||
count=(!!(x>>16))<<4; | |||
temp1=(x>>(count+8)); | |||
count=((!!temp1)<<3)+count; | |||
temp2=(x>>(count+4)); | |||
count=((!!temp2)<<2)+count; | |||
temp3=(x>>(count+2)); | |||
count=((!!temp3)<<1)+count; | |||
temp4=(x>>(count+1)); | |||
count=(!!temp4)+count; | |||
return count; | |||
} | |||
/* | |||
* float_neg - Return bit-level equivalent of expression -f for | |||
* floating point argument f. | |||
* Both the argument and result are passed as unsigned int's, but | |||
* they are to be interpreted as the bit-level representations of | |||
* single-precision floating point values. | |||
* When argument is NaN, return argument. | |||
* Legal ops: Any integer/unsigned operations incl. ||, &&. also if, while | |||
* Max ops: 10 | |||
* Rating: 2 | |||
*/ | |||
unsigned float_neg(unsigned uf) { | |||
unsigned temp,res; | |||
temp = uf & 0x7fffffff; | |||
res = uf ^ 0x80000000; | |||
if(temp>0x7f800000) | |||
res=uf; //NaN | |||
return res; | |||
} | |||
/* | |||
* float_i2f - Return bit-level equivalent of expression (float) x | |||
* Result is returned as unsigned int, but | |||
* it is to be interpreted as the bit-level representation of a | |||
* single-precision floating point values. | |||
* Legal ops: Any integer/unsigned operations incl. ||, &&. also if, while | |||
* Max ops: 30 | |||
* Rating: 4 | |||
*/ | |||
unsigned float_i2f(int x) { | |||
unsigned shiftleft,aftershift,tmp,flag,absx,sign; | |||
shiftleft=0; | |||
absx=x; | |||
sign=0; | |||
if(!x) return 0; | |||
if(x<0){ | |||
sign=0x80000000; | |||
absx=(~x)+1; | |||
} | |||
aftershift=absx; | |||
for(;;){ | |||
tmp=aftershift; | |||
aftershift=aftershift<<1; | |||
shiftleft++; | |||
if(tmp&0x80000000) break; | |||
} | |||
if((aftershift&0x01ff)>0x0100) | |||
flag=1; | |||
else if((aftershift&0x03ff)==0x0300) | |||
flag=1; | |||
else | |||
flag=0; | |||
return sign+(aftershift>>9)+((159-shiftleft)<<23)+flag; | |||
//according to the design of float value, devide into 3 parts | |||
//aftershift: last part, shiftleft: second part,count the multiples of 2, sign: first part, decide if x is + or - | |||
} | |||
/* | |||
* float_twice - Return bit-level equivalent of expression 2*f for | |||
* floating point argument f. | |||
* Both the argument and result are passed as unsigned int's, but | |||
* they are to be interpreted as the bit-level representation of | |||
* single-precision floating point values. | |||
* When argument is NaN, return argument | |||
* Legal ops: Any integer/unsigned operations incl. ||, &&. also if, while | |||
* Max ops: 30 | |||
* Rating: 4 | |||
*/ | |||
unsigned float_twice(unsigned uf) { | |||
unsigned sign,normalized,special,itsign,doutem; | |||
sign=1<<31; | |||
itsign=uf&sign; | |||
normalized=(uf<<1)>>24; | |||
if(normalized==0xff) | |||
special=1; | |||
else | |||
special=0; | |||
//special number: NaN etc. | |||
doutem=uf<<1; | |||
if(special || (!uf) || uf==sign) //0 -0 special | |||
return uf; | |||
else if(normalized) //normalize | |||
return uf + (1<<23); | |||
else | |||
return doutem | itsign; //second and last part are both multipled by 2. | |||
} |
@ -0,0 +1,333 @@ | |||
/* | |||
* mm-naive.c - The fastest, least memory-efficient malloc package. | |||
* | |||
* In this naive approach, a block is allocated by simply incrementing | |||
* the brk pointer. A block is pure payload. There are no headers or | |||
* footers. Blocks are never coalesced or reused. Realloc is | |||
* implemented directly using mm_malloc and mm_free. | |||
* | |||
* NOTE TO STUDENTS: Replace this header comment with your own header | |||
* comment that gives a high level description of your solution. | |||
*/ | |||
#include <stdio.h> | |||
#include <stdlib.h> | |||
#include <assert.h> | |||
#include <unistd.h> | |||
#include <string.h> | |||
#include "mm.h" | |||
#include "memlib.h" | |||
/********************************************************* | |||
* NOTE TO STUDENTS: Before you do anything else, please | |||
* provide your team information in the following struct. | |||
********************************************************/ | |||
team_t team = { | |||
/* Team name */ | |||
"ateam", | |||
/* First member's full name */ | |||
"Harry Bovik", | |||
/* First member's email address */ | |||
"bovik@cs.cmu.edu", | |||
/* Second member's full name (leave blank if none) */ | |||
"", | |||
/* Second member's email address (leave blank if none) */ | |||
"" | |||
}; | |||
/* single word (4) or double word (8) alignment */ | |||
#define ALIGNMENT 8 | |||
/* rounds up to the nearest multiple of ALIGNMENT */ | |||
#define ALIGN(size) (((size) + (ALIGNMENT-1)) & ~0x7) | |||
#define SIZE_T_SIZE (ALIGN(sizeof(size_t))) | |||
#define WSIZE 4 /* Word and header/footer size (bytes) */ | |||
#define DSIZE 8 /* Double word size (bytes) */ | |||
#define CHUNKSIZE (1<<12) /* Extend heap by this amount (bytes) */ | |||
#define MAX(x, y) ((x) > (y)? (x) : (y)) | |||
#define MIN(x, y) ((x) > (y)? (y) : (x)) | |||
/* Pack a size and allocated bit into a word(4 bytes) */ | |||
#define PACK(size, alloc) ((size) | (alloc)) | |||
/* Read and write a word at address p */ | |||
#define GET(ptr) (*(unsigned int *)(ptr)) | |||
#define PUT(ptr, val) (*(unsigned int *)(ptr) = (val)) | |||
/* not affected in the case that the first bit is 1 */ | |||
/* if use int, may affected */ | |||
/* Read the size and allocated flags from address ptr */ | |||
#define GET_SIZE(ptr) (GET(ptr) & ~0x7) | |||
#define GET_ALLOC(ptr) (GET(ptr) & 0x1) | |||
/* ALIGN return the nearest multiple of 8, so the last 3 bits are always 000 */ | |||
/* Given block ptr ptr, compute address of its header and footer */ | |||
#define HDRP(ptr) ((char *)(ptr) - WSIZE) | |||
#define FTRP(ptr) ((char *)(ptr) + GET_SIZE(HDRP(ptr)) - DSIZE) | |||
/* Given block ptr ptr, compute address of next and previous blocks */ | |||
#define NEXT_BLKP(ptr) ((char *)(ptr) + GET_SIZE(((char *)(ptr) - WSIZE))) | |||
#define PREV_BLKP(ptr) ((char *)(ptr) - GET_SIZE(((char *)(ptr) - DSIZE))) | |||
/* ptr means the address of payload */ | |||
/* | |||
* I don't know why PREV_BLKP is written like this, but according to the information | |||
* I have found, it seems that this method can work properly. | |||
*/ | |||
/* Modified micros */ | |||
/* Add two micros only for free blocks */ | |||
/* Point to next and prev free block, only free blocks have*/ | |||
#define PREV_LINKNODE_RP(ptr) ((char *)(ptr)) | |||
#define NEXT_LINKNODE_RP(ptr) ((char *)(ptr) + WSIZE) | |||
char *heap_ptr = NULL; | |||
char *root = NULL; | |||
/* | |||
* Insert_linknode - insert p to the root of explcit list. | |||
*/ | |||
inline void insert_linknode(char *p){ //inline decreases the cost of calling the func. | |||
/* insert p to explcit list. */ | |||
char *next = GET(root); | |||
if (next != NULL) | |||
PUT(PREV_LINKNODE_RP(next), p); | |||
PUT(NEXT_LINKNODE_RP(p), next); | |||
PUT(root, p); | |||
} | |||
/* | |||
* Del_linknode - delete linknode p. | |||
*/ | |||
inline void del_linknode(char *p){ | |||
char *prev = GET(PREV_LINKNODE_RP(p)); | |||
char *next = GET(NEXT_LINKNODE_RP(p)); | |||
if (prev == NULL){ | |||
if (next != NULL) | |||
PUT(PREV_LINKNODE_RP(next), 0); | |||
PUT(root, next); | |||
} | |||
else if (prev != NULL){ | |||
if (next != NULL) | |||
PUT(PREV_LINKNODE_RP(next), prev); | |||
PUT(NEXT_LINKNODE_RP(prev), next); | |||
} | |||
PUT(NEXT_LINKNODE_RP(p), 0); | |||
PUT(PREV_LINKNODE_RP(p), 0); | |||
} | |||
/* | |||
* coalesce - Boundary tag coalescing. Return ptr to coalesced block | |||
*/ | |||
static void *coalesce(void *ptr) | |||
{ | |||
size_t prev_alloc = GET_ALLOC(FTRP(PREV_BLKP(ptr))); | |||
size_t next_alloc = GET_ALLOC(HDRP(NEXT_BLKP(ptr))); | |||
size_t size = GET_SIZE(HDRP(ptr)); | |||
if (prev_alloc && next_alloc) { /* left and right used */ | |||
//return ptr; | |||
} | |||
else if (prev_alloc && !next_alloc) { /* only right free */ | |||
size += GET_SIZE(HDRP(NEXT_BLKP(ptr))); | |||
del_linknode(NEXT_LINKNODE_RP(ptr)); /* remove from linklist*/ | |||
PUT(HDRP(ptr), PACK(size, 0)); | |||
PUT(FTRP(ptr), PACK(size,0)); | |||
} | |||
else if (!prev_alloc && next_alloc) { /* only left free */ | |||
size += GET_SIZE(HDRP(PREV_BLKP(ptr))); | |||
del_linknode(PREV_BLKP(ptr)); | |||
PUT(FTRP(ptr), PACK(size, 0)); | |||
PUT(HDRP(PREV_BLKP(ptr)), PACK(size, 0)); | |||
ptr = PREV_BLKP(ptr); | |||
} | |||
else { /* both sides free */ | |||
size += GET_SIZE(HDRP(PREV_BLKP(ptr))) + | |||
GET_SIZE(FTRP(NEXT_BLKP(ptr))); | |||
del_linknode(PREV_BLKP(ptr)); | |||
del_linknode(NEXT_BLKP(ptr)); | |||
PUT(HDRP(PREV_BLKP(ptr)), PACK(size, 0)); | |||
PUT(FTRP(NEXT_BLKP(ptr)), PACK(size, 0)); | |||
ptr = PREV_BLKP(ptr); | |||
} | |||
insert_linknode(ptr); | |||
return ptr; | |||
} | |||
/* | |||
* extend_heap - Extend heap with free block and return its block pointer | |||
*/ | |||
static void *extend_heap(size_t extendsize) | |||
{ | |||
char *ptr; | |||
size_t size; | |||
/* Allocate an even number of extendsize to maintain alignment */ | |||
size = (extendsize % 2) ? (extendsize+1) * WSIZE : extendsize * WSIZE; | |||
if ((long)(ptr = mem_sbrk(size)) == -1) /* this means ptr -> 0xffffffff */ | |||
return NULL; | |||
/* Initialize free block header,footer and the epilogue header */ | |||
PUT(HDRP(ptr), PACK(size, 0)); /* Free block header */ | |||
PUT(FTRP(ptr), PACK(size, 0)); /* Free block footer */ | |||
// HDRP make a left 8-bits shift, the same to a node size. | |||
PUT(NEXT_LINKNODE_RP(ptr), 0); /* Next linknode */ | |||
PUT(PREV_LINKNODE_RP(ptr), 0); /* Prev linknode */ | |||
PUT(HDRP(NEXT_BLKP(ptr)), PACK(0, 1)); /* New epilogue header */ | |||
/* Coalesce free blocks */ | |||
return coalesce(ptr); | |||
} | |||
/* | |||
* find_fit - Find a fit for a block with size bytes | |||
*/ | |||
static void *find_fit(size_t asize) | |||
{ | |||
char *tmp = GET(root); | |||
int count = 0; | |||
while (tmp != NULL){ | |||
if (GET_SIZE(HDRP(tmp)) >= asize && (count++) != 0) | |||
return tmp; | |||
tmp = GET(NEXT_LINKNODE_RP(tmp)); | |||
} | |||
return NULL; /* No fit */ | |||
} | |||
/* | |||
* place - Place block of size bytes at start of free block ptr | |||
* and split if remainder would be at least minimum block size | |||
*/ | |||
static void place(void *ptr, size_t size) | |||
{ | |||
size_t newsize = GET_SIZE(HDRP(ptr)); | |||
del_linknode(ptr); | |||
if ((newsize - size) >= (2*DSIZE)) { | |||
PUT(HDRP(ptr), PACK(size, 1)); | |||
PUT(FTRP(ptr), PACK(size, 1)); | |||
ptr = NEXT_BLKP(ptr); | |||
PUT(HDRP(ptr), PACK(newsize-size, 0)); | |||
PUT(FTRP(ptr), PACK(newsize-size, 0)); | |||
PUT(NEXT_LINKNODE_RP(ptr), 0); | |||
PUT(PREV_LINKNODE_RP(ptr), 0); | |||
coalesce(ptr); | |||
} | |||
else { | |||
PUT(HDRP(ptr), PACK(newsize, 1)); | |||
PUT(FTRP(ptr), PACK(newsize, 1)); | |||
} | |||
} | |||
int mm_init(void) | |||
{ | |||
/* Create the initial empty heap */ | |||
if ((heap_ptr = mem_sbrk(6*WSIZE)) == (void *)-1) /* 0xffffffff */ | |||
return -1; | |||
PUT(heap_ptr, 0); /* Alignment padding */ | |||
PUT(heap_ptr + (1*WSIZE), 0); /* Free prev linknode */ | |||
PUT(heap_ptr + (2*WSIZE), 0); /* Free next linknode */ | |||
PUT(heap_ptr + (3*WSIZE), PACK(DSIZE, 1)); /* Prologue header */ | |||
PUT(heap_ptr + (4*WSIZE), PACK(DSIZE, 1)); /* Prologue footer */ | |||
PUT(heap_ptr + (5*WSIZE), PACK(0, 1)); /* Epilogue header */ | |||
root = heap_ptr + (1 * WSIZE); | |||
heap_ptr += (4*WSIZE); | |||
/* Extend the empty heap with a free block of CHUNKSIZE bytes */ | |||
if (extend_heap(CHUNKSIZE/WSIZE) == NULL) | |||
return -1; | |||
return 0; | |||
} | |||
void *mm_malloc(size_t size) | |||
{ | |||
size_t newsize; /* Adjusted block size */ | |||
size_t extendsize; /* Amount to extend heap if no fit */ | |||
char *ptr; | |||
if (heap_ptr == NULL){ | |||
mm_init(); | |||
} | |||
if (size == 0) | |||
return NULL; | |||
/* Adjust block size to include overhead and alignment reqs. */ | |||
/* Need to use this method, but I don't know why */ | |||
if (size <= DSIZE) | |||
newsize = 2*DSIZE; | |||
else | |||
newsize = DSIZE * ((size + (DSIZE) + (DSIZE-1)) / DSIZE); | |||
/* Search the free list for a fit */ | |||
if ((ptr = find_fit(newsize)) != NULL) { | |||
place(ptr, newsize); | |||
return ptr; | |||
} | |||
/* No fit found. Get more memory and place the block */ | |||
extendsize = MAX(newsize,CHUNKSIZE); | |||
if ((ptr = extend_heap(extendsize/WSIZE)) == NULL) | |||
return NULL; | |||
place(ptr, newsize); | |||
return ptr; | |||
} | |||
void mm_free(void *ptr) | |||
{ | |||
if (ptr == NULL) | |||
return; | |||
size_t size = GET_SIZE(HDRP(ptr)); | |||
PUT(HDRP(ptr), PACK(size, 0)); | |||
PUT(FTRP(ptr), PACK(size, 0)); | |||
/* Reset linknodes */ | |||
PUT(NEXT_LINKNODE_RP(ptr), 0); | |||
PUT(PREV_LINKNODE_RP(ptr), 0); | |||
coalesce(ptr); | |||
} | |||
void *mm_realloc(void *ptr, size_t size) | |||
{ | |||
size_t oldsize; | |||
void *newptr; | |||
if(size == 0) { | |||
mm_free(ptr); | |||
return 0; | |||
} | |||
if(ptr == NULL) { | |||
return mm_malloc(size); | |||
} | |||
newptr = mm_malloc(size); | |||
/* realloc failed */ | |||
if(newptr == NULL) { | |||
return 0; | |||
} | |||
/* Copy the old heap area. */ | |||
oldsize = GET_SIZE(HDRP(ptr)); | |||
if(size < oldsize) | |||
oldsize = size; | |||
memcpy(newptr, ptr, oldsize); | |||
/* Free the old block. */ | |||
mm_free(ptr); | |||
return newptr; | |||
} |
@ -0,0 +1,659 @@ | |||
/* | |||
* tsh - A tiny shell program with job control | |||
* | |||
* <Put your name and login ID here> | |||
*/ | |||
#include <stdio.h> | |||
#include <stdlib.h> | |||
#include <unistd.h> | |||
#include <string.h> | |||
#include <ctype.h> | |||
#include <signal.h> | |||
#include <sys/types.h> | |||
#include <sys/wait.h> | |||
#include <errno.h> | |||
/* Misc manifest constants */ | |||
#define MAXLINE 1024 /* max line size */ | |||
#define MAXARGS 128 /* max args on a command line */ | |||
#define MAXJOBS 16 /* max jobs at any point in time */ | |||
#define MAXJID 1<<16 /* max job ID */ | |||
/* Job states */ | |||
#define UNDEF 0 /* undefined */ | |||
#define FG 1 /* running in foreground */ | |||
#define BG 2 /* running in background */ | |||
#define ST 3 /* stopped */ | |||
/* | |||
* Jobs states: FG (foreground), BG (background), ST (stopped) | |||
* Job state transitions and enabling actions: | |||
* FG -> ST : ctrl-z | |||
* ST -> FG : fg command | |||
* ST -> BG : bg command | |||
* BG -> FG : fg command | |||
* At most 1 job can be in the FG state. | |||
*/ | |||
/* Global variables */ | |||
extern char **environ; /* defined in libc */ | |||
char prompt[] = "tsh> "; /* command line prompt (DO NOT CHANGE) */ | |||
int verbose = 0; /* if true, print additional output */ | |||
int nextjid = 1; /* next job ID to allocate */ | |||
char sbuf[MAXLINE]; /* for composing sprintf messages */ | |||
struct job_t { /* The job struct */ | |||
pid_t pid; /* job PID */ | |||
int jid; /* job ID [1, 2, ...] */ | |||
int state; /* UNDEF, BG, FG, or ST */ | |||
char cmdline[MAXLINE]; /* command line */ | |||
}; | |||
struct job_t jobs[MAXJOBS]; /* The job list */ | |||
/* End global variables */ | |||
/* Function prototypes */ | |||
/* Here are the functions that you will implement */ | |||
void eval(char *cmdline); | |||
int builtin_cmd(char **argv); | |||
void do_bgfg(char **argv); | |||
void waitfg(pid_t pid); | |||
void sigchld_handler(int sig); | |||
void sigtstp_handler(int sig); | |||
void sigint_handler(int sig); | |||
/* Here are helper routines that we've provided for you */ | |||
int parseline(const char *cmdline, char **argv); | |||
void sigquit_handler(int sig); | |||
void clearjob(struct job_t *job); | |||
void initjobs(struct job_t *jobs); | |||
int maxjid(struct job_t *jobs); | |||
int addjob(struct job_t *jobs, pid_t pid, int state, char *cmdline); | |||
int deletejob(struct job_t *jobs, pid_t pid); | |||
pid_t fgpid(struct job_t *jobs); | |||
struct job_t *getjobpid(struct job_t *jobs, pid_t pid); | |||
struct job_t *getjobjid(struct job_t *jobs, int jid); | |||
int pid2jid(pid_t pid); | |||
void listjobs(struct job_t *jobs); | |||
void usage(void); | |||
void unix_error(char *msg); | |||
void app_error(char *msg); | |||
typedef void handler_t(int); | |||
handler_t *Signal(int signum, handler_t *handler); | |||
/* | |||
* main - The shell's main routine | |||
*/ | |||
int main(int argc, char **argv) | |||
{ | |||
char c; | |||
char cmdline[MAXLINE]; | |||
int emit_prompt = 1; /* emit prompt (default) */ | |||
/* Redirect stderr to stdout (so that driver will get all output | |||
* on the pipe connected to stdout) */ | |||
dup2(1, 2); | |||
/* Parse the command line */ | |||
while ((c = getopt(argc, argv, "hvp")) != EOF) { | |||
switch (c) { | |||
case 'h': /* print help message */ | |||
usage(); | |||
break; | |||
case 'v': /* emit additional diagnostic info */ | |||
verbose = 1; | |||
break; | |||
case 'p': /* don't print a prompt */ | |||
emit_prompt = 0; /* handy for automatic testing */ | |||
break; | |||
default: | |||
usage(); | |||
} | |||
} | |||
/* Install the signal handlers */ | |||
/* These are the ones you will need to implement */ | |||
Signal(SIGINT, sigint_handler); /* ctrl-c */ | |||
Signal(SIGTSTP, sigtstp_handler); /* ctrl-z */ | |||
Signal(SIGCHLD, sigchld_handler); /* Terminated or stopped child */ | |||
/* This one provides a clean way to kill the shell */ | |||
Signal(SIGQUIT, sigquit_handler); | |||
/* Initialize the job list */ | |||
initjobs(jobs); | |||
/* Execute the shell's read/eval loop */ | |||
while (1) { | |||
/* Read command line */ | |||
if (emit_prompt) { | |||
printf("%s", prompt); | |||
fflush(stdout); | |||
} | |||
if ((fgets(cmdline, MAXLINE, stdin) == NULL) && ferror(stdin)) | |||
app_error("fgets error"); | |||
if (feof(stdin)) { /* End of file (ctrl-d) */ | |||
fflush(stdout); | |||
exit(0); | |||
} | |||
/* Evaluate the command line */ | |||
eval(cmdline); | |||
fflush(stdout); | |||
fflush(stdout); | |||
} | |||
exit(0); /* control never reaches here */ | |||
} | |||
/* | |||
* eval - Evaluate the command line that the user has just typed in | |||
* | |||
* If the user has requested a built-in command (quit, jobs, bg or fg) | |||
* then execute it immediately. Otherwise, fork a child process and | |||
* run the job in the context of the child. If the job is running in | |||
* the foreground, wait for it to terminate and then return. Note: | |||
* each child process must have a unique process group ID so that our | |||
* background children don't receive SIGINT (SIGTSTP) from the kernel | |||
* when we type ctrl-c (ctrl-z) at the keyboard. | |||
*/ | |||
void eval(char *cmdline) | |||
{ | |||
/* Init */ | |||
int bg; | |||
char buf[MAXLINE]; | |||
char *argv[MAXARGS]; | |||
pid_t pid; | |||
sigset_t mask; | |||
/* build argv first */ | |||
strcpy(buf, cmdline); | |||
bg = parseline(buf, argv); | |||
if(!argv[0]) /* not found PATH */ | |||
return; | |||
if(!builtin_cmd(argv)) { | |||
/* if builtin, exec and exit */ | |||
sigemptyset(&mask); /* clear the set */ | |||
sigaddset(&mask, SIGCHLD); /* add stop to mask first */ | |||
sigprocmask(SIG_BLOCK, &mask, NULL); /* inorder to block the parent */ | |||
if((pid = fork()) < 0) { | |||
unix_error("forking error"); | |||
} | |||
else if(pid == 0){ | |||
sigprocmask(SIG_UNBLOCK, &mask, NULL); /* set unblock */ | |||
setpgid(0, 0); /* set group pid to 0 */ | |||
/* if command not found, exit */ | |||
if(execvp(argv[0], argv) < 0) { | |||
printf("%s: Command not found.\n", argv[0]); | |||
exit(1); | |||
} | |||
} | |||
/* command found */ | |||
int state = bg ? BG : FG; /* background? foreground? */ | |||
addjob(jobs, pid, state, cmdline); /* exec cmd ok -> job ready to go */ | |||
sigprocmask(SIG_UNBLOCK, &mask, NULL); /* set unblock before exec */ | |||
/* | |||
* From the Internet: 托管线程可以是后台线程,也可以是前台线程。 | |||
* 后台线程和前台线程几乎完全相同,只有一处不同,即后台线程不会确保托管执行环境一直运行。 | |||
* 一旦托管进程(其中 .exe 文件为托管程序集)中的所有前台线程都停止,系统会停止并关闭所有后台线程。 | |||
*/ | |||
if(!bg) /* foreground need to wait other fg */ | |||
waitfg(pid); | |||
else /* background can exec */ | |||
printf("[%d] (%d) %s", pid2jid(pid), pid, cmdline); | |||
} | |||
} | |||
/* | |||
* parseline - Parse the command line and build the argv array. | |||
* | |||
* Characters enclosed in single quotes are treated as a single | |||
* argument. Return true if the user has requested a BG job, false if | |||
* the user has requested a FG job. | |||
*/ | |||
int parseline(const char *cmdline, char **argv) | |||
{ | |||
static char array[MAXLINE]; /* holds local copy of command line */ | |||
char *buf = array; /* ptr that traverses command line */ | |||
char *delim; /* points to first space delimiter */ | |||
int argc; /* number of args */ | |||
int bg; /* background job? */ | |||
strcpy(buf, cmdline); | |||
buf[strlen(buf)-1] = ' '; /* replace trailing '\n' with space */ | |||
while (*buf && (*buf == ' ')) /* ignore leading spaces */ | |||
buf++; | |||
/* Build the argv list */ | |||
argc = 0; | |||
if (*buf == '\'') { | |||
buf++; | |||
delim = strchr(buf, '\''); | |||
} | |||
else { | |||
delim = strchr(buf, ' '); | |||
} | |||
while (delim) { | |||
argv[argc++] = buf; | |||
*delim = '\0'; | |||
buf = delim + 1; | |||
while (*buf && (*buf == ' ')) /* ignore spaces */ | |||
buf++; | |||
if (*buf == '\'') { | |||
buf++; | |||
delim = strchr(buf, '\''); | |||
} | |||
else { | |||
delim = strchr(buf, ' '); | |||
} | |||
} | |||
argv[argc] = NULL; | |||
if (argc == 0) /* ignore blank line */ | |||
return 1; | |||
/* should the job run in the background? */ | |||
if ((bg = (*argv[argc-1] == '&')) != 0) { | |||
argv[--argc] = NULL; | |||
} | |||
return bg; | |||
} | |||
/* | |||
* builtin_cmd - If the user has typed a built-in command then execute | |||
* it immediately. | |||
*/ | |||
int builtin_cmd(char **argv) | |||
{ | |||
/* 1 - a cmd that can be exec, 0 - not */ | |||
if(!argv[0]) { /* void cmd */ | |||
return 1; | |||
} | |||
else if(!strcmp(argv[0], "&")) { | |||
return 1; | |||
} | |||
else if(!strcmp(argv[0], "jobs")) { /* cmd to list jobs */ | |||
listjobs(jobs); | |||
return 1; | |||
} | |||
else if(!strcmp(argv[0], "quit")) { /* cmd to quit, nothing to exec, so return 0 */ | |||
exit(0); | |||
} | |||
else if((!strcmp(argv[0], "bg")) || (!strcmp(argv[0], "fg"))) { /* cmd to exec fg/bg */ | |||
do_bgfg(argv); | |||
return 1; | |||
} | |||
else{ | |||
return 0; /* not a builtin command */ | |||
} | |||
} | |||
/* | |||
* do_bgfg - Execute the builtin bg and fg commands | |||
*/ | |||
void do_bgfg(char **argv) | |||
{ | |||
/* init */ | |||
pid_t pid; | |||
int jid; | |||
struct job_t *job; | |||
if(!argv[1]) { /* have no PID/JID arguments */ | |||
printf("%s command requires PID or %%jobid argument\n", argv[0]); | |||
return; | |||
} | |||
if(*argv[1] == '%') { /* %: need jid */ | |||
jid = atoi(argv[1] + 1); /* get jid */ | |||
if( (job = getjobjid(jobs, jid)) == 0 ) { /* illegal jid */ | |||
printf("%s: No such job\n", argv[1]); | |||
return; | |||
} | |||
pid = job->pid; /* get job and pid */ | |||
} | |||
else if(isdigit(*argv[1])) { /* if get a pid */ | |||
pid = atoi(argv[1]); /* use pid(not %) */ | |||
if( (job = getjobjid(jobs, jid)) == 0 ) { /* illegal pid */ | |||
printf("(%d): No such process\n", pid); | |||
return; | |||
} | |||
} | |||
else { | |||
printf("%s: argument must be a PID or %%jobid\n", argv[0]); | |||
return; | |||
} | |||
if(!job) { /* illegal job */ | |||
printf("Error -- no such job.\n"); | |||
return; | |||
} | |||
if(!strcmp(argv[0], "bg")) { /* if background */ | |||
kill(-pid, SIGCONT); /* SIGCONT -- continue to exec */ | |||
job->state = BG; /* state to BG(background) */ | |||
printf("[%d] (%d) %s", job->jid, pid, job->cmdline); | |||
} | |||
else { /* if foreground */ | |||
kill(-pid, SIGCONT); /* SIGCONT -- continue to exec */ | |||
job->state = FG; /* state to FG(foreground) */ | |||
waitfg(pid); /* need to wait other FGs */ | |||
} | |||
} | |||
/* | |||
* waitfg - Block until process pid is no longer the foreground process | |||
*/ | |||
void waitfg(pid_t pid) | |||
{ | |||
if(pid == 0) | |||
return; | |||
/* | |||
* From the Internet:Thread.Sleep(0) 并非是真的要线程挂起0毫秒,意义在于这次调用Thread.Sleep(0)的当前线程确实的被冻结了一下,让其他线程有机会优先执行。 | |||
* Thread.Sleep(0) 是你的线程暂时放弃cpu,也就是释放一些未用的时间片给其他线程或进程使用,就相当于一个让位动作。 | |||
*/ | |||
while(pid == fgpid(jobs)) | |||
sleep(0); | |||
return; | |||
} | |||
/***************** | |||
* Signal handlers | |||
*****************/ | |||
/* | |||
* sigchld_handler - The kernel sends a SIGCHLD to the shell whenever | |||
* a child job terminates (becomes a zombie), or stops because it | |||
* received a SIGSTOP or SIGTSTP signal. The handler reaps all | |||
* available zombie children, but doesn't wait for any other | |||
* currently running children to terminate. | |||
*/ | |||
void sigchld_handler(int sig) | |||
{ | |||
pid_t pid; | |||
int status; | |||
while ((pid = waitpid(fgpid(jobs), &status, WNOHANG|WUNTRACED)) > 0) { | |||
/* WNOHANG 若pid指定的子进程没有结束,则waitpid()函数返回0,不予以等待。若结束,则返回该子进程的ID。*/ | |||
/* WUNTRACED 若子进程进入暂停状态,则马上返回,但子进程的结束状态不予以理会。WIFSTOPPED(status)宏确定返回值是否对应与一个暂停子进程。*/ | |||
if (WIFSTOPPED(status)){ /* if stopped */ | |||
getjobpid(jobs, pid)->state = ST; /* change state to ST(stopped) */ | |||
int jid = pid2jid(pid); | |||
printf("Job [%d] (%d) Stopped by signal %d\n", jid, pid, WSTOPSIG(status)); | |||
} | |||
else if (WIFSIGNALED(status)){ /* if signaled */ | |||
int jid = pid2jid(pid); /* delete */ | |||
printf("Job [%d] (%d) terminated by signal %d\n", jid, pid, WTERMSIG(status)); | |||
deletejob(jobs, pid); | |||
} | |||
else if (WIFEXITED(status)){ /* if exited */ | |||
deletejob(jobs, pid); | |||
} | |||
else; | |||
} | |||
return; | |||
} | |||
/* | |||
* sigint_handler - The kernel sends a SIGINT to the shell whenver the | |||
* user types ctrl-c at the keyboard. Catch it and send it along | |||
* to the foreground job. | |||
*/ | |||
void sigint_handler(int sig) | |||
{ | |||
pid_t pid; | |||
if( (pid = fgpid(jobs)) <= 0) | |||
return; /* illegal pid */ | |||
kill(-pid, sig); | |||
return; | |||
} | |||
/* | |||
* sigtstp_handler - The kernel sends a SIGTSTP to the shell whenever | |||
* the user types ctrl-z at the keyboard. Catch it and suspend the | |||
* foreground job by sending it a SIGTSTP. | |||
*/ | |||
void sigtstp_handler(int sig) | |||
{ | |||
pid_t pid; | |||
if ((pid = fgpid(jobs)) <= 0) | |||
return; /* illegal pid */ | |||
kill(-pid, sig); | |||
return; | |||
} | |||
/********************* | |||
* End signal handlers | |||
*********************/ | |||
/*********************************************** | |||
* Helper routines that manipulate the job list | |||
**********************************************/ | |||
/* clearjob - Clear the entries in a job struct */ | |||
void clearjob(struct job_t *job) { | |||
job->pid = 0; | |||
job->jid = 0; | |||
job->state = UNDEF; | |||
job->cmdline[0] = '\0'; | |||
} | |||
/* initjobs - Initialize the job list */ | |||
void initjobs(struct job_t *jobs) { | |||
int i; | |||
for (i = 0; i < MAXJOBS; i++) | |||
clearjob(&jobs[i]); | |||
} | |||
/* maxjid - Returns largest allocated job ID */ | |||
int maxjid(struct job_t *jobs) | |||
{ | |||
int i, max=0; | |||
for (i = 0; i < MAXJOBS; i++) | |||
if (jobs[i].jid > max) | |||
max = jobs[i].jid; | |||
return max; | |||
} | |||
/* addjob - Add a job to the job list */ | |||
int addjob(struct job_t *jobs, pid_t pid, int state, char *cmdline) | |||
{ | |||
int i; | |||
if (pid < 1) | |||
return 0; | |||
for (i = 0; i < MAXJOBS; i++) { | |||
if (jobs[i].pid == 0) { | |||
jobs[i].pid = pid; | |||
jobs[i].state = state; | |||
jobs[i].jid = nextjid++; | |||
if (nextjid > MAXJOBS) | |||
nextjid = 1; | |||
strcpy(jobs[i].cmdline, cmdline); | |||
if(verbose){ | |||
printf("Added job [%d] %d %s\n", jobs[i].jid, jobs[i].pid, jobs[i].cmdline); | |||
} | |||
return 1; | |||
} | |||
} | |||
printf("Tried to create too many jobs\n"); | |||
return 0; | |||
} | |||
/* deletejob - Delete a job whose PID=pid from the job list */ | |||
int deletejob(struct job_t *jobs, pid_t pid) | |||
{ | |||
int i; | |||
if (pid < 1) | |||
return 0; | |||
for (i = 0; i < MAXJOBS; i++) { | |||
if (jobs[i].pid == pid) { | |||
clearjob(&jobs[i]); | |||
nextjid = maxjid(jobs)+1; | |||
return 1; | |||
} | |||
} | |||
return 0; | |||
} | |||
/* fgpid - Return PID of current foreground job, 0 if no such job */ | |||
pid_t fgpid(struct job_t *jobs) { | |||
int i; | |||
for (i = 0; i < MAXJOBS; i++) | |||
if (jobs[i].state == FG) | |||
return jobs[i].pid; | |||
return 0; | |||
} | |||
/* getjobpid - Find a job (by PID) on the job list */ | |||
struct job_t *getjobpid(struct job_t *jobs, pid_t pid) { | |||
int i; | |||
if (pid < 1) | |||
return NULL; | |||
for (i = 0; i < MAXJOBS; i++) | |||
if (jobs[i].pid == pid) | |||
return &jobs[i]; | |||
return NULL; | |||
} | |||
/* getjobjid - Find a job (by JID) on the job list */ | |||
struct job_t *getjobjid(struct job_t *jobs, int jid) | |||
{ | |||
int i; | |||
if (jid < 1) | |||
return NULL; | |||
for (i = 0; i < MAXJOBS; i++) | |||
if (jobs[i].jid == jid) | |||
return &jobs[i]; | |||
return NULL; | |||
} | |||
/* pid2jid - Map process ID to job ID */ | |||
int pid2jid(pid_t pid) | |||
{ | |||
int i; | |||
if (pid < 1) | |||
return 0; | |||
for (i = 0; i < MAXJOBS; i++) | |||
if (jobs[i].pid == pid) { | |||
return jobs[i].jid; | |||
} | |||
return 0; | |||
} | |||
/* listjobs - Print the job list */ | |||
void listjobs(struct job_t *jobs) | |||
{ | |||
int i; | |||
for (i = 0; i < MAXJOBS; i++) { | |||
if (jobs[i].pid != 0) { | |||
printf("[%d] (%d) ", jobs[i].jid, jobs[i].pid); | |||
switch (jobs[i].state) { | |||
case BG: | |||
printf("Running "); | |||
break; | |||
case FG: | |||
printf("Foreground "); | |||
break; | |||
case ST: | |||
printf("Stopped "); | |||
break; | |||
default: | |||
printf("listjobs: Internal error: job[%d].state=%d ", | |||
i, jobs[i].state); | |||
} | |||
printf("%s", jobs[i].cmdline); | |||
} | |||
} | |||
} | |||
/****************************** | |||
* end job list helper routines | |||
******************************/ | |||
/*********************** | |||
* Other helper routines | |||
***********************/ | |||
/* | |||
* usage - print a help message | |||
*/ | |||
void usage(void) | |||
{ | |||
printf("Usage: shell [-hvp]\n"); | |||
printf(" -h print this message\n"); | |||
printf(" -v print additional diagnostic information\n"); | |||
printf(" -p do not emit a command prompt\n"); | |||
exit(1); | |||
} | |||
/* | |||
* unix_error - unix-style error routine | |||
*/ | |||
void unix_error(char *msg) | |||
{ | |||
fprintf(stdout, "%s: %s\n", msg, strerror(errno)); | |||
exit(1); | |||
} | |||
/* | |||
* app_error - application-style error routine | |||
*/ | |||
void app_error(char *msg) | |||
{ | |||
fprintf(stdout, "%s\n", msg); | |||
exit(1); | |||
} | |||
/* | |||
* Signal - wrapper for the sigaction function | |||
*/ | |||
handler_t *Signal(int signum, handler_t *handler) | |||
{ | |||
struct sigaction action, old_action; | |||
action.sa_handler = handler; | |||
sigemptyset(&action.sa_mask); /* block sigs of type being handled */ | |||
action.sa_flags = SA_RESTART; /* restart syscalls if possible */ | |||
if (sigaction(signum, &action, &old_action) < 0) | |||
unix_error("Signal error"); | |||
return (old_action.sa_handler); | |||
} | |||
/* | |||
* sigquit_handler - The driver program can gracefully terminate the | |||
* child shell by sending it a SIGQUIT signal. | |||
*/ | |||
void sigquit_handler(int sig) | |||
{ | |||
printf("Terminating after receipt of SIGQUIT signal\n"); | |||
exit(1); | |||
} |