2012-02-15 6 views
1

Я изучаю, как создавать процессы, используя fork, и я смущен в следующем. Это код:Что делает двойной амперсанд в этой программе?

int main() { 
    int ret = fork(); 
    // printf("%d\n", ret); 
    ret = ret && fork(); /* Here is where I am confused*/ 
    // print("ret: %d\n", ret); 
    if(ret == 0) { 
     fork(); 
    } 
    printf("Hello world\n"); 
    return 1; 
} 

Итак, что именно используется в двойном амперсанде? Я запускал программу с помощью «printf», чтобы знать, что именно такое значение, но это стало более запутанным, потому что вывод в первом «printf» равен 0, а второй «printf» равен «1». Поэтому я не совсем уверен, что делает двойной амперсанд.

Я ценю помощь!

+0

Это булевский оператор ... http://en.wikipedia.org/wiki/Boolean_algebra#Basic_operations Кроме того, это очень ... интересно, почему в конце есть 'return 1'. Это нонсенс. – Gandaro

+4

Что вам нужно, это учебник, посвященный основам C. – dandan78

+1

неаккуратный код действительно запутанный –

ответ

5

Это называется логическим оператором , Это будет определять логический результат AND для двух операндов. Свойство этого оператора:

Сначала левый операнд оценивается, если он ИСТИНА (не равен нулю), затем оценивается операнд правой стороны. Если это так, то все выражение истинно, иначе false. С другой стороны, если операнд левой стороны FALSE, то правый операнд не оценивается вообще. Это можно сделать, потому что, поскольку один из операндов является ложным, каким бы ни был другой операнд, выражение становится ложным. Это известно как short circuiting

В вашем коде, если левая рука, если ret верно, то только правая часть оценивается, что в конечном итоге вызывает fork() системный вызов. Возвращаемое значение вызова имеет значение AND с текущим значением ret и переназначено до ret.

В основном он работает как

if (ret == TRUE) 
{ 
    retval = fork(); 
    ret = ret && retval; 
} 

Прочитайте это:

В случае успеха, PID дочернего процесса возвращается в родительском, и 0 возвращается в дочернем. При ошибке -1 возвращается родительскому, дочерний процесс не создается, а errno устанавливается соответствующим образом.

Рассмотрите дерево вилок ниже. Каждый «узел» дерева показывает последовательность выполняемых операторов в каждом отдельном выражении. Одна работа в одной строке.

