Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add simple pipe syscall and unit test method for linux syscall #132

Merged
merged 6 commits into from
Aug 10, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
ROOTFS_TAR := alpine-minirootfs-3.12.0-x86_64.tar.gz
ROOTFS_URL := http://dl-cdn.alpinelinux.org/alpine/v3.12/releases/x86_64/$(ROOTFS_TAR)

# for linux syscall tests
TEST_DIR := linux-syscall/test/
DEST_DIR := rootfs/bin/
TEST_PATH := $(wildcard $(TEST_DIR)*.c)
BASENAMES := $(notdir $(basename $(TEST_PATH)))

CFLAG := -Wl,--dynamic-linker=/lib/ld-musl-x86_64.so.1

.PHONY: rootfs

prebuilt/linux/$(ROOTFS_TAR):
Expand All @@ -10,3 +18,4 @@ rootfs: prebuilt/linux/$(ROOTFS_TAR)
rm -rf rootfs && mkdir -p rootfs
tar xf $< -C rootfs
cp prebuilt/linux/libc-libos.so rootfs/lib/ld-musl-x86_64.so.1
@for VAR in $(BASENAMES); do gcc $(TEST_DIR)$$VAR.c -o $(DEST_DIR)$$VAR $(CFLAG); done
9 changes: 9 additions & 0 deletions linux-loader/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ mod tests {
proc.wait_signal(Signal::PROCESS_TERMINATED).await;
}

// test using busybox

#[async_std::test]
async fn test_busybox() {
test("/bin/busybox").await;
Expand Down Expand Up @@ -114,4 +116,11 @@ mod tests {
async fn test_env() {
test("/bin/busybox env").await;
}

// syscall unit test

#[async_std::test]
async fn test_pipe() {
test("/bin/testpipe1").await;
}
}
3 changes: 2 additions & 1 deletion linux-object/src/fs/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,8 @@ impl File {
Ok(read_len) => return Ok(read_len),
Err(FsError::Again) => {
//thread::yield_now();
unimplemented!()
//unimplemented!()
self.poll()?;
}
Err(err) => return Err(err.into()),
}
Expand Down
14 changes: 14 additions & 0 deletions linux-object/src/fs/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use rcore_fs_ramfs::RamFS;

pub use self::device::*;
pub use self::file::*;
pub use self::pipe::*;
pub use self::pseudo::*;
pub use self::random::*;
pub use self::stdio::*;
Expand All @@ -21,6 +22,7 @@ use zircon_object::object::*;
mod device;
mod file;
mod ioctl;
mod pipe;
mod pseudo;
mod random;
mod stdio;
Expand Down Expand Up @@ -56,6 +58,12 @@ impl From<usize> for FileDesc {
}
}

impl From<i32> for FileDesc {
fn from(x: i32) -> Self {
FileDesc(x)
}
}

