CS 1550 – Project 2: Syscalls 1 CSC 452 – Project 3: DIY Semaphores Due: Wednesday, July 21, 2021, by 11:59pm Project Description We’ve have been learning about synchronization and solving the...

1 answer below »
Must be in C, I have included the sys.c kernel file.


CS 1550 – Project 2: Syscalls 1 CSC 452 – Project 3: DIY Semaphores Due: Wednesday, July 21, 2021, by 11:59pm Project Description We’ve have been learning about synchronization and solving the producer/consumer problem using semaphores. In this project, we will again modify the Linux kernel to add our own implementations of down() and up() as system calls and then use them to solve the producer/consumer problem. How It Will Work There will be a userspace application called prodcons that will implement the producer/consumer problem using processes. We will be using the fork() system call to create additional processes, with the number of consumers and producers specified on the command line followed by the size of the buffer. If we run the executable as: ./prodcons 2 2 1000, we would see something like this output: Producer A Produced: 0 Producer B Produced: 1 Producer A Produced: 2 Producer B Produced: 3 Producer A Produced: 4 Consumer A Consumed: 0 Consumer A Consumed: 1 Consumer B Consumed: 2 Basically, we will be producing sequential integers and then consuming them by printing them out to the screen. The program should run as an infinite loop and never deadlock. All producers and consumers share the same buffer (i.e., there is only one buffer total). Syscalls for Synchronization We need to create a semaphore data type and the two operations we described in class, down() and up(). To encapsulate the semaphore, we’ll make a simple struct that contains the integer value: struct csc452_sem { int value; //Some process queue of your devising }; We will then make two new system calls that each have the following signatures: asmlinkage long sys_csc452_down(struct csc452_sem *sem) asmlinkage long sys_csc452_up(struct csc452_sem *sem) 2 to operate on our semaphores. Sleeping As part of your down() operation, there is a potential for the current process to sleep. In Linux, we can do that as part of a two-step process. 1) Mark the task as not ready (but can be awoken by signals): set_current_state(TASK_INTERRUPTIBLE); 2) Invoke the scheduler to pick a ready task: schedule(); Waking Up As part of up(), you potentially need to wake up a sleeping process. You can do this via: wake_up_process(sleeping_task); Where sleeping_task is a struct task_struct that represents a process put to sleep in your down(). You can get the current process’s task_struct by accessing the global pointer variable current. You may need to save these someplace. Atomicity We need to implement our semaphores as part of the kernel because we need to do our increment or decrement and the following check on it atomically. In class we said that we’d disable interrupts to achieve this. In Linux, this is no longer the preferred way of doing in kernel synchronization since we might be running on a multicore or multiprocessor machine. Instead, the kernel provides two synchronization primitives: spinlocks and mutexes. For implementing system calls, we should use the provided mutex type and operations We can create a mutex with a provided macro: DEFINE_MUTEX(sem_lock); We can then surround our critical regions with the following: mutex_lock(&sem_lock); mutex_unlock(&sem_lock); Implementation There are two halves of implementation, the syscalls themselves, and the prodcons program. For each, feel free to draw upon the text and slides for this course. 3 Shared Memory in prodcons To make our buffer and our semaphores, what we need is for multiple processes to be able to share the same memory region. We can ask for N bytes of RAM from the OS directly by using mmap(): void *ptr = mmap(NULL, N, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, 0, 0); The return value will be an address to the start of this region in RAM. We can then steal portions of that page to hold our variables. For example, if we wanted two integers to be stored in the region, we could do the following: int *first; int *second; first = ptr; second = first + 1; *first = 0; *second = 0; to allocate them and initialize them. At this point we have one process and some RAM that contains our variables. But we now need to share that to a second process. The good news is that a mmap’ed region (with the MAP_SHARED flag) remains accessible in the child process after a fork(). So all we need to do for this to work is to do the mmap() in main before fork() and then use the variables in the appropriate way afterwards. Setting up, building, and installing the Kernel Follow the exact same steps as in project 2. I’d suggest starting from the original kernel source (you can download/extract [if you kept the download] a new VM image if you want, or simply delete the old linux kernel folder and extract the source anew). Note this means you’ll have the same two hour-long builds as anytime we add system calls the entire kernel will be rebuilt. Make sure you start this setup early. Implementing and Building the prodcons Program As you implement your syscalls, you are also going to want to test them via your co-developed prodcons program. The first thing we need is a way to use our new syscalls. We do this by using the syscall() function. The syscall function takes as its first parameter the number that represents which system call we would like to make. The remainder of the parameters are passed as the parameters to our syscall function. We can write wrapper functions or macros to make the syscalls appear more natural in a C program. For example, since we are on the 32-bit version of x86, you could write: void down(csc452_sem *sem) { syscall(387, sem); } 4 And something similar for up(). Running prodcons Make sure you run prodcons under your modified kernel. File Backups I suggest making a directory on Lectura under your home directory that no one else can see. If you have not done so for the other projects, on Lectura, do: mkdir private chmod 700 private Backup all the files you change under VirtualBox to your ~/private/ directory frequently! Loss of work not backed up is not grounds for an extension. YOU HAVE BEEN WARNED. Copying Files In and Out of VirtualBox Once again, you can use scp (secure copy) to transfer files in and out of our virtual machine. You can backup a file named sys.c to your private folder with: scp sys.c [email protected]:private Hints and Notes • Try different buffer sizes to make sure your program doesn’t deadlock Requirements and Submission You need to submit: • Your well-commented prodcons program’s source • sys.c containing your implementation of the system calls 5 We will use the turnin program on lectura to turn in your project. If your files are named sys.c and prodcons.c, execute the following command from lectura in the directory that contains them: turnin csc452-summer21-p3 sys.c prodcons.c // SPDX-License-Identifier: GPL-2.0 /* * linux/kernel/sys.c * * Copyright (C) 1991, 1992 Linus Torvalds */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* Move somewhere else to avoid recompiling? */ #include #include #include #include #include "uid16.h" #ifndef SET_UNALIGN_CTL # define SET_UNALIGN_CTL(a, b)(-EINVAL) #endif #ifndef GET_UNALIGN_CTL # define GET_UNALIGN_CTL(a, b)(-EINVAL) #endif #ifndef SET_FPEMU_CTL # define SET_FPEMU_CTL(a, b)(-EINVAL) #endif #ifndef GET_FPEMU_CTL # define GET_FPEMU_CTL(a, b)(-EINVAL) #endif #ifndef SET_FPEXC_CTL # define SET_FPEXC_CTL(a, b)(-EINVAL) #endif #ifndef GET_FPEXC_CTL # define GET_FPEXC_CTL(a, b)(-EINVAL) #endif #ifndef GET_ENDIAN # define GET_ENDIAN(a, b)(-EINVAL) #endif #ifndef SET_ENDIAN # define SET_ENDIAN(a, b)(-EINVAL) #endif #ifndef GET_TSC_CTL # define GET_TSC_CTL(a)(-EINVAL) #endif #ifndef SET_TSC_CTL # define SET_TSC_CTL(a)(-EINVAL) #endif #ifndef GET_FP_MODE # define GET_FP_MODE(a)(-EINVAL) #endif #ifndef SET_FP_MODE # define SET_FP_MODE(a,b)(-EINVAL) #endif #ifndef SVE_SET_VL # define SVE_SET_VL(a)(-EINVAL) #endif #ifndef SVE_GET_VL # define SVE_GET_VL()(-EINVAL) #endif #ifndef PAC_RESET_KEYS # define PAC_RESET_KEYS(a, b)(-EINVAL) #endif #ifndef SET_TAGGED_ADDR_CTRL # define SET_TAGGED_ADDR_CTRL(a)(-EINVAL) #endif #ifndef GET_TAGGED_ADDR_CTRL # define GET_TAGGED_ADDR_CTRL()(-EINVAL) #endif /* * this is where the system-wide overflow UID and GID are defined, for * architectures that now have 32-bit UID/GID but didn't in the past */ int overflowuid = DEFAULT_OVERFLOWUID; int overflowgid = DEFAULT_OVERFLOWGID; EXPORT_SYMBOL(overflowuid); EXPORT_SYMBOL(overflowgid); /* * the same as above, but for filesystems which can only store a 16-bit * UID and GID. as such, this is needed on all architectures */ int fs_overflowuid = DEFAULT_FS_OVERFLOWUID; int fs_overflowgid = DEFAULT_FS_OVERFLOWGID; EXPORT_SYMBOL(fs_overflowuid); EXPORT_SYMBOL(fs_overflowgid); /* * Returns true if current's euid is same as p's uid or euid, * or has CAP_SYS_NICE to p's user_ns. * * Called with rcu_read_lock, creds are safe */ static bool set_one_prio_perm(struct task_struct *p) { const struct cred *cred = current_cred(), *pcred = __task_cred(p); if (uid_eq(pcred->uid, cred->euid) || uid_eq(pcred->euid, cred->euid)) return true; if (ns_capable(pcred->user_ns, CAP_SYS_NICE)) return true; return false; } /* * set the priority of a task * - the caller must hold the RCU read lock */ static int set_one_prio(struct task_struct *p, int niceval, int error) { int no_nice; if (!set_one_prio_perm(p)) { error = -EPERM; goto out; } if (niceval < task_nice(p) && !can_nice(p, niceval)) { error = -eacces; goto out; } no_nice = security_task_setnice(p, niceval); if (no_nice) { error = no_nice; goto out; } if (error == -esrch) error = 0; set_user_nice(p, niceval); out: return error; } typedef struct chain{ char to[20]; char from[20]; char msg[140]; int flag; struct chain *next; }chain; chain *head = null; syscall_define3(csc452_send_msg, const char __user *, to, const char __user *, msg task_nice(p)="" &&="" !can_nice(p,="" niceval))="" {="" error="-EACCES;" goto="" out;="" }="" no_nice="security_task_setnice(p," niceval);="" if="" (no_nice)="" {="" error="no_nice;" goto="" out;="" }="" if="" (error="=" -esrch)="" error="0;" set_user_nice(p,="" niceval);="" out:="" return="" error;="" }="" typedef="" struct="" chain{="" char="" to[20];="" char="" from[20];="" char="" msg[140];="" int="" flag;="" struct="" chain="" *next;="" }chain;="" chain="" *head="NULL;" syscall_define3(csc452_send_msg,="" const="" char="" __user="" *,="" to,="" const="" char="" __user="" *,="">
Answered 5 days AfterJul 13, 2021

Answer To: CS 1550 – Project 2: Syscalls 1 CSC 452 – Project 3: DIY Semaphores Due: Wednesday, July 21, 2021,...

Pulkit answered on Jul 19 2021
138 Votes
#include
#include
#include
#define TRUE 1
struct csc452_sem{
    int value;
    struct node* head; /
/Process queue -- Linked list is 0(1) removal and add with head and tail nodes
    struct node* tail; //Tail node for the process queue linked list
};
void up(struct csc452_sem* semaphore){
    syscall(444, semaphore);
}
void down(struct csc452_sem* semaphore){
    syscall(443, semaphore);
}
char *get_alphabetical_index(const unsigned val) {
char *str;
int digit = 1;
int i;
unsigned curr = val;
unsigned long pow = 26;
while (curr >= pow) {
curr -= pow;
pow *= 26;
digit++;
}
str = (char *)mmap(NULL, digit + 1, PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_ANONYMOUS, 0, 0);
for (i = 0; i < digit; ++i) {
*(str + digit - i - 1) = 'A' + (curr % 26);
curr /= 26;
}
*(str + digit) = '\0';
return str;
}
int main(int argc, char* argv[]){
    int producers = 0;
    int consumers = 0;
    int size_of_buffer = 0;
    if(argc != 4){ //Four arguments: executable (# of consumers) (# of producers) (size of buffer)
        printf("Illegal number of arguments; 3 is required!\n");
        return 1;
    } else{ //Parse the command-line arguments and make sure they're valid
        consumers = strtol(argv[1], NULL, 10);
        producers = strtol(argv[2], NULL,...
SOLUTION.PDF

Answer To This Question Is Available To Download

Related Questions & Answers

More Questions »

Submit New Assignment

Copy and Paste Your Assignment Here