Linux下C语言编程-进程的创建

作者:hoyt

1.进程的概念

Linux操作系统是面向多用户的.在同一时间可以有许多用户向操作系统发出各种命令.那么操作系统是怎么实现多用户的环境呢?在现代的操作系统里面,都有程序和进程的概念.那么什么是程序,什么是进程呢?通俗的讲程序是一个包含可以执行代码的文件,是一个静态的文件.而进程是一个开始执行但是还没有结束的程序的实例.就是可执行文件的具体实现.

一个程序可能有许多进程,而每一个进程又可以有许多子进程.依次循环下去,而产生子孙进程.当程序被系统调用到内存以后,系统会给程序分配一定的资源(内存,设备等等)然后进行一系列的复杂操作,使程序变成进程以供系统调用.在系统里面只有进程没有程序,为了区分各个不同的进程,系统给每一个进程分配了一个ID(就象我们的身份证)以便识别.

为了充分的利用资源,系统还对进程区分了不同的状态.将进程分为新建,运行,阻塞,就绪和完成五个状态.新建表示进程正在被创建,运行是进程正在运行,阻塞是进程正在等待某一个事件发生,就绪是表示系统正在等待CPU来执行命令,而完成表示进程已经结束了系统正在回收资源.关于进程五个状态的详细解说我们可以看《操作系统》上面有详细的解说。

2.进程的标志

上面我们知道了进程都有一个ID,那么我们怎么得到进程的ID呢?系统调用getpid可以得到进程的ID,而getppid可以得到父进程(创建调用该函数进程的进程)的ID.

pid_t getpid(void);
    pid_t getppid(void);

进程是为程序服务的,而程序是为了用户服务的.系统为了找到进程的用户名,还为进程和用户建立联系.这个用户称为进程的所有者.相应的每一个用户也有一个用户ID.通过系统调用getuid可以得到进程的所有者的ID.由于进程要用到一些资源,而Linux对系统资源是进行保护的,为了获取一定资源进程还有一个有效用户ID.这个ID和系统的资源使用有关,涉及到进程的权限.通过系统调用geteuid我们可以得到进程的有效用户ID.和用户ID相对应进程还有一个组ID和有效组ID系统调用getgid和getegid可以分别得到组ID和有效组ID.

uid_t getuid(void);
    uid_t geteuid(void);
    gid_t getgid(void);
    git_t getegid(void);

有时候我们还会对用户的其他信息感兴趣(登录名等等),这个时候我们可以调用getpwuid来得到.

struct passwd {
char *pw_name; /* 登录名称 */
char *pw_passwd; /* 登录口令 */
uid_t pw_uid; /* 用户ID */
gid_t pw_gid; /* 用户组ID */
char *pw_gecos; /* 用户的真名 */
char *pw_dir; /* 用户的目录 */
char *pw_shell; /* 用户的SHELL */
};

#include
#include

struct passwd *getpwuid(uid_t uid);
 
    下面我们学习一个实例来实践一下上面我们所学习的几个函数:

#include
#include
#include
#include

int main(int argc,char **argv)
{
pid_t my_pid,parent_pid;
uid_t my_uid,my_euid;
gid_t my_gid,my_egid;
struct passwd *my_info;

my_pid=getpid();
parent_pid=getppid();
my_uid=getuid();
my_euid=geteuid();
my_gid=getgid();
my_egid=getegid();
my_info=getpwuid(my_uid);

printf("Process ID:%ld\n",my_pid);
printf("Parent ID:%ld\n",parent_pid);
printf("User ID:%ld\n",my_uid);
printf("Effective User ID:%ld\n",my_euid);
printf("Group ID:%ld\n",my_gid);
printf("Effective Group ID:%ld\n",my_egid):

if(my_info)
{
printf("My Login Name:%s\n" ,my_info->pw_name);
printf("My Password :%s\n" ,my_info->pw_passwd);
printf("My User ID :%ld\n",my_info->pw_uid);
printf("My Group ID :%ld\n",my_info->pw_gid);
printf("My Real Name:%s\n" ,my_info->pw_gecos);
printf("My Home Dir :%s\n", my_info->pw_dir);
printf("My Work Shell:%s\n", my_info->pw_shell);
}
}

3.进程的创建

创建一个进程的系统调用很简单.我们只要调用fork函数就可以了.

pid_t fork();

当一个进程调用了fork以后,系统会创建一个子进程.这个子进程和父进程不同的地方只有他的进程ID和父进程ID,其他的都是一样.就象符进程克隆(clone)自己一样.当然创建两个一模一样的进程是没有意义的.为了区分父进程和子进程,我们必须跟踪fork的返回值.

当fork掉用失败的时候(内存不足或者是用户的最大进程数已到)fork返回-1,否则fork的返回值有重要的作用.对于父进程fork返回子进程的ID,而对于fork子进程返回0.我们就是根据这个返回值来区分父子进程的.

