系统编程函数之进程及进程通信

1、产生一个子进程
NAME
       fork - create a child process
SYNOPSIS
        #include <unistd.h>
        pid_t fork(void);
RETURN VALUE
       On  success, the PID of the child process is returned in the parent, and 0 is returned in the child.  On failure, -1 is returned in
       the parent, no child process is created, and errno is set appropriately.
使用:
pid_t pid = -1;
    //产生一个子进程
    pid = fork();
    if (pid > 0) //parent
    {
        num = 2;
        strcpy(caMsg, "this is parent");
    }
    else if (0 == pid)  //child
    {
        num = 6;
        strcpy(caMsg, "this is child");
    }
    else if (-1 == pid)  //error
    {
          perror("fork");
          exit(EXIT_FAILURE);
     }

2、等待进程死亡
NAME
       wait, waitpid, waitid - wait for process to change state
SYNOPSIS
       #include <sys/types.h>
       #include <sys/wait.h>
       pid_t wait(int *status);
       pid_t waitpid(pid_t pid, int *status, int options);
       int waitid(idtype_t idtype, id_t id, siginfo_t *infop, int options);
RETURN VALUE
        wait(): on success, returns the process ID of the terminated child; on error, -1 is returned.
       waitpid(): on success, returns the process ID of the child whose state has changed; if  WNOHANG  was  specified  and  one  or  more
       child(ren) specified by pid exist, but have not yet changed state, then 0 is returned.  On error, -1 is returned.
       waitid(): returns 0 on success or if WNOHANG was specified and no child(ren) specified by id has yet changed state; on error, -1 is
       returned.
       Each of these calls sets errno to an appropriate value in the case of an error.
使用:wait(NULL); (为僵尸进程收尸)

3、调用可执行文件
NAME
       execl, execlp, execle, execv, execvp, execvpe - execute a file
SYNOPSIS
        #include <unistd.h>
       extern char **environ;
       int execl(const char *path, const char *arg, ...
                       /* (char  *) NULL */);
       int execlp(const char *file, const char *arg, ...
                       /* (char  *) NULL */);
       int execle(const char *path, const char *arg, ...
                       /*, (char *) NULL, char * const envp[] */);
       int execv(const char *path, char *const argv[]);
       int execvp(const char *file, char *const argv[]);
       int execvpe(const char *file, char *const argv[],
                       char *const envp[]);
RETURN VALUE
       The exec() functions return only if an error has occurred.  The return value is -1, and errno is set to indicate the error.

    //参数一:执行文件的路径
    //参数二:要执行的文件名
    //后续参数为文件执行时的参数
    //最后一个参数设置为NULL
execl("/home/sq/tmp/test", "test", NULL);
execl("/usr/bin/gedit"  , "gedit", "./execl.c", NULL);
    execlp("test", "./test", NULL);
(省略了路径,如何添加默认路径)
/etc
sudo vim profile
export PATH=/home/shenglin/today:$PATH
scourse profile
只对当前终端有效
重启后全有效
    char *arg[] = {"gedit", "./execlp.c", NULL};
    execv("/usr/bin/gedit", arg);

       只有 ececve 是正真意义上的系统调用,其他都是在此基础上经过包装的库函数。
       exec函数族的作用是 根据指定的文件名找到可执行文件 ,并用它来调取进程中的内容, 也就是 调用进程内部执行一个可执行文件,可以是二进制文件,也可以是Linux下的脚本文件
       执行成功后不会返回 ,因为调用进程的实体,包括代码段,数据段,堆栈等都已经被新的内容取代, 只留下进程ID等一些表面上的信息仍然保持原样,金蝉脱壳, 看上去还是旧躯壳,却注入了新灵魂,只有失败才会返回-1;

Linux下如何执行新程序?
        每当有进程认为自己不能为系统和用户做出贡献了,他就发挥最后的余热,调用ecec,让自己重生; or 若一个进程想执行另一个程序,可以fork一个新进程,调用exec,看起来像是执行应用程序产生了新进程(非常普遍)
       Linux专门做了优化,fork会将所有内容拷贝到子进程,而fork完后调用exec,这些拷贝的东西又会被抹掉,
       于是人们设计了 写时拷贝(copy-on-write)技术 ,使得fork结束后不立刻复制父进程的内容,到了真正实用的时候才复制,这样如果下一条语句是exec,就不会做无用功了,提高了效率。
       一定要加错误判断,因为权限问题,总会出差错


1、无名管道 pipe   (相当于定义了一个数据结构用于通信,程序结束消失)
NAME
       pipe, pipe2 - create pipe
SYNOPSIS
       #include <unistd.h>
       int pipe(int pipefd[2]);
       #define _GNU_SOURCE             /* See feature_test_macros(7) */
       #include <fcntl.h>              /* Obtain O_* constant definitions */
       #include <unistd.h>
       int pipe2(int pipefd[2], int flags);
RETURN VALUE
        On success, zero is returned.  On error, -1 is returned, and errno is set appropriately.
