ls命令的Go语言实现
package main
import (
"fmt"
"os"
)
func main() {
if len(os.Args) != 2 {
panic("参数数量不足")
}
targetPath := os.Args[1]
if dirList, err := os.ReadDir(targetPath); err == nil {
for _, dirInfo := range dirList {
fmt.Println(dirInfo.Name())
}
} else {
fmt.Println(err.Error())
}
}
从标准输入复制到标准输出
package main
import (
"bufio"
"os"
"time"
)
func main() {
reader := bufio.NewReader(os.Stdin)
message, _ := reader.ReadString('\n')
os.Stdout.WriteString(message)
time.Sleep(time.Second)
}
打印进程的pid
package main
import (
"fmt"
"os"
)
func main() {
fmt.Printf("hello world from process ID %d\n", os.Getpid())
}
使用fork创建子进程并查看运行结果
package main
import (
"fmt"
"os"
"os/exec"
"syscall"
)
func main() {
bar := 2
foo := "demo"
id, _, _ := syscall.Syscall(syscall.SYS_FORK, 0, 0, 0)
if id == 0 {
bar++
cmd := exec.Command("ls")
out, _ := cmd.CombinedOutput()
fmt.Printf("ls output: %s\n", string(out))
fmt.Printf("in child: %d, bar: %v, foo: %v\n", os.Getpid(), bar, foo)
} else {
foo += "hello"
fmt.Printf("in parent: %d, bar: %v, foo: %v\n", os.Getpid(), bar, foo)
}
}
获取uid与gid
package main
import (
"fmt"
"os"
)
func main() {
fmt.Printf("uid = %d, gid = %d\n", os.Getuid(), os.Getgid())
}
信号signal
中断键 interrupt key, 通常是delete键或者ctrl+c和退出键ctrl+.他们用于中断当前进程.另一种产生信号的方式是调用名为kill的函数.
package main
import (
"fmt"
"os"
"os/exec"
"os/signal"
"syscall"
"time"
)
func main() {
bar := 2
foo := "demo"
c := make(chan os.Signal, 1)
signal.Notify(c, syscall.SIGINT)
go func() {
for sig := range c {
switch sig {
case syscall.SIGINT:
fmt.Printf("interrupt no: %d\n", syscall.SIGINT)
os.Exit(-1)
}
}
}()
time.Sleep(5 * time.Second)
id, _, _ := syscall.Syscall(syscall.SYS_FORK, 0, 0, 0)
if id == 0 {
bar++
cmd := exec.Command("ls")
out, _ := cmd.CombinedOutput()
fmt.Printf("ls output: %s\n", string(out))
fmt.Printf("in child: %d, bar: %v, foo: %v\n", os.Getpid(), bar, foo)
} else {
foo += "hello"
fmt.Printf("in parent: %d, bar: %v, foo: %v\n", os.Getpid(), bar, foo)
}
}
UNIX时间值
UNIX中有两种时间
- 日历时间, 从1970年1月1日00:00:00所经过的秒数累计值.这些时间可用于记录文件最近一次的修改时间等.
- 进程时间.也成为CPU时间, 用以度量进程使用的cpu资源.进程时间以tick记录.
当度量一个进程的执行时间时, UNIX系统使用三个进程时间值:
- 时钟时间
- 用户CPU时间
- 系统CPU时间
时钟时间又称为墙上时间(wall clock time). 它是进程运行的时间总量, 其值与系统中同时运行的进程数有关. 在我们报告时钟时间时, 都是在系统中没有其他活动时进行度量的.
用户时间是执行用户指令所用的时间量.
系统cpu时间是为该进程执行内核所经历的时间.
例如,只有一个进程执行一个系统服务, 如read或write, 则在内核内执行该服务所花费的时间就计入该进程的系统cpu时间.
用户cpu时间与系统cpu时间的和常被称为CPU时间.
要得到一个程序运行的时钟时间, 用户时间和系统时间很容易, 使用time(1)命令即可.
$ cd /usr/include
$ time grep _POSIX_SOURCE */*.h > /dev/null
real 0m19.81s
user 0m0.43s
sys 0m4.53s
time函数的输出格式与所使用的shell有关系.
系统调用和库函数
系统调用和库函数之间有重大区别, 但从用户角度看, 其区别并不非常重要.
本书的系统调用和库函数都是以c函数的形式存在.
但我们应当理解, 如果希望的话, 我们可以替换库函数, 但是通常不能替换系统调用.
以内存分配函数malloc为例, 有很多方法可以进行内存管理.如果不喜欢这样的操作方法, 可以定义自己的malloc函数, 它可能将使用sbrk系统调用.
事实上,有很多软件包, 她们实现自己的存储器分配算法, 但仍然使用sbrk系统调用.
下图显示了应用程序, malloc函数和sbrk系统调用的关系
从图中可见, 两者职责不同,相互分开, 内核中的系统调用分配另外一块空间给进程, 而库函数malloc则管理这一空间.
另外一个可说明系统调用和库函数区别的例子是, UNIX提供决定当前时间和日期的接口.某些操作系统提供一个系统调用以返回时间, 而另一个则返回日期.
任何特殊的处理, 例如正常时制与夏令时的转换, 由内核处理或要求人为干预.
UNIX则不同, 它只提供一个系统调用, 该系统调用返回国际标准时间1970年1月1日0点以来所经过的秒数.对该值的任何解释, 如将其变换为人们可读的, 使用本地时区的时间和日期, 都留给用户进程进行. 在标准c库中, 提供了若干函数来处理大多数情况. 这些库函数处理各种细节, 例如各种夏令时算法.
应用程序可以调用系统调用或库函数, 而很多库函数则会调用系统调用.
系统调用与库函数的另一个差别是: 系统调用通常提供了一种最小接口, 而库函数通常提供比较复杂的功能. 我们从sbrk系统调用和malloc库函数之间的差别中可以看到这一点.
以后比较不带缓冲的IO函数(第三章)与标准IO函数(第五章)时, 还将看到这种差别.
进程控制系统调用(fork,exec和wait)通常由用户的应用程序直接调用. 但为了简化某些常见情况, UNIX系统也提供了一些库函数, 如system和popen.
为了使读者了解大多数程序员应用的unix系统接口, 我们不得不既说明系统调用, 还要介绍某些库函数.
例如若只说明sbrk系统调用, 那么就会忽略很多应用程序使用的malloc库函数.
小结
本节快速浏览了UNIX, 说明了某些以后会多次用到的基本术语.
下一节是关于UNIX标准化的内容, 以及这方面的工作对当前系统的影响.标准, 特别是ANSI C标准和POSIX.1标准将影响本书的余下部分.
课后问题
日历时间存放在有符号32位整型中, 什么时候会溢出(2038年溢出)
如果进程时间存在32位整型数中, 每秒100tick, 经过多少天溢出(248天)
package main
import "fmt"
func main() {
var n int32
n = 2147483646
for i := 0; i < 5; i++ {
fmt.Println("n:", n)
n += 1
}
}