父进程为什么要创建子进程呢?前面我们已经说过了Linux是一个多用户操作系统,在同一时间会有许多的用户在争夺系统的资源.有时进程为了早一点完成任务就创建子进程来争夺资源.一旦子进程被创建,父子进程一起从fork处继续执行,相互竞争系统的资源.有时候我们希望子进程继续执行,而父进程阻塞直到子进程完成任务.这个时候我们可以调用wait或者waitpid系统调用.

pid_t wait(int *stat_loc);

pid_t waitpid(pid_t pid,int *stat_loc,int options);

wait系统调用会使父进程阻塞直到一个子进程结束或者是父进程接受到了一个信号.如果没有父进程没有子进程或者他的子进程已经结束了wait回立即返回.成功时(因一个子进程结束)wait将返回子进程的ID,否则返回-1,并设置全局变量errno.stat_loc是子进程的退出状态.子进程调用exit,_exit 或者是return来设置这个值. 为了得到这个值Linux定义了几个宏来测试这个返回值.

WIFEXITED:判断子进程退出值是非0

WEXITSTATUS:判断子进程的退出值(当子进程退出时非0).

WIFSIGNALED:子进程由于有没有获得的信号而退出.

WTERMSIG:子进程没有获得的信号号(在WIFSIGNALED为真时才有意义).

waitpid等待指定的子进程直到子进程返回.如果pid为正值则等待指定的进程(pid).如果为0则等待任何一个组ID和调用者的组ID相同的进程.为-1时等同于wait调用.小于-1时等待任何一个组ID等于pid绝对值的进程. stat_loc和wait的意义一样. options可以决定父进程的状态.可以取两个值 WNOHANG:父进程立即返回当没有子进程存在时.WUNTACHED:当子进程结束时waitpid返回,但是子进程的退出状态不可得到.

父进程创建子进程后,子进程一般要执行不同的程序.为了调用系统程序,我们可以使用系统调用exec族调用.exec族调用有着5个函数.

int execl(const char *path,const char *arg,...);

int execlp(const char *file,const char *arg,...);

int execle(const char *path,const char *arg,...);

int execv(const char *path,char *const argv[]);

int execvp(const char *file,char *const argv[]):

exec族调用可以执行给定程序.关于exec族调用的详细解说可以参考系统手册(manexecl).下面我们来学习一个实例.注意编译的时候要加 -lm以便连接数学函数库.

#include
#include
#include
#include
#include
#include

void main(void)
{
pid_t child;
int status;

printf("This will demostrate how to get child status\n");
if((child=fork())==-1)
{
printf("Fork Error :%s\n",strerror(errno));
exit(1);
}
else if(child==0)
{
int i;
printf("I am the child:%ld\n",getpid());
for(i=0;i<1000000;i++) sin(i);
i=5;
printf("I exit with %d\n",i);
exit(i);
}
while(((child=wait(&status))==-1)&(errno==EINTR));
if(child==-1)
printf("Wait Error:%s\n",strerror(errno));
else if(!status)
printf("Child %ld terminated normally return status is zero\n",
child);
else if(WIFEXITED(status))
printf("Child %ld terminated normally return status is %d\n",
child,WEXITSTATUS(status));
else if(WIFSIGNALED(status))
printf("Child %ld terminated due to signal %d znot caught\n",
child,WTERMSIG(status));
}

strerror函数会返回一个指定的错误号的错误信息的字符串.
 
    4.守护进程的创建

如果你在DOS时代编写过程序,那么你也许知道在DOS下为了编写一个常驻内存的程序我们要编写多少代码了.相反如果在Linux下编写一个"常驻内存"的程序却是很容易的.我们只要几行代码就可以做到.     实际上由于Linux是多任务操作系统,我们就是不编写代码也可以把一个程序放到后台去执行的.我们只要在命令后面加上&符号SHELL就会把我们的程序放到后台去运行的.这里我们"开发"一个后台检查邮件的程序.这个程序每个一个指定的时间回去检查我们的邮箱,如果发现我们有邮件了,会不断的报警(通过机箱上的小喇叭来发出声音).后面有这个函数的加强版本.后台进程的创建思想:首先父进程创建一个子进程.然后子进程杀死父进程(是不是很无情?). 信号处理所有的工作由子进程来处理.

#include
#include
#include
#include
#include
#include
#include

/* Linux 的默任个人的邮箱地址是 /var/spool/mail/用户的登录名 */

#define MAIL "/var/spool/mail/hoyt"

/* 睡眠10秒钟 */

#define SLEEP_TIME 10

main(void)
{
pid_t child;

if((child=fork())==-1)
{
printf("Fork Error:%s\n",strerror(errno));
exit(1);
}
else if(child>0)
while(1);
if(kill(getppid(),SIGTERM)==-1)
{
printf("Kill Parent Error:%s\n",strerror(errno));
exit(1);
}
{
int mailfd;

while(1)
{
if((mailfd=open(MAIL,O_RDONLY))!=-1)
{
fprintf(stderr,"%s","\007");
close(mailfd);
}
sleep(SLEEP_TIME);
}
}
}
 
    你可以在默认的路径下创建你的邮箱文件,然后测试一下这个程序.当然这个程序还有很多地方要改善的.我们后面会对这个小程序改善的,再看我的改善之前你可以尝试自己改善一下.比如让用户指定邮相的路径和睡眠时间等等.相信自己可以做到的.动手吧,勇敢的探险者.