impl TryFrom<&str> for FileDesc {
type Error = LxError;
fn try_from(name: &str) -> LxResult<Self> {
Expand All @@ -70,6 +78,12 @@ impl Into<usize> for FileDesc {
}
}

impl Into<i32> for FileDesc {
fn into(self) -> i32 {
self.0
}
}

pub fn create_root_fs(rootfs: Arc<dyn FileSystem>) -> Arc<dyn INode> {
let rootfs = MountFS::new(rootfs);
let root = rootfs.root_inode();
Expand Down
132 changes: 132 additions & 0 deletions linux-object/src/fs/pipe.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
//! Implement INode for Pipe
#![deny(missing_docs)]

use alloc::{collections::vec_deque::VecDeque, sync::Arc};
use core::{any::Any, cmp::min};
use rcore_fs::vfs::*;
use spin::Mutex;

#[derive(Clone, PartialEq)]
#[allow(dead_code)]
/// Pipe end specify
pub enum PipeEnd {
/// read end
Read,
/// write end
Write,
}

/// Pipe inner data
pub struct PipeData {
/// pipe buffer
buf: VecDeque<u8>,
/// number of pipe ends
end_cnt: i32,
}

/// pipe struct
#[derive(Clone)]
pub struct Pipe {
data: Arc<Mutex<PipeData>>,
direction: PipeEnd,
}

impl Drop for Pipe {
fn drop(&mut self) {
// pipe end closed
let mut data = self.data.lock();
data.end_cnt -= 1;
}
}

#[allow(dead_code)]
impl Pipe {
/// Create a pair of INode: (read, write)
pub fn create_pair() -> (Pipe, Pipe) {
let inner = PipeData {
buf: VecDeque::new(),
end_cnt: 2, // one read, one write
};
let data = Arc::new(Mutex::new(inner));
(
Pipe {
data: data.clone(),
direction: PipeEnd::Read,
},
Pipe {
data,
direction: PipeEnd::Write,
},
)
}
/// whether the pipe struct is readable
fn can_read(&self) -> bool {
if let PipeEnd::Read = self.direction {
// true
let data = self.data.lock();
!data.buf.is_empty() || data.end_cnt < 2 // other end closed
} else {
false
}
}

/// whether the pipe struct is writeable
fn can_write(&self) -> bool {
if let PipeEnd::Write = self.direction {
self.data.lock().end_cnt == 2
} else {
false
}
}
}

impl INode for Pipe {
/// read from pipe
fn read_at(&self, _offset: usize, buf: &mut [u8]) -> Result<usize> {
if buf.is_empty() {
return Ok(0);
}
if let PipeEnd::Read = self.direction {
let mut data = self.data.lock();
if data.buf.is_empty() && data.end_cnt == 2 {
Err(FsError::Again)
} else {
let len = min(buf.len(), data.buf.len());
for item in buf.iter_mut().take(len) {
*item = data.buf.pop_front().unwrap();
}
Ok(len)
}
} else {
Ok(0)
}
}

/// write to pipe
fn write_at(&self, _offset: usize, buf: &[u8]) -> Result<usize> {
if let PipeEnd::Write = self.direction {
let mut data = self.data.lock();
for c in buf {
data.buf.push_back(*c);
}
Ok(buf.len())
} else {
Ok(0)
}
}

/// monitoring events and determine whether the pipe is readable or writeable
/// if the write end is not close and the buffer is empty, the read end will be block
fn poll(&self) -> Result<PollStatus> {
Ok(PollStatus {
read: self.can_read(),
write: self.can_write(),
error: false,
})
}

/// return the any ref
fn as_any_ref(&self) -> &dyn Any {
self
}
}
74 changes: 38 additions & 36 deletions linux-syscall/src/file/fd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
//! - pipe

use super::*;
use alloc::string::String;

impl Syscall<'_> {
/// Opens or creates a file, depending on the flags passed to the call. Returns an integer with the file descriptor.
Expand Down Expand Up @@ -73,42 +74,43 @@ impl Syscall<'_> {
Ok(fd2.into())
}

// pub fn sys_pipe(&self, fds: *mut u32) -> SysResult {
// info!("pipe: fds={:?}", fds);
//
// let proc = self.process();
// let fds = unsafe { self.vm().check_write_array(fds, 2)? };
// let (read, write) = Pipe::create_pair();
//
// let read_fd = proc.add_file(FileLike::File(File::new(
// Arc::new(read),
// OpenOptions {
// read: true,
// write: false,
// append: false,
// nonblock: false,
// },
// String::from("pipe_r:[]"),
// )));
//
// let write_fd = proc.add_file(FileLike::File(File::new(
// Arc::new(write),
// OpenOptions {
// read: false,
// write: true,
// append: false,
// nonblock: false,
// },
// String::from("pipe_w:[]"),
// )));
//
// fds[0] = read_fd as u32;
// fds[1] = write_fd as u32;
//
// info!("pipe: created rfd={} wfd={}", read_fd, write_fd);
//
// Ok(0)
// }
/// Creates a pipe, a unidirectional data channel that can be used for interprocess communication.
pub fn sys_pipe(&self, mut fds: UserOutPtr<[i32; 2]>) -> SysResult {
info!("pipe: fds={:?}", fds);

let proc = self.linux_process();
let (read, write) = Pipe::create_pair();

let read_fd = proc.add_file(File::new(
Arc::new(read),
OpenOptions {
read: true,
write: false,
append: false,
nonblock: false,
},
String::from("pipe_r:[]"),
))?;

let write_fd = proc.add_file(File::new(
Arc::new(write),
OpenOptions {
read: false,
write: true,
append: false,
nonblock: false,
},
String::from("pipe_w:[]"),
))?;
fds.write([read_fd.into(), write_fd.into()])?;

info!(
"pipe: created rfd={:?} wfd={:?} fds={:?}",
read_fd, write_fd, fds
);

Ok(0)
}
}

