diff --git a/README.md b/README.md new file mode 100644 index 0000000..de3326a --- /dev/null +++ b/README.md @@ -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. \ No newline at end of file diff --git a/bits.c b/bits.c new file mode 100644 index 0000000..4835b5e --- /dev/null +++ b/bits.c @@ -0,0 +1,456 @@ +/* + * CS:APP Data Lab + * + * + * + * 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 header; it confuses the dlc + * compiler. You can still use printf for debugging without including + * , 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; +} + /* + * 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. +} \ No newline at end of file diff --git a/mm.c b/mm.c new file mode 100644 index 0000000..303c223 --- /dev/null +++ b/mm.c @@ -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 +#include +#include +#include +#include + +#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; +} \ No newline at end of file diff --git a/tsh.c b/tsh.c new file mode 100644 index 0000000..5bcd1fc --- /dev/null +++ b/tsh.c @@ -0,0 +1,659 @@ +/* + * tsh - A tiny shell program with job control + * + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* 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); +} \ No newline at end of file