c语言初始化字符串 函数 manment,[转载]3.09进程(C语言班最后一天的课程)
1,进程:是容器,是内存上的概念。线程是CPU的概念。
2,fork的作用是根据一个现有的进程复制出一个新进程,原来的进程称为父进程(Parents Process),新进程称为子进程(Child Process)。
系统中同时运行着许多进程,这些进程都是从最初只有一个进程开始一个一个复制出来的。
分进程函数fork(),用来开辟子进程。执行fork()之后,会有两个进程,一个是原来的那个,一个是新生成的子进程。这两个进程都会执行程序下面的代码。pid = fork();pid得到的是返回子进程的id,不是自己的id。
1.c
#include
#include
#include
#include
int main(void)
{
pid_t pid = fork();
if(pid < 0){
perror("fork");
exit(0);
}
if(pid > 0){
printf("Parents!n");
}
if(0 == pid){
printf("Child!n");
}
printf("Hello world!n");
return 0;
}
3,先让父进程睡1秒,会先执行子进程。
2.c
#include
#include
#include
#include
int main(void)
{
pid_t pid = fork();
if(pid < 0){
perror("fork");
exit(0);
}
if(0 == pid){
printf("Child!n");
}else{
sleep(1);
printf("Parents!n");
}
return 0;
}
如果父进程结束后,sleep(1);子进程会打印到终端。
3.c
#include
#include
#include
#include
int main(void)
{
pid_t pid = fork();
if(pid < 0){
perror("fork");
exit(0);
}
if(0 == pid){
sleep(1);
printf("Son!n");
}else{
printf("Father!n");
}
return 0;
}
4,进程号:pid 。getppid()是得到当前进程父进程的pid;getpid()是得到当前进程的pid。
如果子进程getppid()时,父进程已经死掉,子进程就会被托管,那getppid()的值就为1。
所以父进程要sleep(1),这样子进程getppid()时,得到的就是此进程父进程的pid。
父进程getppid()时,得到的是终端的pid。
4.c
#include
#include
#include
#include
int main(void)
{
pid_t pid = fork();
if(pid < 0){
perror("fork");
exit(0);
}
if(0 == pid){
printf("Child!ngetppid=%d getpid=%d pid=%dn", getppid(),
getpid(), pid);
}else{
printf("Parents!ngetppid=%d getpid=%d pid=%dn", getppid(),
getpid(), pid);
sleep(1);
}
return 0;
}
5,ps查看进程;
ps -jas 查看所有的进程。
6,探讨怎么让父进程知道子进程结束?
exit(0)不会结束整个程序,只会结束一个进程。wait(NULL)等待自己子进程的结束。
父进程执行到wait()时,会进入休眠,子进程结束时,会发一个信号,wait()会捕捉到这个信号,以此来唤醒父进程。
5.c
#include
#include
#include
#include
int main(void)
{
pid_t pid = fork();
if(pid < 0){
perror("fork");
exit(0);
}
if(0 == pid){
sleep(3);
printf("Child!n");
exit(0);
}
wait(NULL);
printf("Parents!n");
return 0;
}
7,探讨父进程与子进程关于变量使用的问题?
在fork()之后,会把原来的内存完全复制一份,但不管怎么样更改,都只会各改各的,互不干扰。
6.c
#include
#include
#include
#include
int main(void)
{
int a = 5;
int * p = &a;
pid_t pid = fork();
if(pid < 0){
perror("fork");
exit(0);
}
if(pid > 0){
*p = 6;
printf("Parents:%dn", *p);
}else{
printf("Child:%dn", *p);
}
return 0;
}
8,怎么样杀死进程?
下面的程序是个死循环,只能重新开一个终端,执行“kill -9 进程号”。
如果执行以下命令,一定要删除交换文件:
$vi 5.c //进入5.c 此进程进程号为3456
$Ctrl + z
//暂停当前程序运行
$kill -9 3456 //杀死vi 5.c,这个进程
$fg
//回到刚暂停的程序中,会报错,因为那个进程已经被杀死了。要删除那个交换文件才能执行这个操作。
$ls -a//查看所有文件,能把交换文件查看出来。
$rm -fr ..5.c.swap. //删除交换文件。
$fg
7.c
#include
#include
#include
#include
int main(void)
{
pid_t pid = fork();
if(pid < 0){
perror("fork");
exit(0);
}
if(0 == pid){
while(1){
printf("Child!n");
}
}else{
printf("Parents!n");
}
return 0;
}
9,exec族:用来代替原来进程,执行的时候完全代替,不会再回来。
#include
所需头文件
(1)execl("/bin/ls", "ls", "-l",
NULL); 后面一定要加NULL,表示参数传完了。
int
execl(const char *path, const char *arg0, ... )
8.c
#include
#include
#include
#include
int main(void)
{
int ret = execl("/bin/ls", "ls", "-l", NULL);
if(ret < 0){
perror("execl!n");
exit(0);
}
printf("Hello world!n");
return 0;
}
(2)
execlp已经安装过的程序,不需要写路径。
int
execlp(const char *file, const char *arg0, ... );
9.c
#include
#include
#include
#include
int main(void)
{
int ret = execlp("ls", "ls", "-l", NULL);
if(ret < 0){
perror("execl!n");
exit(0);
}
printf("Hello world!n");
return 0;
}
(3)int execv(const char *path, char
*const argv[]);
int
execvp(const char *file, char *const argv[]);
10.c
#include
#include
#include
#include
int main(void)
{
char * buf[5] = {};
buf[0] = "ls";
buf[1] = "-l";
buf[2] = NULL;
int ret = execv("/bin/ls", buf);
// int ret =
execvp("ls", buf);
if(ret < 0){
perror("execv");
exit(0);
}
return 0;
}
10,whereis ls 查看ls的路径。
11,-rwx-rwx-rwx:普通文件
drwx-rwx-rwx:d代表是一个文件夹
lrwx-rwx-rwx:l代表是一个快捷方式
12,cd是shell的内置命令。如果输入等于cd,则用chdir(),它并没有fork()子进程,是父进程在执行chdir()。
11.c
#include
#include
#include
int main(void)
{
chdir("dic");
//这个目录要事先创建好。
mkdir("./ttt");
return 0;
}
如果用execlp(),会达不到想要的结果。
12.c
#include
#include
#include
#include
int main(void)
{
int ret = execlp("cd", "cd", "/", NULL);
if(ret < 0){
perror("execlp");
exit(0);
}
return 0;
}
13,一定要记住(固定格式):切割字符串函数strtok()
13.c
#include
#include
#include
#include
#define BUF 512
#define BUFF 10
int main(void)
{
char buf[BUF] = {};
ssize_t ret = read(0, buf, BUF);
int i = 0;
char * buff[BUFF] = {};
char * p = buf;
while(1){
p = strtok(p, " n");
if(!p)
break;
buff[i++] = p;
p = NULL;
}
for(i = 0; i < BUFF; i++){
if(NULL == buff[i])
break;
printf("%sn", buff[i]);
}
execvp(buff[0], buff);
return 0;
}
14,编写一个自己的终端
14.c
#include
#include
#include
#include
#define BUF 512
#define BUFF 10
int main(void)
{
char buf[BUF];
char * buff[BUFF];
char * p = buf;
int i = 0;
pid_t pid;
int ret = 0;
while(1){
write(1, "myshell$", 8);
memset(buf, 0,
BUF);
read(0, buf, BUF);
while(1){
p = strtok(p, "n ");
if(!p){
break;
}
buff[i++] = p;
p = NULL;
}
buff[i] = NULL;
if(strcmp(buff[0], "cd") == 0){
chdir(buff[1]);
continue;
}
pid = fork();
if(pid < 0){
perror("fork");
exit(0);
}else if(pid == 0){
ret = execvp(buff[0], buff);
if(ret < 0){
perror("execvp");
exit(0);
}
}else{
wait(NULL);
}
}
return 0;
}
15,仔细探讨fork()的用法。
15.c
#include
#include
#include
#include
int main(void)
{
pid_t pid;
char * message;
int n = 0;
pid = fork();
if(pid < 0){
perror("fork");
exit(0);
}
if(0 == pid){
message = "This is the childn";
n = 6;
}else{
message = "This is the parentn";
n = 3;
}
for(;n > 0; n--){
printf("%s", message);
sleep(1);
}
return 0;
}
程序运行顺序如下:
(1)父进程初始化。
(2)父进程调用fork,这是一个系统调用,因此进入内核。
(3)内核根据父进程复制出一个子进程,父进程和子进程的PCB信息相同,用户态代码和数据也相同。因此,子进程现在的状态和父进程一样,做完了初始化,刚调用了fork进入内核,还没有从内核返回。
(4)现在有两个一模一样的进程看起来都调用了fork进入内核等待从内核返回(实际上fork只调用了一次),系统中还有很多别的进程也等待从内核返回。是父进程先返回还是子进程先返回,还是这两个进程都等待,先去调度执行别的进程,这都不一定,取决于内核的调度算法。
(5)如果某个时刻父进程被调度执行了,从内核返回后就从fork函数返回,保存在变量pid中的返回值是子进程的id,是一个大于0的整数,因此执行下面else分支,然后执行for循环,打印"This is parentn"三次之后终止。
(6)如果某个时刻子进程被调度执行了,从内核返回后就从fork函数返回,保存在变量pid中的返回值是0,因此执行下面的if(0 == pid)分支,然后执行for循环,打印"This is childn"六次这样之后终止。fork调用把父进程的数据复制一份给子进程,但此后二者互不影响,在这个例子中,fork调用之后父进程和子进程的变量message和n被赋予不同的值,互不影响。
(7)父进程每打印一条信息就睡眠1秒,这时内核调度别的进程执行,在这1秒这么长的间隙里(对于计算机来说1秒很长了)子进程很有可能被调度到。同样地,子进程每打印一条信息就睡眠1秒,在这1秒期间父进程也很有可能被调度到。所以程序运行地结果基本上是父子进程交替打印,但这也不一定的,取决于系统中其他进程的运行情况和内核的调动算法,如果系统中其他进程非常繁忙则有可能观察到不同的结果。
(8)这个程序是在Shell下运行的,因此Shell进程是父进程的父进程。父进程运行时Shell进程处于等待状态,当父进程终止时Shell进程认为命令执行结束了,于是打印Shell提示符,而事实上子进程这时还没结束,所以子进程的消息打印到了Shell提示符后面。最后光标停在This is the child的下一行,这时用户仍然可以敲命令,即使命令不是紧跟在提示符后面,Shell也能正确读取。
fork函数:
(1)fork函数的特点"调用一次,返回两次",在父进程中调用一次,在父进程和子进程中各返回一次。一开始是一个控制流程,调用fork之后发生了分叉,变成两个控制流程,这也就是"fork"(分叉)这个名字的由来了。子进程中fork的返回值是0,而父进程中fork的返回值则是子进程的id(从根本上说fork是从内核返回的,内核自有办法让父进程和子进程返回不同的值),这样当fork函数返回后,程序员可以根据返回值的不同让父进程和子进程执行不同的代码。
(2)fork的返回值这样规定也是有道理的。fork在子进程中返回0,子进程仍可以调用getpid函数得到自己的进程id,也可以调用getppid函数得到父进程的id。在父进程中用getpid可以得到自己的进程id,然而要想得到子进程的id,只有将fork的返回值记录下来,别无他法。
(3)fork的另一个特性是所有由父进程打开的描述符都被复制到子进程中。父、子进程中相同编号的文件描述符在内核中指向同一个file结构体,也就是说file结构体的引用计数要增加。
16,exec函数
用fork创建子进程后执行的是和父进程相同的程序(但有可能执行不同的代码分支),子进程往往要调用一种exec函数以执行另一个程序。当进程调用一种exec函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序的启动例程开始执行。调用exec并不创建新进程,所以调用exec前后该进程的id并未改变。
exec族
man
3 exec
头文件:#include
int execl(const char *path, const char *arg0, ... );
int execlp(const char *file, const char *arg0, ... );
int execle(const char *path, const char *arg0, ...);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execvP(const char *file, const char *search_path, char *const
argv[]);
man 2 execve
int execve(const char *path, char *const argv[], char *const
envp[]);
这些函数如果调用成功则加载新的程序从启动代码开始执行,不再返回,如果调用出错则返回-1,所以exec函数只有出错的返回值而没有成功的返回值。
不带p(表示path)的exec函数第一个参数必须是程序的相对路径或绝对路径,例如"/bin/ls"或"./a.out",而不能是"ls"或"a.out"。对于带字母p的函数:如果参数中包含/,则将其视为路径名。否则视为不带路径的程序名,在PATH环境变量的目录列表中搜索这个程序。
带由字母l(表示list)的exec函数要求将新程序的每个命令行参数都作为一个参数传给它,命令行参数的个数是可变的,因此函数原型中有…,…中的最有一个可变参数应该是NULL,起sentinel的作用。
对于带有字母v(表示vector)的函数,则应该先构造一个指向各参数的指针数组,然后将该数组的首地址当作参数传给它,数组中的最后一个指针也应该是NULL,就像main函数的argv参数或者环境变量一样。
对于以e(envirnoment)结尾的exec函数,可以把一份新的环境变量传给它,其他exec函数仍使用当前的环境变量表执行新程序。
事实上,只有execve是真正的系统调用,其他五个函数最终都调用execve,所以execve在man手册第2节,其他函数在man手册第3节。
c语言初始化字符串 函数 manment,[转载]3.09进程(C语言班最后一天的课程)相关推荐
- c语言初始化字符串 函数 manment,nesC语言参考手册.doc
nesC语言参考手册 1 简介 nesC 是对 C 的扩展 ,它基于体现 TinyOS 的结构化概念和执行模型而设计. TinyOS 是为传感器网络节点而设计的一个事件驱动的操作系统,传感器网络节点拥 ...
- C语言常用字符串函数strlen、strcpy、strcat、strcmp、strchr
C语言常用字符串函数,求串长strlen(char *s).串复制strcpy(char *s1,char *s2).串连接strcat(char *s1,char *s2).串比较strcmp(ch ...
- 【C语言】字符串函数详解
hello~~,我是~小鹿 ,这是我的第一篇博客,没有循序渐进从基础开始写,只是最近在学习这里就写了,比较随心吧.希望这一篇博客能够给你带来帮助,之后也会继续写的,只是可能没有循序渐进,会比较杂七杂八 ...
- C语言常用字符串函数详解
在C语言标准库里面,存在一个对字符串数组进行操作的函数的头文件为string.h. 常用的字符串函数有strlen,strcpy,strcat等等. 了解这些字符串函数是如何实现的可以有助于更好的使用 ...
- c语言字符初始化怎么表示,C语言初始化字符串 怎么进行字符串赋值?C语言
C语言数组字符串初始化问题 C语言中,初始化操作就是在定义变量的时候,对变量进行赋值,即称为变量的初始化.对于字符变量也是一样的. 举例如下: char ch='a'; // 定义一个字符变量,变量名 ...
- c语言处理字符串函数的头文件,C语言字符处理函数 - 20131125的个人空间 - OSCHINA - 中文开源技术交流社区...
C语言提供了丰富的字符串处理函数, 大致可分为字符串的输入.输出.合并.修改.比较.转换.复制.搜索几类. 使用这些函数可大大减轻编程的负担.用于输入输出的字符串函数,在使用前应包含头文件" ...
- c语言如何让字符串变成空字符,C语言初始化字符串 c语言 如何将已赋值的字符串初始化为空?...
C语言数组字符串初始化问题恋爱语录:能牵手的时候,请别肩并肩,能拥抱的时候,请别手牵手,能相爱的时候,请别说分开:拥有了爱情,请别去碰暧昧 字符数组的定义与初始化字符数组的初始化,最容易理解的方式就是 ...
- 超详细C语言的字符串函数讲解
字符串函数 前言 C语言中对字符和字符串的处理很是频繁,但是C语言本身是没有字符串类型的,字符串通常放在常量字符串中或者字符数组中.字符串常量 适用于那些对它不做修改的字符串函数 接下来本文就是对于介 ...
- 【C语言】字符串函数strtok 按照指定字符串分割
C语言字符串函数 strtok() 函数原型 char *strtok(char *str,const char *delimiters); 参数 str,待分割的字符串 delimiters,分隔符 ...
最新文章
- html提交表单原理,HTML表单、HTTP Get与Post杂谈
- pandas:数据规范化方法与python实现
- 用long类型让我出了次生产事故,写代码还是要小心点
- 如何 给给软件开发 添加 代理_敏捷开发是如何被跑偏的
- Java Web学习总结(7)——HttpServletRequest对象
- Linux中Vim基本用法
- 使用QXmlStreamReader读取解析XML文件
- Linux上SQL Server 2019和Ubuntu上的Docker容器
- 解决eclipse环境下maven项目tomcat启动,未加载到项目的问题
- Ubuntu 16.04 安装 VMware Tools(解决windows和Ubuntu之间不能互相复制粘贴文件的问题)
- 微信小程序富文本组件mp-html
- easyui常用控件样式收藏
- python爬虫笔记_python简单爬虫笔记
- 移动技术--从网页游戏谈起1--网页游戏的兴起和现状
- matlab代码 无标度网络 生成图,标准无标度网络matlab
- k3服务器系统资源不足,金蝶k3云服务器已离线
- 求大于某数的最小素数Python版
- VEH Hook 及 检测
- 操作系统 银行家算法 安全性检查
- OPEN CASCADE Curve Continuity
热门文章
- Java堆转储:您可以完成任务吗?
- 在Spring容器外部连接对象依赖项
- 使用java.util.prefs.Preferences代替java.util.Properties
- Request的学习笔记(属Servlet学习课程)
- 精通 RPM 之查询篇
- 如何将本地 Windows 电脑中的文件复制(上传)到远程的 Windows 服务器主机上
- zip直链生成网站_安装网站程序
- vb6 判断打印机是否有效_吊打面试官 | 算法之如何判断括号是否有效?
- 滴滴java开发面试题_Java开发经典面试题(十二)
- 如何用python抢课_名额不够,技术来凑,利用Python实现教务系统强制性抢课