进程是一个非常重要的概念,许多的程序都会用子进程.创建一个子进程是每一个程序员的基本要求!

转载于:https://www.cnblogs.com/F4ncy/archive/2005/01/26/97842.html

Linux下C语言编程-进程的创建相关推荐

  1. linux+下c语言编程项目,精通UNIX下C语言编程与项目实践

    cc -I  //include 目录 -L //静态库目录?动态也可以 -l //小写L,接静态库名称?动态也可以 -DXXX='"XXFF"' //-D直接定义宏 -c 只编译 ...

  2. Linux下C语言编程

    第1章 Linux下C语言编程简介 本章将简要介绍一下什么是Linux,C语言的特点,程序开发的预备知识,Linux下C语言开发的环境,程序设计的特点和原则以及编码风格等.通过本章的学习,可以对在Li ...

  3. 初识Linux下C语言编程

    本章将简要介绍一下什么是Linux,C语言的特点,程序开发的预备知识,Linux下C语言开发的环境,程序设计的特点和原则以及编码风格等.通过本章的学习,可以对在Linux下使用C语言编程有一个基本的了 ...

  4. 您知道Linux下C语言编程的一些注意事项吗_教育中国

    您知道Linux下C语言编程的一些注意事项吗_教育中国 云风的 BLOG: 一个 C 接口设计的问题 一个 C 接口设计的问题 C 语言在本质上,参数传递都是值传递.不像 Pascal 和 C++ 可 ...

  5. linux下C语言编程操作数据库(sqlite3)

    前言:C语言中通过调用 sqlite 的函数接口来实现对数据库的管理(创建数据库.创建表格.插入数据.查询.数据.删除数据等),掌握sqlite数据库的语法,以及sqlite提供的函数接口,那么在li ...

  6. linux c语言工具,Linux下C语言编程环境的工具.doc

    Linux下C语言编程环境的工具 Linux下C语言编程环境的工具 Linux下C语言编程环境的工具 要想在Linux下进行C语言编程,首先得搭建好一个编程环境.这里分别说明一下几个非常有用的软件包. ...

  7. linux c语言 ppt,Linux下C语言编程.ppt

    Linux下C语言编程 Linux中C语言的重要性 Linux和C天生有不解之缘. Linux操作系统的内核主要是用C写的,另外Linux下的很多软件也是用C写的,特别是一些著名的服务软件,比如MyS ...

  8. linux 下C语言编程(2)——进程的创建,挂起,解挂,进程间通信

    在 linux 下利用C语言实现进程的创建,挂起和解挂操作 #include <stdio.h> #include <sys/stat.h> #include <sys/ ...

  9. linux下c语言编程书籍6,Linux下C语言编程-6.pdf

    永远的下语言编程基础知识 页码, 首页编程技术正文 L inux下C语言编程 - -基础知识 作者 前言 这篇文章介绍在下进行语言编程所需要的基础知识在这篇文章当中我们将会学 内容 源程序编译 的编写 ...

最新文章

  1. cuda cudnn pytorch版本对应关系
  2. Ellipse函数画圆
  3. 【番外篇1】青龙面板中cron表达式新手入门教程cron的介绍与使用
  4. android真机上 conn.getOutputStream()异常 Does not support output
  5. 工业机器人行业研究报告
  6. Spring Cloud构建微服务架构(六)高可用服务注册中心
  7. 最小公倍数 最大公约数
  8. 【干货分享】推荐5个可以让你事半功倍的Python自动化脚本
  9. 使用Gradle构建Monorepo项目
  10. java右移位_java中的左移 右移
  11. 详解mysql什么时候不走索引
  12. 3d激光雷达开发(lidar使用)
  13. java公司自己封装的框架_SpringBoot封装自己的Starter的实现方法
  14. SqlServer2005基于已有表创建分区
  15. java 单体 启动优化_java设计优化之单例模式
  16. 大数据时代的国际贸易理论新发展
  17. python爬虫进阶-每日一学(图片反爬-雪碧图)
  18. 电影后台管理系统(实训)
  19. WebRtC视频jitterbuffer原理机制
  20. 飞行摇杆设置_分离摇杆+16000DPI,旗舰电竞鼠标ROG魔刃标准版体验

热门文章

  1. Python 图像处理篇-利用opencv库和numpy库读取包含中文路径下的本地图片实例演示
  2. Python 技术篇-使用opencv读取图片实例演示,python安装opencv库
  3. Java Web - 服务器中的过滤器和监听器
  4. 三星电子电容器件识别
  5. 【递推DP技巧 hdu 2050 折线分割平面】
  6. SDUT _2117 数据结构实验之链表二:逆序建立链表
  7. 遗传算法的收敛性分析
  8. Sympy计算结果带参数的方程组
  9. 【解决方案】VMWare拖动窗口后有黑色边框(分辨率自适应问题)
  10. 解微分方程_matlab