使用:
    int pipefd[2] = {0};
    int ret = -1;
    //创建管道
    ret = pipe(pipefd);
    if (-1 == ret)
    {
        perror("pipe");
        return -1;
    }
    //创建子进程
    //通过管道实现父子进程间的通信
    pid_t pid = fork();
    if (pid > 0) //parent
    {
        char caMsg[64] = {'\0'};
        //防止父进程意外将数据读取
        //可以将读端关闭
        close(pipefd[0]);
        while (1)
        {
            printf("父进程输入:\n");
            memset(caMsg, '\0', sizeof(caMsg));
            //从标准输入获得数据
            read(STDIN_FILENO, caMsg, 64);
            //将数据写入管道
            write(pipefd[1], caMsg, strlen(caMsg));
        }
    }
    else if (0 == pid) //child
    {
        char caMsg[64] = {'\0'};
        //防止子进程意外往管道中写数据
        //可以将写端关闭
        close(pipefd[1]);
        while (1)
        {
            memset(caMsg, '\0', 64);
            //从管道中读取数据
            //若管道中没有数据,则阻塞等待数据
            read(pipefd[0], caMsg, 64);
            //将数据显示
            printf("子进程显示数据:\n");
            write(STDOUT_FILENO, caMsg, strlen(caMsg));
        }
    }
    else if (-1 == pid)  //error
    {
        perror("fork");
        return -1;
    }

pipefd[1] 写入端口,pipefd[0] 读取端口,程序结束管道消失
       无名管道数据结构,数据读取后就清除,参数存放接口描述符,管道独立于进程,属于第三方, 子进程仅拷贝描述符,只能用于具有 亲缘关系 的进程间通信 ,创建无名管道的进程结束后,无名管道也被释放


2、有名管道;mkfifo  (创建了管道文件用于通信,永远存在)
NAME
       mkfifo - make FIFOs (named pipes)
SYNOPSIS
       mkfifo [OPTION]... NAME...
使用:
    int ret = mkfifo("myfifo", 0664);
    if (-1 == ret)
    {
        perror("mkfifo");
        return -1;
    }
读数据:
int main(int argc, char *argv[])
{
    int fd = open("myfifo", O_RDWR);
    char caMsg[64] = {'\0'};
    int sign = 1;
    while (1)
    {
        memset(caMsg, '\0', sizeof(caMsg));
        if (0 == sign)
        {
            printf("请输入数据:\n");
            read(STDIN_FILENO, caMsg, 64);
            write(fd, caMsg, strlen(caMsg));
            sign = 1;
        }
        else if (1 == sign)
        {
            read(fd, caMsg, 64);
            printf("获得数据:\n");
            write(STDOUT_FILENO, caMsg, strlen(caMsg));
            sign = 0;
        }
        sleep(1);
    }
    printf("Hello World\n");
    return 0;
}
写数据:
int main(int argc, char *argv[])
{
    int fd = open("myfifo", O_RDWR);
    char caMsg[64] = {'\0'};
    int sign = 0;
    while (1)
    {
        memset(caMsg, '\0', sizeof(caMsg));
        if (0 == sign)
        {
            printf("请输入数据:\n");
            read(STDIN_FILENO, caMsg, 64);
            write(fd, caMsg, strlen(caMsg));
            sign = 1;
        }
        else if (1 == sign)
        {
            read(fd, caMsg, 64);
            printf("获得数据:\n");
            write(STDOUT_FILENO, caMsg, strlen(caMsg));
            sign = 0;
        }
        sleep(1);
    }
    printf("Hello World\n");
    return 0;
}
    p开头的是管道文件若写打开一个有名管道,则会阻塞,直到有进程读打开该有名管道,同理读打开一样
    若写段关闭,read返回值为0, 若在运行过程中,读段关闭,则系统会发送信号干掉本进程

3、共享内存  (向计算机申请一块内存用于通信,电脑关机后消失)
(1)创建:shmget
SHMGET(2)                                                Linux Programmer's Manual                                               SHMGET(2)
NAME
       shmget - allocates a System V shared memory segment
SYNOPSIS
       #include <sys/ipc.h>
       #include <sys/shm.h>
key 十六进制标识,共享内存编号,编号可以相同,size:共享内存大小,shmflg:下列
       int shmget(key_t key, size_t size, int shmflg);
RETURN VALUE   返回共享编号:共享编号唯一 若存在,只返回共享编号
       On success, a valid shared memory identifier is returned.  On error, -1 is returned, and errno is set to indicate the error.
