1、C语言无论做任何事情都需要操作系统,如果想与硬件相连,就要进行系统调用。
2、系统调用是操作系统内核中的函数。
3、system()接收一个字符串参数,并把它当成命令执行。
4、system()不会编译到程序中,只会在操作系统内。
5、安全问题:
echo '&& ls / && echo' <timestamp>' >> reports.log
如果用户运行了该命令,就可以随心所欲的在计算机上运行任何命令。
6、什么是内核:
在大部分计算机上,系统调用就是操作系统内核中的函数,内核一直在控制计算机。内核是计算机中最重要的程序,主管三样东西:
(1)进程
只有当内核把程序加载到存储器时程序才能运行。内核创建进程,并确保它们得到了所需资源。内核同时也会留意那些变得贪得无厌或者已经崩溃的进程。
(2)存储器
计算机所能提供的存储器资源是有限的,因此内核必须小心翼翼地分配每个进程所能使用的存储器大小。内核还能把部分存储器交换到磁盘从而增加虚拟存储空间。
(3)硬件
内核利用设备驱动与连接到计算机上的设备交互。程序可以在不了解键盘、屏幕和图形处理器的情况下就能使用它们,因为内核会与它们交涉。
系统调用是程序用来与内核对话的函数。
7、当使用system()函数时,操作系统必须解释命令字符串,然后决定运行哪些程序和怎样运行,这个过程容易出错,可以使用exec()函数。
8、进程是存储器中运行的程序,在Windows中输入taskmgr或在Liunx或Mac输入pc -ef就可以看系统中的进程。操作系统用一个数字标示进程,叫做进程标识符。
9、exec()函数在unistd.h中。分为列表函数和数组函数两类。
10、列表函数(execl、execlp和execle):
(1)程序:第一个参数告诉exec()函数将运行什么程序。execl和execle它是完整的程序路径名,而execlp算是命令的名字
(2)命令行参数:需要依次列出想使用的命令行参数。第一个必须是程序名。
(3)NULL:需要在最后一个参数后加上NULL。
(4)环境变量:调用了以e结尾的exec函数,可以传递环境变量。
execl("/home/flynn/clu","/home/flynn/clu","paranoids","contract",NULL)
execlp("clu","clu","paranoids","contract",NULL)
execle("/home/flynn/clu","/home/flynn/clu","paranoids","contract",NULL,env_vars)
env_vars是一个字符串数组,里面放了环境变量。
11、数组函数(execvl、execvp和execve):
execv(“/home/flynn/clu",my_args);
execvp("clu",my_args);
12、失败黄金法则:尽可能收拾残局;把error变量设为错误码;返回-1.
13、由于系统调用依赖程序以外的东西,一旦出错就没办法控制。为避免总以相同方式出错,并遵循失败黄金法则。
14、错误码:
15、string.h库中strerror()函数查询标准错误消息:
puts(strerror(errno));
系统找不到想运行的程序时会把errno变量设置为ENOENT,以上代码会显示该消息:
没有文件或目录
16、为什么一定要检查系统调用的返回值?
如果在进行系统调用时不检查错误,代码是短了,但是可能引发更多错误。
17、调用exec函数后不能做其他事,因为该代码执行成功后会修改进程,用新程序替代旧程序,也就是说,该函数一运行,旧程序就会停止运行。
18、exec是程序中最后一行代码。
19、如果想要启动另一个进程的同时让原进程继续运行需要使用fork函数克隆进程,fork会克隆当前进程,新建副本将从同一行开始运行相同程序,变量和变量中的值完全一样,只有进程标识符(PID)和原进程不同。Windows天生不支持fork,所有想要在Windows中使用,必须按照Cygwin。
20、原进程叫父进程,新建副本叫子进程。
21、在子进程中调用exec函数,父进程就可以继续运行了。先用fork复制当前进程,子进程返回0,父进程返回非零值。
22、调用fork:
pid_t pid = fork();
23、为了避免复制进程影响程序运行速度,并不会真的复制父进程数据,而是让父子进程共享数据。
24、当子进程尝试修改存储器中的数据时,操作系统会为它复制一份,这叫做写时复制(copy-on-write)