(p1) 
+--+ret = fork(); 
| printf 1 shows pid 
| && allows   fork(), ret = 1 = pid1 && pid2 
| printf 2 shows 1 + 
| `if' not entered | 
| show hello   | 
|      | (p3) 
|      +--+ ret = 0 = ret && fork() (this is 0 here) 
+-----+      printf 2 shows 0 
     |      `if' is entered 
     |      fork() 
     |      show hello 
     |       + 
     |       | 
     +       | 
    (p2)       | 
    level 1      +-------+ 
    print 0 in 1st printf     | 
    && DOES NOT allow fork()   (p5) 
    print 0 in 2st printf    show hello 
    `if' entered 
    fork() +-----------+ 
    show hello   | 
         | 
         + 
         (p4) 
        show hello 

Здесь каждый процесс.

р1 выполняет fork() один раз, и имеет PID (ненулевой) в RET. печатает pid короткое замыкание позволяет выполнять fork(). Поскольку это родительский элемент, он возвращает еще один pid, который обрабатывается предыдущим дочерним pid, который оценивается как 1. Поэтому ret теперь содержит 1, который печатается во втором printf. поскольку ret is 1, if не выполняется. Привет напечатан.

p2 Ребенок p1, поэтому ret имеет 0. печатает 0 в первом printf. Короткое замыкание не позволяет звонить fork(). if тело введено, и fork() называется, что делает (p4). Теперь (p2) переходит к печати Hello.

p3 Дитя p1, так fork() возвращение 0, который с операции AND RET, и делает его 0 после назначения. Это порождается после первого printf, поэтому отображается только второй printf 0. if, выполняется fork(), что делает (p5). Теперь p4 продолжает печатать Hello.

p4 начинается от if тела, вылезает и печатает Hello

p5 начинается с if тела, вылезает и печатает Hello

Выше я попытался выразить дерево процесс икру , а последовательность работ в каждом процессе выражается в каждой строке процесса «узел» на дереве. Края обозначают икру, а край начинается с соответствующей вилки.

+0

Спасибо! Это проясняет многое! – Oscarin

+0

Обратите внимание на то, что напечатано на каждом уровне. – phoxis

2

Это логическое И, то есть, если ret истинно (не равно нулю), и результат fork() истинно (не равно нулю) назначить верный ret, иначе присвоить ложный (ноль) для ret.

Поскольку этот оператор short-cirucuited, fork() будет называться, только если ret - это правда.

+0

Тогда объясните, почему «выход в первом printf равен 0, а второй printf равен 1» – BlackBear

+0

@BlackBear - через час я должен поймать поездку/ – MByD

+0

Наверное, важно отметить, что то, что он действительно пытается сделать, является причиной чтобы ребенок не разветвлялся, так как «После успешного завершения fork() возвращает 0 в дочерний процесс и возвращает идентификатор процесса дочернего процесса в родительский процесс». – BeRecursive

0

Это ленивый логический AND функция. Найдите таблицу истинности для AND, если вам нужна дополнительная информация. Это лениво, потому что если ret false, fork() не будет оцениваться, поскольку все, что ANDed с false всегда false.

6

В C двойной амперсанд && - короткое замыкание logical AND. Если у вас есть что-то вроде a && b, то это будет оцениваться следующим образом: оно вернет true, если оба значения a и b истинны, но если a является ложным, то b никогда не будет выполнен.

В качестве дополнительного example:

int a = 0; 
if (a && myfunc(b)) { 
    do_something(); 
} 

В этом примере, гарантии оценки короткого замыкания, что MyFunc (б) никогда не вызывается. Это связано с тем, что вычисляется значение false. Эта функция позволяет использовать две полезные программирующие конструкции. Во-первых, если первое подвыражение проверяет, требуется ли дорогостоящее вычисление, а проверка оценивается как false, можно исключить дорогостоящие вычисления во втором аргументе. Во-вторых, он позволяет построить конструкцию, где первое выражение гарантирует условие, без которого второе выражение может вызвать ошибку во время выполнения.

Таким образом, ваш код вызывает только fork(), если ret - это правда. ret присваивается либо 0 (ret равно 0), либо 1 (оба значения - ret и fork()).

+0

Спасибо, я ценю помощь! Теперь это имеет смысл. Это был какой-то глупый вопрос, но я не видел ответа после того, как вы показали мне. Опять же, спасибо! – Oscarin

0

Это короткое замыкание логическое И. Если ret равно 0, то он выполнит fork(). Если нет, это не так. Я могу пройти через код для вас.

//we fork a child process 
int ret = fork(); 

//ret is 0 if we are in the child process, -1 if fork failed 
//otherwise ret is the process id of the child process 

//because of this, the fork below executes only within the child process 
ret = ret && fork(); /* Here is where I am confused*/ 

//at this point, if we did fork a process (in the child), then ret is 0 in the child 
//then we fork again 
if(ret == 0) { 
    fork(); 
} 

Итак, у нас есть наш первый процесс, процесс 1, выполняющий этот код. Предположим, что все вилки успешны (но вы должны проверить это ... Я думаю, что это должно быть обработано в рамках существующего кода, но это не так очевидно).

  • Процесс 1 вилки, создающий дочерний процесс с идентификатором процесса 2.
  • ret теперь 2 в процессе 1 и 0 в процессе 2.
  • В процессе 1, поскольку ret не равен нулю, вилка не происходит.
  • В процессе 2 ret равен 0, поэтому мы перерабатываем дочерний процесс с идентификатором процесса 3.
  • Теперь ret в процессе 1, 2 и 3 являются 2, 3 и 0 соответственно.
  • Теперь процесс 3 разветвит нового ребенка.
1

Я думаю, что неправильно, это непонимание того, как работает fork(). Если вы на UNIX, что вам нужно сделать «человек вилы», потому что в соответствии с той, которую я прочитал:

ОПИСАНИЕ вилка() создает новый процесс, дублируя вызывающий процесс. новый процесс, называемый как ребенок, является точной копией процесса вызывающего , упоминается как родитель,

и ..

ВОЗВРАТ СТОИМОСТИ В случае успеха, PID дочернего процесса возвращается родительскому, а 0 возвращается в дочерний процесс. При сбое в родительском элементе возвращается -1, не создается дочерний процесс, а errno устанавливается соответствующим образом.

Я подозреваю, что может произойти, вы можете видеть вывод из нескольких разветвленных процессов, которые только сбивают вас с толку. Каков точный полный выход вашей программы?

Это вряд ли будет проблемой с коротким замыканием, потому что, даже если второй не удастся, по крайней мере первая вилка должна была преуспеть, и, таким образом, вы должны получить pid, по крайней мере, из одного из первых printfs, если эта вилка преуспела.

0

Первая вилка, RET - вилка(), приведет к 3 возможных результатов:

  • > 0 => родитель получает идентификатор процесса ребенка
  • = 0 => Дочерний процесс
  • < 0 => ошибка с вилкой

второй вилкой, RET = & & отставка вилки(), будет выполнять вилку() только тогда, когда RET не равно нуль. Это может произойти в родительском хорошем случае и в случае родительской ошибки. В результате этого заявления могут быть:

  • == 0 => ret был отличным от нуля, а fork() равен нулю.
  • ! = 0 => ret был отличным от нуля, а fork() был отличным от нуля.

Третья вилка, если (ret == 0) {fork()}, будет выполняться только в том случае, если ret равен нулю.

Так что же все это значит? Вторая вилка кажется подозрительной в том, что возвращаемое значение от первой вилки может быть отличным от нуля в случае успеха или неудачи родителя! Я не знаю, был ли это намеченный результат, но он кажется сомнительным.

Третья вилка произошла, если первый ответ вилки был у ребенка, вторая вилка не выполнена, но третья делает. Третья вилка также может быть выполнена, если первая вилка находится в родительском контексте, а вторая вилка находится в дочернем контексте.

Кто-то проверяет эту логику, поскольку я никогда не мог написать такой код.

0

В языке Си, акт оператора && так:

  1. Вычислить первый аргумент. если он равен нулю, не вычисляйте второй аргумент, а результат равен 0.
  2. Если первый аргумент не равен нулю, вычислите второй аргумент. если это ноль, то ответ 0. еще, это 1.

Функция fork возврата 0 дочернего процесс, а другой номер (ИДП ребенка) к отцу, так что после первого fork, в отцовский процесс ret>0, а в детском процессе - ret==0. Если вы раскомментируете первый printf, вы получите 0 и другое число.

Затем вы запускаете свою линию. У ребенка ret равно 0, поэтому вычисление && останавливается перед вилкой, а ret остается 0. У отца ret>0, поэтому он запускается fork() и создает другого ребенка. В процессе отца, fork вернуть положительное число, так ret будет 1, а во втором ребенке, fork возвращение 0, так ret будет 0. Таким образом, если вы раскомментировать только второй printf, вы получите 0, 0, 1 (Возможно, в другом порядке).

Затем вы делаете if (ret==0) fork();, поэтому оба ребенка (чье ret равно 0) создают новый процесс каждый. Теперь у вас всего 5 процессов, поэтому строка Hello world\n будет напечатана 5 раз.

(Это очень опасно использовать функции вывода, а баловаться с fork - у вас есть 5 процессов, которые записывают в тот же файл без блокировки, так что вы можете получить результаты, как HHHHHeeeeellllllllllooooo wwwwwooooorrrrrlllllddddd\n\n\n\n\n)

+0

Да. Это то, что я понимаю после этого. Спасибо за объяснения. И не волнуйся об этом. Цель этой программы - узнать, сколько процессов имеет эта программа. И я думал, что у него должно быть больше пяти, и когда я попытался отладить его, я понял, что то, о чем я думал в этой строке, было неправильным.Вот почему я спрашивал, что происходит в этой конкретной линии. Еще раз спасибо за объяснение. – Oscarin

Смежные вопросы