IPC_CREAT   Create a new segment.  If this flag is not used, then shmget() will find the segment associated with key and  check  to
                   see if the user has permission to access the segment.
       IPC_EXCL    This flag is used with IPC_CREAT to ensure that this call creates the segment.  If the segment already exists, the call
                   fails.
       SHM_HUGETLB (since Linux 2.6)
                   Allocate the segment using "huge pages."  See the Linux kernel source file Documentation/vm/hugetlbpage.txt for further
                   information.
       SHM_HUGE_2MB, SHM_HUGE_1GB (since Linux 3.8)
                   Used  in conjunction with SHM_HUGETLB to select alternative hugetlb page sizes (respectively, 2 MB and 1 GB) on systems
                   that support multiple hugetlb page sizes.
                   More generally, the desired huge page size can be configured by encoding the base-2 logarithm of the desired page  size
                   in the six bits at the offset SHM_HUGE_SHIFT.  Thus, the above two constants are defined as:

                       #define SHM_HUGE_2MB    (21 << SHM_HUGE_SHIFT)
                       #define SHM_HUGE_1GB    (30 << SHM_HUGE_SHIFT)
                   For some additional details, see the discussion of the similarly named constants in mmap(2).
       SHM_NORESERVE (since Linux 2.6.15)
                  This flag serves the same purpose as the mmap(2) MAP_NORESERVE flag.  Do not reserve swap space for this segment. 
         在内存中申请空间,返回地址,读完后数据依然存在,程序结束后依然存在(malloc 消失),在电脑关机时消失
(2)建立联系:shmat
SYNOPSIS
       #include <sys/types.h>
       #include <sys/shm.h>
       void *shmat(int shmid, const void *shmaddr, int shmflg); (0读写,SHM_RDONLY )
       int shmdt(const void *shmaddr);
RETURN VALUE
       On success, shmat() returns the address of the attached shared memory segment; on error, (void *) -1 is returned, and errno is  set
       to indicate the cause of the error.
       On success, shmdt() returns 0; on error -1 is returned, and errno is set to indicate the cause of the error.
使用:
int main(int argc, char *argv[])
{
    int shmid = -1;
    shmid = shmget(0x1024, 4096, IPC_CREAT|0664);
    if (-1 == shmid)
    {
        perror("shmget");
        return -1;
    }
    void *addr = NULL;
    //参数二:NULL让系统自动将共享内存关联到进程合适位置
    //参数三:0表示读写
    //SHM_RDONLY:表示只读
    //没有只写
    //成功:返回值为共享内存的地址
    //失败:返回值为(void *)-1
    addr = shmat(shmid, NULL, 0);
    if ((void *)-1 == addr)
    {
        perror("shmat");
        return -1;
    }
    char *pMsg = "欢迎来到上海";
    //将数据拷贝到共享内存中
    //strcpy((char *)addr, pMsg);
    //sprintf((char*)addr, "%s", pMsg);
    //memcpy(addr, pMsg, strlen(pMsg));
    char *p = (char *)addr;
    while ('\0' != *pMsg)
    {
        *p = *pMsg;
        p++;
        pMsg++;
    }
    *p = '\0';
    //将数据从共享内存中拷贝出来
    char caMsg[64] = {'\0'};
//    strcpy(caMsg, (char*)addr);
//    sscanf((char*)addr, "%s", caMsg);
    memcpy(caMsg, addr, 64);
    printf("msg:%s\n", caMsg);
    //取消关联
    shmdt(addr);
   return 0;
}


4、文件映射:mmap,munmap
NAME
       mmap, munmap - map or unmap files or devices into memory
SYNOPSIS
       #include <sys/mman.h>
(地址NULL, 大小,  作用读还是写,是本进程自己用还是一起用,文件描述符,)
       v oid *mmap(void *addr, size_t length, int prot, int flags ,
                  int fd, off_t offset);
       int munmap(void *addr, size_t length);
        MAP_SHARED (flags)
              Share  this  mapping.   Updates to the mapping are visible to other processes that map this file, and are carried through to
              the underlying file.  (To precisely control when updates are carried through to the underlying  file  requires  the  use  of
              msync(2).)
        MAP_PRIVATE
              Create  a  private  copy-on-write mapping.  Updates to the mapping are not visible to other processes mapping the same file,
              and are not carried through to the underlying file.  It is unspecified whether changes made to the  file  after  the  mmap()
              call are visible in the mapped region.
RETURN VALUE
       On  success,  mmap()  returns a pointer to the mapped area.   On error, the value MAP_FAILED (that is, (void *) -1) is returned, and
       errno is set to indicate the cause of the error.
       On success, munmap() returns 0.  On failure, it returns -1, and errno is set to indicate the cause of the error (probably  to  EIN‐
       VAL).
使用:
int main(int argc, char *argv[])
{
    int fd = -1;
    //以读写的方式打开文件
    fd = open(argv[1], O_RDWR);
    if (-1 == fd)
    {
        perror("open");
        return -1;
    }
    void *addr = NULL;
    //1:NULL表示让系统自动映射到内存的合适位置
    //2:存储空间的大小
    addr = mmap(NULL, 1024, PROT_READ|PROT_WRITE
                , MAP_SHARED, fd, 0);
    //MAP_FAILED --> (void *)-1
    if (MAP_FAILED == addr)
    {
        perror("mmap");
        return -1;
    }   
    char *pData = "北京欢迎你";
    sprintf((char *)addr, "%s", pData);
    close(fd);
    munmap(addr, 1024);
    return 0;
}
一般不用做通信,用在对设备的操作,U盘,摄像头, 把摄像头当做文件映射到内存
将文件的一块内容放入内存,对内存的操作,会自动更新到文件中



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值