bitflags! {
Expand Down
4 changes: 2 additions & 2 deletions linux-syscall/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ impl Syscall<'_> {
Sys::FCHOWNAT => self.unimplemented("fchownat", Ok(0)),
Sys::FACCESSAT => self.sys_faccessat(a0.into(), a1.into(), a2, a3),
Sys::DUP3 => self.sys_dup2(a0.into(), a1.into()), // TODO: handle `flags`
// Sys::PIPE2 => self.sys_pipe(a0.into()), // TODO: handle `flags`
Sys::PIPE2 => self.sys_pipe(a0.into()), // TODO: handle `flags`
Sys::UTIMENSAT => self.unimplemented("utimensat", Ok(0)),
Sys::COPY_FILE_RANGE => {
self.sys_copy_file_range(a0.into(), a1.into(), a2.into(), a3.into(), a4, a5)
Expand Down Expand Up @@ -245,7 +245,7 @@ impl Syscall<'_> {
Sys::LSTAT => self.sys_lstat(a0.into(), a1.into()),
// Sys::POLL => self.sys_poll(a0.into(), a1, a2),
Sys::ACCESS => self.sys_access(a0.into(), a1),
// Sys::PIPE => self.sys_pipe(a0.into()),
Sys::PIPE => self.sys_pipe(a0.into()),
// Sys::SELECT => self.sys_select(a0, a1.into(), a2.into(), a3.into(), a4.into()),
Sys::DUP2 => self.sys_dup2(a0.into(), a1.into()),
// Sys::ALARM => self.unimplemented("alarm", Ok(0)),
Expand Down
38 changes: 38 additions & 0 deletions linux-syscall/test/testpipe1.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#include<sys/types.h>
#include<unistd.h>
#include<stdio.h>
#include<string.h>
#include <stdlib.h>

int main()
{
pid_t pid;
int cnt = 0;
int pipefd[2];
char buf;
char w[12];
char r[12];
if (pipe(pipefd) == -1) {
printf("pipe");
exit(-1);
}
sprintf(w,"%d",pipefd[1]);
sprintf(r,"%d",pipefd[0]);
pid = vfork();
if(pid<0)
printf("error in fork!\n");
else if(pid == 0)
{
execl("/bin/testpipe2","/bin/testpipe2",r,w,NULL);
exit(0);
}
else if(pid > 0)
{
close(pipefd[1]);
while (read(pipefd[0], &buf, 1) > 0)
write(STDOUT_FILENO, &buf, 1);
write(STDOUT_FILENO, "\n", 1);
close(pipefd[0]);
}
return 0;
}
16 changes: 16 additions & 0 deletions linux-syscall/test/testpipe2.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

int main(int argc, char *argv[])
{
int writefd, readfd;
sscanf(argv[2],"%d",&writefd);
sscanf(argv[1],"%d",&readfd);
close(readfd);
write(writefd, "hello pipe", strlen("hello pipe"));
close(writefd);
exit(0);
}