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语言班最后一天的课程)相关推荐

  1. c语言初始化字符串 函数 manment,nesC语言参考手册.doc

    nesC语言参考手册 1 简介 nesC 是对 C 的扩展 ,它基于体现 TinyOS 的结构化概念和执行模型而设计. TinyOS 是为传感器网络节点而设计的一个事件驱动的操作系统,传感器网络节点拥 ...

  2. C语言常用字符串函数strlen、strcpy、strcat、strcmp、strchr

    C语言常用字符串函数,求串长strlen(char *s).串复制strcpy(char *s1,char *s2).串连接strcat(char *s1,char *s2).串比较strcmp(ch ...

  3. 【C语言】字符串函数详解

    hello~~,我是~小鹿 ,这是我的第一篇博客,没有循序渐进从基础开始写,只是最近在学习这里就写了,比较随心吧.希望这一篇博客能够给你带来帮助,之后也会继续写的,只是可能没有循序渐进,会比较杂七杂八 ...

  4. C语言常用字符串函数详解

    在C语言标准库里面,存在一个对字符串数组进行操作的函数的头文件为string.h. 常用的字符串函数有strlen,strcpy,strcat等等. 了解这些字符串函数是如何实现的可以有助于更好的使用 ...

  5. c语言字符初始化怎么表示,C语言初始化字符串 怎么进行字符串赋值?C语言

    C语言数组字符串初始化问题 C语言中,初始化操作就是在定义变量的时候,对变量进行赋值,即称为变量的初始化.对于字符变量也是一样的. 举例如下: char ch='a'; // 定义一个字符变量,变量名 ...

  6. c语言处理字符串函数的头文件,C语言字符处理函数 - 20131125的个人空间 - OSCHINA - 中文开源技术交流社区...

    C语言提供了丰富的字符串处理函数, 大致可分为字符串的输入.输出.合并.修改.比较.转换.复制.搜索几类. 使用这些函数可大大减轻编程的负担.用于输入输出的字符串函数,在使用前应包含头文件" ...

  7. c语言如何让字符串变成空字符,C语言初始化字符串 c语言 如何将已赋值的字符串初始化为空?...

    C语言数组字符串初始化问题恋爱语录:能牵手的时候,请别肩并肩,能拥抱的时候,请别手牵手,能相爱的时候,请别说分开:拥有了爱情,请别去碰暧昧 字符数组的定义与初始化字符数组的初始化,最容易理解的方式就是 ...

  8. 超详细C语言的字符串函数讲解

    字符串函数 前言 C语言中对字符和字符串的处理很是频繁,但是C语言本身是没有字符串类型的,字符串通常放在常量字符串中或者字符数组中.字符串常量 适用于那些对它不做修改的字符串函数 接下来本文就是对于介 ...

  9. 【C语言】字符串函数strtok 按照指定字符串分割

    C语言字符串函数 strtok() 函数原型 char *strtok(char *str,const char *delimiters); 参数 str,待分割的字符串 delimiters,分隔符 ...

最新文章

  1. html提交表单原理,HTML表单、HTTP Get与Post杂谈
  2. pandas:数据规范化方法与python实现
  3. 用long类型让我出了次生产事故,写代码还是要小心点
  4. 如何 给给软件开发 添加 代理_敏捷开发是如何被跑偏的
  5. Java Web学习总结(7)——HttpServletRequest对象
  6. Linux中Vim基本用法
  7. 使用QXmlStreamReader读取解析XML文件
  8. Linux上SQL Server 2019和Ubuntu上的Docker容器
  9. 解决eclipse环境下maven项目tomcat启动,未加载到项目的问题
  10. Ubuntu 16.04 安装 VMware Tools(解决windows和Ubuntu之间不能互相复制粘贴文件的问题)
  11. 微信小程序富文本组件mp-html
  12. easyui常用控件样式收藏
  13. python爬虫笔记_python简单爬虫笔记
  14. 移动技术--从网页游戏谈起1--网页游戏的兴起和现状
  15. matlab代码 无标度网络 生成图,标准无标度网络matlab
  16. k3服务器系统资源不足,金蝶k3云服务器已离线
  17. 求大于某数的最小素数Python版
  18. VEH Hook 及 检测
  19. 操作系统 银行家算法 安全性检查
  20. OPEN CASCADE Curve Continuity

热门文章

  1. Java堆转储:您可以完成任务吗?
  2. 在Spring容器外部连接对象依赖项
  3. 使用java.util.prefs.Preferences代替java.util.Properties
  4. Request的学习笔记(属Servlet学习课程)
  5. 精通 RPM 之查询篇
  6. 如何将本地 Windows 电脑中的文件复制(上传)到远程的 Windows 服务器主机上
  7. zip直链生成网站_安装网站程序
  8. vb6 判断打印机是否有效_吊打面试官 | 算法之如何判断括号是否有效?
  9. 滴滴java开发面试题_Java开发经典面试题(十二)
  10. 如何用python抢课_名额不够,技术来凑,利用Python实现教务系统强制性抢课